From 1e3f4af4e3400c697b206475b10d2b17c3b57efb Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Fri, 25 Oct 2024 16:10:32 +0000 Subject: [PATCH 001/140] FEAT: enable backend switching for base gravitational-wave transient likelihood --- .github/workflows/basic-install.yml | 5 +- bilby/core/utils/series.py | 13 +- bilby/gw/detector/geometry.py | 20 +- bilby/gw/detector/interferometer.py | 29 ++- bilby/gw/detector/networks.py | 4 + bilby/gw/jaxstuff.py | 113 ++++++++++++ bilby/gw/likelihood/base.py | 12 +- bilby/gw/utils.py | 25 +-- bilby/gw/waveform_generator.py | 8 +- .../injection_examples/jax_fast_tutorial.py | 173 ++++++++++++++++++ 10 files changed, 363 insertions(+), 39 deletions(-) create mode 100644 bilby/gw/jaxstuff.py create mode 100644 examples/gw_examples/injection_examples/jax_fast_tutorial.py diff --git a/.github/workflows/basic-install.yml b/.github/workflows/basic-install.yml index 7d8d7a3fe..9652b0758 100644 --- a/.github/workflows/basic-install.yml +++ b/.github/workflows/basic-install.yml @@ -20,8 +20,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - # disable windows build test as bilby_cython is currently broken there - os: [ubuntu-latest, macos-latest] + os: [ubuntu-latest, macos-latest, windows-latest] python-version: ["3.10", "3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v4 @@ -39,6 +38,8 @@ jobs: - name: Test imports run: bash test/ci_test_imports.sh - name: Test entry points + run: | + - if: ${{ matrix.os != "windows-latest" }} run: | for script in $(pip show -f bilby | grep "bin\/" | xargs -I {} basename {}); do ${script} --help; diff --git a/bilby/core/utils/series.py b/bilby/core/utils/series.py index 63daebd6e..c3d71e3e2 100644 --- a/bilby/core/utils/series.py +++ b/bilby/core/utils/series.py @@ -1,4 +1,5 @@ import numpy as np +from bilback.utils import array_module _TOL = 14 @@ -97,9 +98,10 @@ def create_time_series(sampling_frequency, duration, starting_time=0.): float: An equidistant time series given the parameters """ + xp = array_module(sampling_frequency) _check_legal_sampling_frequency_and_duration(sampling_frequency, duration) number_of_samples = int(duration * sampling_frequency) - return np.linspace(start=starting_time, + return xp.linspace(start=starting_time, stop=duration + starting_time - 1 / sampling_frequency, num=number_of_samples) @@ -117,11 +119,12 @@ def create_frequency_series(sampling_frequency, duration): array_like: frequency series """ + xp = array_module(sampling_frequency) _check_legal_sampling_frequency_and_duration(sampling_frequency, duration) - number_of_samples = int(np.round(duration * sampling_frequency)) - number_of_frequencies = int(np.round(number_of_samples / 2) + 1) + number_of_samples = int(xp.round(duration * sampling_frequency)) + number_of_frequencies = int(xp.round(number_of_samples / 2) + 1) - return np.linspace(start=0, + return xp.linspace(start=0, stop=sampling_frequency / 2, num=number_of_frequencies) @@ -139,7 +142,7 @@ def _check_legal_sampling_frequency_and_duration(sampling_frequency, duration): """ num = sampling_frequency * duration - if np.abs(num - np.round(num)) > 10**(-_TOL): + if abs(num % 1) > 10**(-_TOL): raise IllegalDurationAndSamplingFrequencyException( '\nYour sampling frequency and duration must multiply to a number' 'up to (tol = {}) decimals close to an integer number. ' diff --git a/bilby/gw/detector/geometry.py b/bilby/gw/detector/geometry.py index d7e1433de..ea2a509ab 100644 --- a/bilby/gw/detector/geometry.py +++ b/bilby/gw/detector/geometry.py @@ -1,5 +1,5 @@ import numpy as np -from bilby_cython.geometry import calculate_arm, detector_tensor +from bilback.geometry import calculate_arm, detector_tensor from .. import utils as gwutils @@ -264,7 +264,7 @@ def detector_tensor(self): if not self._x_updated or not self._y_updated: _, _ = self.x, self.y # noqa if not self._detector_tensor_updated: - self._detector_tensor = detector_tensor(x=self.x, y=self.y) + self._detector_tensor = detector_tensor(self.x, self.y) self._detector_tensor_updated = True return self._detector_tensor @@ -290,17 +290,17 @@ def unit_vector_along_arm(self, arm): """ if arm == 'x': return calculate_arm( - arm_tilt=self._xarm_tilt, - arm_azimuth=self._xarm_azimuth, - longitude=self._longitude, - latitude=self._latitude + self._xarm_tilt, + self._xarm_azimuth, + self._longitude, + self._latitude ) elif arm == 'y': return calculate_arm( - arm_tilt=self._yarm_tilt, - arm_azimuth=self._yarm_azimuth, - longitude=self._longitude, - latitude=self._latitude + self._yarm_tilt, + self._yarm_azimuth, + self._longitude, + self._latitude ) else: raise ValueError("Arm must either be 'x' or 'y'.") diff --git a/bilby/gw/detector/interferometer.py b/bilby/gw/detector/interferometer.py index 9e9c23bdf..aef6c6247 100644 --- a/bilby/gw/detector/interferometer.py +++ b/bilby/gw/detector/interferometer.py @@ -1,7 +1,7 @@ import os import numpy as np -from bilby_cython.geometry import ( +from bilback.geometry import ( get_polarization_tensor, three_by_three_matrix_contraction, time_delay_from_geocenter, @@ -313,7 +313,8 @@ def get_detector_response(self, waveform_polarizations, parameters, frequencies= otherwise the provided :code:`Parameters["geocent_time"]` is used. """ if frequencies is None: - frequencies = self.frequency_array[self.frequency_mask] + # frequencies = self.frequency_array[self.frequency_mask] + frequencies = self.frequency_array mask = self.frequency_mask else: mask = np.ones(len(frequencies), dtype=bool) @@ -331,8 +332,8 @@ def get_detector_response(self, waveform_polarizations, parameters, frequencies= antenna_time, parameters['psi'], mode) - signal[mode] = waveform_polarizations[mode] * det_response - signal_ifo = sum(signal.values()) * mask + signal[mode] = waveform_polarizations[mode] * mask * det_response + signal_ifo = sum(signal.values()) time_shift = self.time_delay_from_geocenter( parameters['ra'], parameters['dec'], parameters['geocent_time']) @@ -342,9 +343,12 @@ def get_detector_response(self, waveform_polarizations, parameters, frequencies= dt_geocent = parameters['geocent_time'] - self.strain_data.start_time dt = dt_geocent + time_shift - signal_ifo[mask] = signal_ifo[mask] * np.exp(-1j * 2 * np.pi * dt * frequencies) + from bilback.utils import array_module + xp = array_module(signal_ifo) - signal_ifo[mask] *= self.calibration_model.get_calibration_factor( + signal_ifo = signal_ifo * xp.exp(-1j * 2 * np.pi * dt * frequencies) + + signal_ifo *= self.calibration_model.get_calibration_factor( frequencies, prefix='recalib_{}_'.format(self.name), **parameters ) @@ -936,3 +940,16 @@ def from_pickle(cls, filename=None): if res.__class__ != cls: raise TypeError('The loaded object is not an Interferometer') return res + + def set_array_backend(self, xp): + for attr in [ + "length", + "latitude", + "longitude", + "elevation", + "xarm_azimuth", + "yarm_azimuth", + "xarm_tilt", + "yarm_tilt", + ]: + setattr(self, attr, xp.array(getattr(self, attr))) diff --git a/bilby/gw/detector/networks.py b/bilby/gw/detector/networks.py index 25b3e7e71..6ed70fd44 100644 --- a/bilby/gw/detector/networks.py +++ b/bilby/gw/detector/networks.py @@ -341,6 +341,10 @@ def from_pickle(cls, filename=None): ) from_pickle.__doc__ = _load_docstring.format(format="pickle") + def set_array_backend(self, xp): + for ifo in self: + ifo.set_array_backend(xp) + class TriangularInterferometer(InterferometerList): def __init__( diff --git a/bilby/gw/jaxstuff.py b/bilby/gw/jaxstuff.py new file mode 100644 index 000000000..f02df34a3 --- /dev/null +++ b/bilby/gw/jaxstuff.py @@ -0,0 +1,113 @@ +""" +Generic dumping ground for jax-specific functions that we need. +This should find a home somewhere down the line, but gives an +idea of how much pain is being added. +""" + +from functools import partial + +import numpy as np +from bilby.core.likelihood import Likelihood + +import jax +import jax.numpy as jnp +from plum import dispatch +from jax.scipy.special import i0e +from ripple.waveforms import IMRPhenomPv2 + +def bilby_to_ripple_spins( + theta_jn, + phi_jl, + tilt_1, + tilt_2, + phi_12, + a_1, + a_2, +): + iota = theta_jn + spin_1x = a_1 * jnp.sin(tilt_1) * jnp.cos(phi_jl) + spin_1y = a_1 * jnp.sin(tilt_1) * jnp.sin(phi_jl) + spin_1z = a_1 * jnp.cos(tilt_1) + spin_2x = a_2 * jnp.sin(tilt_2) * jnp.cos(phi_jl + phi_12) + spin_2y = a_2 * jnp.sin(tilt_2) * jnp.sin(phi_jl + phi_12) + spin_2z = a_2 * jnp.cos(tilt_2) + return iota, spin_1x, spin_1y, spin_1z, spin_2x, spin_2y, spin_2z + + +def ripple_bbh(frequency, mass_1, mass_2, luminosity_distance, theta_jn, phase, + a_1, a_2, tilt_1, tilt_2, phi_12, phi_jl, **kwargs): + iota, spin_1x, spin_1y, spin_1z, spin_2x, spin_2y, spin_2z = bilby_to_ripple_spins( + theta_jn, phi_jl, tilt_1, tilt_2, phi_12, a_1, a_2 + ) + theta = jnp.array([ + mass_1, mass_2, spin_1x, spin_1y, spin_1z, spin_2x, spin_2y, spin_2z, + luminosity_distance, 0.0, phase, iota + ]) + hp, hc = jax.jit(IMRPhenomPv2.gen_IMRPhenomPv2)(frequency, theta, jax.numpy.array(20.0)) + return dict(plus=hp, cross=hc) + + +def generic_bilby_likelihood_function(likelihood, parameters, use_ratio=True): + """ + A wrapper to allow a :code:`Bilby` likelihood to be used with :code:`jax`. + + Parameters + ========== + likelihood: bilby.core.likelihood.Likelihood + The likelihood to evaluate. + parameters: dict + The parameters to evaluate the likelihood at. + use_ratio: bool, optional + Whether to evaluate the likelihood ratio or the full likelihood. + Default is :code:`True`. + """ + likelihood.parameters.update(parameters) + if use_ratio: + return likelihood.log_likelihood_ratio() + else: + return likelihood.log_likelihood() + + +class JittedLikelihood(Likelihood): + """ + A wrapper to just-in-time compile a :code:`Bilby` likelihood for use with :code:`jax`. + + .. note:: + + This is currently hardcoded to return the log likelihood ratio, regardless of + the input. + + Parameters + ========== + likelihood: bilby.core.likelihood.Likelihood + The likelihood to wrap. + likelihood_func: callable, optional + The function to use to evaluate the likelihood. Default is + :code:`generic_bilby_likelihood_function`. This function should take the + likelihood and parameters as arguments along with additional keyword arguments. + kwargs: dict, optional + Additional keyword arguments to pass to the likelihood function. + """ + + def __init__( + self, likelihood, likelihood_func=generic_bilby_likelihood_function, kwargs=None + ): + if kwargs is None: + kwargs = dict() + self.kwargs = kwargs + self._likelihood = likelihood + self.likelihood_func = jax.jit(partial(likelihood_func, likelihood)) + super().__init__(dict()) + + def __getattr__(self, name): + return getattr(self._likelihood, name) + + def log_likelihood_ratio(self): + return float( + np.nan_to_num(self.likelihood_func(self.parameters, **self.kwargs)) + ) + + +@dispatch +def ln_i0(value: jax.Array): + return jnp.log(i0e(value)) + jnp.abs(value) \ No newline at end of file diff --git a/bilby/gw/likelihood/base.py b/bilby/gw/likelihood/base.py index 8dfbcdbf5..7781ca836 100644 --- a/bilby/gw/likelihood/base.py +++ b/bilby/gw/likelihood/base.py @@ -107,9 +107,13 @@ class GravitationalWaveTransient(Likelihood): @attr.s(slots=True, weakref_slot=False) class _CalculatedSNRs: - d_inner_h = attr.ib(default=0j, converter=complex) - optimal_snr_squared = attr.ib(default=0, converter=float) - complex_matched_filter_snr = attr.ib(default=0j, converter=complex) + # the complex converted breaks JAX compilation + # d_inner_h = attr.ib(default=0j, converter=complex) + # optimal_snr_squared = attr.ib(default=0, converter=float) + # complex_matched_filter_snr = attr.ib(default=0j, converter=complex) + d_inner_h = attr.ib(default=0j) + optimal_snr_squared = attr.ib(default=0) + complex_matched_filter_snr = attr.ib(default=0j) d_inner_h_array = attr.ib(default=None) optimal_snr_squared_array = attr.ib(default=None) @@ -433,7 +437,7 @@ def log_likelihood_ratio(self, parameters): if self.time_marginalization and self.jitter_time: parameters['geocent_time'] -= parameters['time_jitter'] - return float(log_l.real) + return log_l.real def compute_log_likelihood_from_snrs(self, total_snrs, parameters): diff --git a/bilby/gw/utils.py b/bilby/gw/utils.py index 420e1fc04..ad0f14e64 100644 --- a/bilby/gw/utils.py +++ b/bilby/gw/utils.py @@ -5,10 +5,12 @@ import numpy as np from scipy.interpolate import interp1d from scipy.special import i0e -from bilby_cython.geometry import ( +from bilback.geometry import ( zenith_azimuth_to_theta_phi as _zenith_azimuth_to_theta_phi, ) -from bilby_cython.time import greenwich_mean_sidereal_time +from bilback.time import greenwich_mean_sidereal_time +from bilback.utils import array_module +from plum import dispatch from ..core.utils import (logger, run_commandline, check_directory_exists_and_if_not_mkdir, @@ -76,14 +78,15 @@ def get_vertex_position_geocentric(latitude, longitude, elevation): array_like: A 3D representation of the geocentric vertex position """ + xp = array_module(latitude) semi_major_axis = 6378137 # for ellipsoid model of Earth, in m semi_minor_axis = 6356752.314 # in m - radius = semi_major_axis**2 * (semi_major_axis**2 * np.cos(latitude)**2 + - semi_minor_axis**2 * np.sin(latitude)**2)**(-0.5) - x_comp = (radius + elevation) * np.cos(latitude) * np.cos(longitude) - y_comp = (radius + elevation) * np.cos(latitude) * np.sin(longitude) - z_comp = ((semi_minor_axis / semi_major_axis)**2 * radius + elevation) * np.sin(latitude) - return np.array([x_comp, y_comp, z_comp]) + radius = semi_major_axis**2 * (semi_major_axis**2 * xp.cos(latitude)**2 + + semi_minor_axis**2 * xp.sin(latitude)**2)**(-0.5) + x_comp = (radius + elevation) * xp.cos(latitude) * xp.cos(longitude) + y_comp = (radius + elevation) * xp.cos(latitude) * xp.sin(longitude) + z_comp = ((semi_minor_axis / semi_major_axis)**2 * radius + elevation) * xp.sin(latitude) + return xp.array([x_comp, y_comp, z_comp]) def inner_product(aa, bb, frequency, PSD): @@ -132,9 +135,8 @@ def noise_weighted_inner_product(aa, bb, power_spectral_density, duration): ======= Noise-weighted inner product. """ - - integrand = np.conj(aa) * bb / power_spectral_density - return 4 / duration * np.sum(integrand) + integrand = aa.conjugate() * bb / power_spectral_density + return 4 / duration * integrand.sum() def matched_filter_snr(signal, frequency_domain_strain, power_spectral_density, duration): @@ -1023,6 +1025,7 @@ def plot_spline_pos(log_freqs, samples, nfreqs=100, level=0.9, color='k', label= plt.xlim(freq_points.min() - .5, freq_points.max() + 50) +@dispatch def ln_i0(value): """ A numerically stable method to evaluate ln(I_0) a modified Bessel function diff --git a/bilby/gw/waveform_generator.py b/bilby/gw/waveform_generator.py index b42f5d8c8..d66589da1 100644 --- a/bilby/gw/waveform_generator.py +++ b/bilby/gw/waveform_generator.py @@ -24,7 +24,8 @@ class WaveformGenerator(object): def __init__(self, duration=None, sampling_frequency=None, start_time=0, frequency_domain_source_model=None, time_domain_source_model=None, parameters=None, parameter_conversion=None, - waveform_arguments=None): + waveform_arguments=None, use_cache=True, + ): """ The base waveform generator class. @@ -58,6 +59,10 @@ def __init__(self, duration=None, sampling_frequency=None, start_time=0, frequen Note: the arguments of frequency_domain_source_model (except the first, which is the frequencies at which to compute the strain) will be added to the WaveformGenerator object and initialised to `None`. + use_cache: bool + Whether to attempt caching the waveform between subsequent calls. + This is :code:`True` by default but must be disabled for JIT compilation + with :code:`JAX`. """ self._times_and_frequencies = CoupledTimeAndFrequencySeries(duration=duration, @@ -79,6 +84,7 @@ def __init__(self, duration=None, sampling_frequency=None, start_time=0, frequen "Non null parameters passed to waveform generator. These will be ignored." ) self._cache = dict(parameters=None, waveform=None, model=None) + self.use_cache = use_cache logger.info(f"Waveform generator instantiated: {self}") def __repr__(self): diff --git a/examples/gw_examples/injection_examples/jax_fast_tutorial.py b/examples/gw_examples/injection_examples/jax_fast_tutorial.py new file mode 100644 index 000000000..b4acfd333 --- /dev/null +++ b/examples/gw_examples/injection_examples/jax_fast_tutorial.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python +""" +Tutorial to demonstrate running parameter estimation on a reduced parameter +space for an injected signal. + +This example estimates the masses using a uniform prior in both component masses +and distance using a uniform in comoving volume prior on luminosity distance +between luminosity distances of 100Mpc and 5Gpc, the cosmology is Planck15. + +We optionally use ripple waveforms and a JIT-compiled likelihood. +""" + +import bilby +import bilby.gw.jaxstuff +import jax +jax.config.update("jax_enable_x64", True) + +USE_JAX = True + +# Set the duration and sampling frequency of the data segment that we're +# going to inject the signal into +duration = 4.0 +sampling_frequency = 2048.0 +minimum_frequency = 20.0 +if USE_JAX: + duration = jax.numpy.array(duration) + sampling_frequency = jax.numpy.array(sampling_frequency) + minimum_frequency = jax.numpy.array(minimum_frequency) + +# Specify the output directory and the name of the simulation. +outdir = "outdir" +label = "fast_tutorial" +bilby.core.utils.setup_logger(outdir=outdir, label=label) + +# Set up a random seed for result reproducibility. This is optional! +bilby.core.utils.random.seed(88170235) + +# We are going to inject a binary black hole waveform. We first establish a +# dictionary of parameters that includes all of the different waveform +# parameters, including masses of the two black holes (mass_1, mass_2), +# spins of both black holes (a, tilt, phi), etc. +injection_parameters = dict( + mass_1=36.0, + mass_2=29.0, + a_1=0.4, + a_2=0.3, + tilt_1=0.5, + tilt_2=1.0, + phi_12=1.7, + phi_jl=0.3, + luminosity_distance=2000.0, + theta_jn=0.4, + psi=2.659, + phase=1.3, + geocent_time=1126259642.413, + ra=1.375, + dec=-1.2108, +) + +# Fixed arguments passed into the source model +waveform_arguments = dict( + waveform_approximant="IMRPhenomPv2", + reference_frequency=50.0, + minimum_frequency=minimum_frequency, +) + +if USE_JAX: + fdsm = bilby.gw.jaxstuff.ripple_bbh +else: + fdsm = bilby.gw.source.lal_binary_black_hole + +# Create the waveform_generator using a LAL BinaryBlackHole source function +waveform_generator = bilby.gw.WaveformGenerator( + duration=duration, + sampling_frequency=sampling_frequency, + frequency_domain_source_model=fdsm, + parameter_conversion=bilby.gw.conversion.convert_to_lal_binary_black_hole_parameters, + waveform_arguments=waveform_arguments, + use_cache=False, +) + +# Set up interferometers. In this case we'll use two interferometers +# (LIGO-Hanford (H1), LIGO-Livingston (L1). These default to their design +# sensitivity +ifos = bilby.gw.detector.InterferometerList(["H1", "L1"]) +ifos.set_strain_data_from_power_spectral_densities( + sampling_frequency=sampling_frequency, + duration=duration, + start_time=injection_parameters["geocent_time"] - 2, +) +ifos.inject_signal( + waveform_generator=waveform_generator, parameters=injection_parameters +) +if USE_JAX: + ifos.set_array_backend(jax.numpy) + +# Set up a PriorDict, which inherits from dict. +# By default we will sample all terms in the signal models. However, this will +# take a long time for the calculation, so for this example we will set almost +# all of the priors to be equall to their injected values. This implies the +# prior is a delta function at the true, injected value. In reality, the +# sampler implementation is smart enough to not sample any parameter that has +# a delta-function prior. +# The above list does *not* include mass_1, mass_2, theta_jn and luminosity +# distance, which means those are the parameters that will be included in the +# sampler. If we do nothing, then the default priors get used. +priors = bilby.gw.prior.BBHPriorDict() +for key in [ + "a_1", + "a_2", + "tilt_1", + "tilt_2", + "phi_12", + "phi_jl", + "psi", + "ra", + "dec", + "geocent_time", +]: + priors[key] = injection_parameters[key] + +# Perform a check that the prior does not extend to a parameter space longer than the data +if not USE_JAX: + priors.validate_prior(duration, minimum_frequency) + +# Initialise the likelihood by passing in the interferometer data (ifos) and +# the waveform generator +likelihood = bilby.gw.GravitationalWaveTransient( + interferometers=ifos, + waveform_generator=waveform_generator, + priors=priors, + phase_marginalization=True, +) + +if USE_JAX: + # burn a few likelihood calls to check that we don't get + # repeated compilation + likelihood.parameters.update(priors.sample()) + likelihood.log_likelihood_ratio() + likelihood.log_likelihood() + likelihood.noise_log_likelihood() + + with jax.log_compiles(): + jit_likelihood = bilby.gw.jaxstuff.JittedLikelihood(likelihood) + jit_likelihood.parameters.update(priors.sample()) + jit_likelihood.log_likelihood_ratio() + jit_likelihood.log_likelihood() + jit_likelihood.noise_log_likelihood() + jit_likelihood.parameters.update(priors.sample()) + jit_likelihood.log_likelihood_ratio() + jit_likelihood.log_likelihood() + jit_likelihood.noise_log_likelihood() + sample_likelihood = jit_likelihood +else: + sample_likelihood = likelihood + +# use the log_compiles context so we can make sure there aren't recompilations +# inside the sampling loop +with jax.log_compiles(): + result = bilby.run_sampler( + likelihood=sample_likelihood, + priors=priors, + sampler="dynesty", + npoints=100, + sample="acceptance-walk", + naccept=10, + injection_parameters=injection_parameters, + outdir=outdir, + label=label, + ) + +# Make a corner plot. +result.plot_corner() From cf5c611fa531ec1736e2ca94a27bf55a8173f533 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Fri, 25 Oct 2024 17:24:06 -0500 Subject: [PATCH 002/140] FEAT: support multiband and relative binning likelihoods --- bilby/gw/detector/interferometer.py | 5 +- bilby/gw/jaxstuff.py | 51 ++- bilby/gw/likelihood/multiband.py | 27 +- bilby/gw/likelihood/relative.py | 10 +- .../injection_examples/jax_fast_tutorial.py | 384 +++++++++++------- 5 files changed, 300 insertions(+), 177 deletions(-) diff --git a/bilby/gw/detector/interferometer.py b/bilby/gw/detector/interferometer.py index aef6c6247..38b00aaf4 100644 --- a/bilby/gw/detector/interferometer.py +++ b/bilby/gw/detector/interferometer.py @@ -6,6 +6,7 @@ three_by_three_matrix_contraction, time_delay_from_geocenter, ) +from bilback.utils import array_module from ...core import utils from ...core.utils import docstring, logger, PropertyAccessor, safe_file_dump @@ -317,7 +318,8 @@ def get_detector_response(self, waveform_polarizations, parameters, frequencies= frequencies = self.frequency_array mask = self.frequency_mask else: - mask = np.ones(len(frequencies), dtype=bool) + xp = array_module(frequencies) + mask = xp.ones(len(frequencies), dtype=bool) if self.reference_time is None: antenna_time = parameters["geocent_time"] @@ -343,7 +345,6 @@ def get_detector_response(self, waveform_polarizations, parameters, frequencies= dt_geocent = parameters['geocent_time'] - self.strain_data.start_time dt = dt_geocent + time_shift - from bilback.utils import array_module xp = array_module(signal_ifo) signal_ifo = signal_ifo * xp.exp(-1j * 2 * np.pi * dt * frequencies) diff --git a/bilby/gw/jaxstuff.py b/bilby/gw/jaxstuff.py index f02df34a3..464a7abb7 100644 --- a/bilby/gw/jaxstuff.py +++ b/bilby/gw/jaxstuff.py @@ -6,7 +6,6 @@ from functools import partial -import numpy as np from bilby.core.likelihood import Likelihood import jax @@ -15,6 +14,7 @@ from jax.scipy.special import i0e from ripple.waveforms import IMRPhenomPv2 + def bilby_to_ripple_spins( theta_jn, phi_jl, @@ -34,16 +34,41 @@ def bilby_to_ripple_spins( return iota, spin_1x, spin_1y, spin_1z, spin_2x, spin_2y, spin_2z -def ripple_bbh(frequency, mass_1, mass_2, luminosity_distance, theta_jn, phase, - a_1, a_2, tilt_1, tilt_2, phi_12, phi_jl, **kwargs): +wf_func = jax.jit(IMRPhenomPv2.gen_IMRPhenomPv2) + + +def ripple_bbh_relbin( + frequency, mass_1, mass_2, luminosity_distance, theta_jn, phase, + a_1, a_2, tilt_1, tilt_2, phi_12, phi_jl, fiducial, **kwargs, +): + if fiducial == 1: + kwargs["frequencies"] = frequency + else: + kwargs["frequencies"] = kwargs.pop("frequency_bin_edges") + return ripple_bbh( + frequency, mass_1, mass_2, luminosity_distance, theta_jn, phase, + a_1, a_2, tilt_1, tilt_2, phi_12, phi_jl, **kwargs + ) + + +def ripple_bbh( + frequency, mass_1, mass_2, luminosity_distance, theta_jn, phase, + a_1, a_2, tilt_1, tilt_2, phi_12, phi_jl, **kwargs, +): iota, spin_1x, spin_1y, spin_1z, spin_2x, spin_2y, spin_2z = bilby_to_ripple_spins( theta_jn, phi_jl, tilt_1, tilt_2, phi_12, a_1, a_2 ) + if "frequencies" in kwargs: + frequencies = kwargs["frequencies"] + elif "minimum_frequency" in kwargs: + frequencies = jnp.maximum(frequency, kwargs["minimum_frequency"]) + else: + frequencies = frequency theta = jnp.array([ mass_1, mass_2, spin_1x, spin_1y, spin_1z, spin_2x, spin_2y, spin_2z, - luminosity_distance, 0.0, phase, iota + luminosity_distance, jnp.array(0.0), phase, iota ]) - hp, hc = jax.jit(IMRPhenomPv2.gen_IMRPhenomPv2)(frequency, theta, jax.numpy.array(20.0)) + hp, hc = wf_func(frequencies, theta, jax.numpy.array(20.0)) return dict(plus=hp, cross=hc) @@ -90,24 +115,30 @@ class JittedLikelihood(Likelihood): """ def __init__( - self, likelihood, likelihood_func=generic_bilby_likelihood_function, kwargs=None + self, + likelihood, + likelihood_func=generic_bilby_likelihood_function, + kwargs=None, + cast_to_float=True, ): if kwargs is None: kwargs = dict() self.kwargs = kwargs self._likelihood = likelihood self.likelihood_func = jax.jit(partial(likelihood_func, likelihood)) + self.cast_to_float = cast_to_float super().__init__(dict()) def __getattr__(self, name): return getattr(self._likelihood, name) def log_likelihood_ratio(self): - return float( - np.nan_to_num(self.likelihood_func(self.parameters, **self.kwargs)) - ) + ln_l = jnp.nan_to_num(self.likelihood_func(self.parameters, **self.kwargs)) + if self.cast_to_float: + ln_l = float(ln_l) + return ln_l @dispatch def ln_i0(value: jax.Array): - return jnp.log(i0e(value)) + jnp.abs(value) \ No newline at end of file + return jnp.log(i0e(value)) + jnp.abs(value) diff --git a/bilby/gw/likelihood/multiband.py b/bilby/gw/likelihood/multiband.py index 7b746eefb..dd743835e 100644 --- a/bilby/gw/likelihood/multiband.py +++ b/bilby/gw/likelihood/multiband.py @@ -3,6 +3,7 @@ import numbers import numpy as np +from bilback.utils import array_module from .base import GravitationalWaveTransient from ...core.utils import ( @@ -746,6 +747,9 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array An object containing the SNR quantities. """ + if self.time_marginalization: + original_time = parameters["geocent_time"] + parameters["geocent_time"] = self._beam_pattern_reference_time modes = { mode: value[self.unique_to_original_frequencies] @@ -755,11 +759,16 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array modes, parameters, frequencies=self.banded_frequency_points ) - d_inner_h = np.conj(np.dot(strain, self.linear_coeffs[interferometer.name])) + if self.time_marginalization: + parameters["geocent_time"] = original_time + + d_inner_h = (strain @ self.linear_coeffs[interferometer.name]).conjugate() + + xp = array_module(strain) if self.linear_interpolation: - optimal_snr_squared = np.vdot( - np.real(strain * np.conjugate(strain)), + optimal_snr_squared = xp.vdot( + (strain * strain.conjugate()).real, self.quadratic_coeffs[interferometer.name] ) else: @@ -769,18 +778,18 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array start_idx, end_idx = self.start_end_idxs[b] Mb = self.Mbs[b] if b == 0: - optimal_snr_squared += (4. / self.interferometers.duration) * np.vdot( - np.real(strain[start_idx:end_idx + 1] * np.conjugate(strain[start_idx:end_idx + 1])), + optimal_snr_squared += (4. / self.interferometers.duration) * xp.vdot( + (strain[start_idx:end_idx + 1] * strain[start_idx:end_idx + 1].conjugate()).real, interferometer.frequency_mask[Ks:Ke + 1] * self.windows[start_idx:end_idx + 1] / interferometer.power_spectral_density_array[Ks:Ke + 1]) else: self.wths[interferometer.name][b][Ks:Ke + 1] = ( self.square_root_windows[start_idx:end_idx + 1] * strain[start_idx:end_idx + 1] ) - self.hbcs[interferometer.name][b][-Mb:] = np.fft.irfft(self.wths[interferometer.name][b]) - thbc = np.fft.rfft(self.hbcs[interferometer.name][b]) - optimal_snr_squared += (4. / self.Tbhats[b]) * np.vdot( - np.real(thbc * np.conjugate(thbc)), self.Ibcs[interferometer.name][b]) + self.hbcs[interferometer.name][b][-Mb:] = xp.fft.irfft(self.wths[interferometer.name][b]) + thbc = xp.fft.rfft(self.hbcs[interferometer.name][b]) + optimal_snr_squared += (4. / self.Tbhats[b]) * xp.vdot( + thbc * np.conjugate(thbc).real, self.Ibcs[interferometer.name][b]) complex_matched_filter_snr = d_inner_h / (optimal_snr_squared**0.5) diff --git a/bilby/gw/likelihood/relative.py b/bilby/gw/likelihood/relative.py index 1928c013d..fbf43e944 100644 --- a/bilby/gw/likelihood/relative.py +++ b/bilby/gw/likelihood/relative.py @@ -2,6 +2,7 @@ import numpy as np from scipy.optimize import differential_evolution +from bilback.utils import array_module from .base import GravitationalWaveTransient from ...core.utils import logger @@ -258,7 +259,7 @@ def set_fiducial_waveforms(self, parameters): for interferometer in self.interferometers: logger.debug(f"Maximum Frequency is {interferometer.maximum_frequency}") wf = interferometer.get_detector_response(self.fiducial_polarizations, parameters) - wf[interferometer.frequency_array > self.maximum_frequency] = 0 + wf *= interferometer.frequency_array <= self.maximum_frequency self.per_detector_fiducial_waveforms[interferometer.name] = wf def find_maximum_likelihood_parameters(self, parameter_bounds, @@ -401,18 +402,19 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array parameters=parameters, ) a0, a1, b0, b1 = self.summary_data[interferometer.name] - d_inner_h = np.sum(a0 * np.conjugate(r0) + a1 * np.conjugate(r1)) - h_inner_h = np.sum(b0 * np.abs(r0) ** 2 + 2 * b1 * np.real(r0 * np.conjugate(r1))) + d_inner_h = (a0 * r0.conjugate() + a1 * r1.conjugate()).sum() + h_inner_h = (b0 * abs(r0) ** 2 + 2 * b1 * (r0 * r1.conjugate()).real).sum() optimal_snr_squared = h_inner_h complex_matched_filter_snr = d_inner_h / (optimal_snr_squared ** 0.5) if return_array and self.time_marginalization: + xp = array_module(r0) full_waveform = self._compute_full_waveform( signal_polarizations=waveform_polarizations, interferometer=interferometer, parameters=parameters, ) - d_inner_h_array = 4 / self.waveform_generator.duration * np.fft.fft( + d_inner_h_array = 4 / self.waveform_generator.duration * xp.fft.fft( full_waveform[0:-1] * interferometer.frequency_domain_strain.conjugate()[0:-1] / interferometer.power_spectral_density_array[0:-1]) diff --git a/examples/gw_examples/injection_examples/jax_fast_tutorial.py b/examples/gw_examples/injection_examples/jax_fast_tutorial.py index b4acfd333..930cc6de5 100644 --- a/examples/gw_examples/injection_examples/jax_fast_tutorial.py +++ b/examples/gw_examples/injection_examples/jax_fast_tutorial.py @@ -9,165 +9,245 @@ We optionally use ripple waveforms and a JIT-compiled likelihood. """ +import os +from itertools import product + +# Set OMP_NUM_THREADS to stop lalsimulation taking over my computer +os.environ["OMP_NUM_THREADS"] = "1" import bilby import bilby.gw.jaxstuff import jax +from numpyro.infer import AIES, ESS # noqa + jax.config.update("jax_enable_x64", True) -USE_JAX = True - -# Set the duration and sampling frequency of the data segment that we're -# going to inject the signal into -duration = 4.0 -sampling_frequency = 2048.0 -minimum_frequency = 20.0 -if USE_JAX: - duration = jax.numpy.array(duration) - sampling_frequency = jax.numpy.array(sampling_frequency) - minimum_frequency = jax.numpy.array(minimum_frequency) - -# Specify the output directory and the name of the simulation. -outdir = "outdir" -label = "fast_tutorial" -bilby.core.utils.setup_logger(outdir=outdir, label=label) - -# Set up a random seed for result reproducibility. This is optional! -bilby.core.utils.random.seed(88170235) - -# We are going to inject a binary black hole waveform. We first establish a -# dictionary of parameters that includes all of the different waveform -# parameters, including masses of the two black holes (mass_1, mass_2), -# spins of both black holes (a, tilt, phi), etc. -injection_parameters = dict( - mass_1=36.0, - mass_2=29.0, - a_1=0.4, - a_2=0.3, - tilt_1=0.5, - tilt_2=1.0, - phi_12=1.7, - phi_jl=0.3, - luminosity_distance=2000.0, - theta_jn=0.4, - psi=2.659, - phase=1.3, - geocent_time=1126259642.413, - ra=1.375, - dec=-1.2108, -) - -# Fixed arguments passed into the source model -waveform_arguments = dict( - waveform_approximant="IMRPhenomPv2", - reference_frequency=50.0, - minimum_frequency=minimum_frequency, -) - -if USE_JAX: - fdsm = bilby.gw.jaxstuff.ripple_bbh -else: - fdsm = bilby.gw.source.lal_binary_black_hole - -# Create the waveform_generator using a LAL BinaryBlackHole source function -waveform_generator = bilby.gw.WaveformGenerator( - duration=duration, - sampling_frequency=sampling_frequency, - frequency_domain_source_model=fdsm, - parameter_conversion=bilby.gw.conversion.convert_to_lal_binary_black_hole_parameters, - waveform_arguments=waveform_arguments, - use_cache=False, -) - -# Set up interferometers. In this case we'll use two interferometers -# (LIGO-Hanford (H1), LIGO-Livingston (L1). These default to their design -# sensitivity -ifos = bilby.gw.detector.InterferometerList(["H1", "L1"]) -ifos.set_strain_data_from_power_spectral_densities( - sampling_frequency=sampling_frequency, - duration=duration, - start_time=injection_parameters["geocent_time"] - 2, -) -ifos.inject_signal( - waveform_generator=waveform_generator, parameters=injection_parameters -) -if USE_JAX: - ifos.set_array_backend(jax.numpy) - -# Set up a PriorDict, which inherits from dict. -# By default we will sample all terms in the signal models. However, this will -# take a long time for the calculation, so for this example we will set almost -# all of the priors to be equall to their injected values. This implies the -# prior is a delta function at the true, injected value. In reality, the -# sampler implementation is smart enough to not sample any parameter that has -# a delta-function prior. -# The above list does *not* include mass_1, mass_2, theta_jn and luminosity -# distance, which means those are the parameters that will be included in the -# sampler. If we do nothing, then the default priors get used. -priors = bilby.gw.prior.BBHPriorDict() -for key in [ - "a_1", - "a_2", - "tilt_1", - "tilt_2", - "phi_12", - "phi_jl", - "psi", - "ra", - "dec", - "geocent_time", -]: - priors[key] = injection_parameters[key] - -# Perform a check that the prior does not extend to a parameter space longer than the data -if not USE_JAX: - priors.validate_prior(duration, minimum_frequency) - -# Initialise the likelihood by passing in the interferometer data (ifos) and -# the waveform generator -likelihood = bilby.gw.GravitationalWaveTransient( - interferometers=ifos, - waveform_generator=waveform_generator, - priors=priors, - phase_marginalization=True, -) - -if USE_JAX: - # burn a few likelihood calls to check that we don't get - # repeated compilation - likelihood.parameters.update(priors.sample()) - likelihood.log_likelihood_ratio() - likelihood.log_likelihood() - likelihood.noise_log_likelihood() +bilby.core.utils.setup_logger(log_level="WARNING") + + +def main(use_jax, model): + # Set the duration and sampling frequency of the data segment that we're + # going to inject the signal into + duration = 4.0 + sampling_frequency = 2048.0 + minimum_frequency = 20.0 + if use_jax: + duration = jax.numpy.array(duration) + sampling_frequency = jax.numpy.array(sampling_frequency) + minimum_frequency = jax.numpy.array(minimum_frequency) + + # Specify the output directory and the name of the simulation. + outdir = "outdir" + label = f"{model}_{'jax' if use_jax else 'numpy'}" + + # Set up a random seed for result reproducibility. This is optional! + bilby.core.utils.random.seed(88170235) + + # We are going to inject a binary black hole waveform. We first establish a + # dictionary of parameters that includes all of the different waveform + # parameters, including masses of the two black holes (mass_1, mass_2), + # spins of both black holes (a, tilt, phi), etc. + injection_parameters = dict( + mass_1=36.0, + mass_2=29.0, + a_1=0.4, + a_2=0.3, + tilt_1=0.5, + tilt_2=1.0, + phi_12=1.7, + phi_jl=0.3, + luminosity_distance=2000.0, + theta_jn=0.4, + psi=2.659, + phase=1.3, + geocent_time=1126259642.413, + ra=1.375, + dec=-1.2108, + ) + if model == "relbin": + injection_parameters["fiducial"] = 1 + + # Fixed arguments passed into the source model + waveform_arguments = dict( + waveform_approximant="IMRPhenomPv2", + reference_frequency=50.0, + minimum_frequency=minimum_frequency, + ) - with jax.log_compiles(): - jit_likelihood = bilby.gw.jaxstuff.JittedLikelihood(likelihood) - jit_likelihood.parameters.update(priors.sample()) - jit_likelihood.log_likelihood_ratio() - jit_likelihood.log_likelihood() - jit_likelihood.noise_log_likelihood() - jit_likelihood.parameters.update(priors.sample()) - jit_likelihood.log_likelihood_ratio() - jit_likelihood.log_likelihood() - jit_likelihood.noise_log_likelihood() - sample_likelihood = jit_likelihood -else: - sample_likelihood = likelihood - -# use the log_compiles context so we can make sure there aren't recompilations -# inside the sampling loop -with jax.log_compiles(): - result = bilby.run_sampler( - likelihood=sample_likelihood, + if use_jax: + match model: + case "relbin": + fdsm = bilby.gw.jaxstuff.ripple_bbh_relbin + case _: + fdsm = bilby.gw.jaxstuff.ripple_bbh + else: + match model: + case "relbin": + fdsm = bilby.gw.source.lal_binary_black_hole_relative_binning + case _: + fdsm = bilby.gw.source.lal_binary_black_hole + + # Create the waveform_generator using a LAL BinaryBlackHole source function + waveform_generator = bilby.gw.WaveformGenerator( + duration=duration, + sampling_frequency=sampling_frequency, + frequency_domain_source_model=fdsm, + parameter_conversion=bilby.gw.conversion.convert_to_lal_binary_black_hole_parameters, + waveform_arguments=waveform_arguments, + use_cache=not use_jax, + ) + + # Set up interferometers. In this case we'll use two interferometers + # (LIGO-Hanford (H1), LIGO-Livingston (L1). These default to their design + # sensitivity + ifos = bilby.gw.detector.InterferometerList(["H1", "L1"]) + ifos.set_strain_data_from_power_spectral_densities( + sampling_frequency=sampling_frequency, + duration=duration, + start_time=injection_parameters["geocent_time"] - 2, + ) + ifos.inject_signal( + waveform_generator=waveform_generator, parameters=injection_parameters + ) + if use_jax: + ifos.set_array_backend(jax.numpy) + + if model == "mb": + if use_jax: + pass + else: + waveform_generator.frequency_domain_source_model = ( + bilby.gw.source.binary_black_hole_frequency_sequence + ) + del waveform_generator.waveform_arguments["minimum_frequency"] + + # Set up a PriorDict, which inherits from dict. + # By default we will sample all terms in the signal models. However, this will + # take a long time for the calculation, so for this example we will set almost + # all of the priors to be equall to their injected values. This implies the + # prior is a delta function at the true, injected value. In reality, the + # sampler implementation is smart enough to not sample any parameter that has + # a delta-function prior. + # The above list does *not* include mass_1, mass_2, theta_jn and luminosity + # distance, which means those are the parameters that will be included in the + # sampler. If we do nothing, then the default priors get used. + priors = bilby.gw.prior.BBHPriorDict() + for key in [ + # "a_1", + # "a_2", + # "tilt_1", + # "tilt_2", + # "phi_12", + # "phi_jl", + # "psi", + # "ra", + # "dec", + # "geocent_time", + ]: + priors[key] = injection_parameters[key] + del priors["mass_1"], priors["mass_2"] + priors["L1_time"] = bilby.core.prior.Uniform(1126259642.313, 1126259642.513) + + # Perform a check that the prior does not extend to a parameter space longer than the data + if not use_jax: + priors.validate_prior(duration, minimum_frequency) + + # Initialise the likelihood by passing in the interferometer data (ifos) and + # the waveform generator + match model: + case "relbin": + likelihood_class = ( + bilby.gw.likelihood.RelativeBinningGravitationalWaveTransient + ) + case "mb": + likelihood_class = bilby.gw.likelihood.MBGravitationalWaveTransient + case _: + likelihood_class = bilby.gw.likelihood.GravitationalWaveTransient + likelihood = likelihood_class( + interferometers=ifos, + waveform_generator=waveform_generator, priors=priors, - sampler="dynesty", - npoints=100, - sample="acceptance-walk", - naccept=10, - injection_parameters=injection_parameters, - outdir=outdir, - label=label, + phase_marginalization=True, + reference_frame=ifos, + time_reference="L1", ) -# Make a corner plot. -result.plot_corner() + if use_jax: + + def sample(): + parameters = priors.sample() + parameters = {key: jax.numpy.array(val) for key, val in parameters.items()} + return parameters + + # burn a few likelihood calls to check that we don't get + # repeated compilation + likelihood.parameters.update(sample()) + likelihood.log_likelihood_ratio() + likelihood.log_likelihood() + likelihood.noise_log_likelihood() + + with jax.log_compiles(): + jit_likelihood = bilby.gw.jaxstuff.JittedLikelihood( + likelihood, + cast_to_float=False, + jit=True, + ) + jit_likelihood.parameters.update(sample()) + jit_likelihood.log_likelihood_ratio() + jit_likelihood.log_likelihood() + jit_likelihood.noise_log_likelihood() + jit_likelihood.parameters.update(sample()) + jit_likelihood.log_likelihood_ratio() + jit_likelihood.log_likelihood() + jit_likelihood.noise_log_likelihood() + sample_likelihood = jit_likelihood + else: + sample_likelihood = likelihood + + def likelihood_func(parameters): + return sample_likelihood.likelihood_func(parameters, **sample_likelihood.kwargs) + + # import IPython; IPython.embed() + # raise SystemExit() + + # use the log_compiles context so we can make sure there aren't recompilations + # inside the sampling loop + with jax.log_compiles(): + result = bilby.run_sampler( + likelihood=sample_likelihood, + priors=priors, + # sampler="dynesty", + sampler="numpyro", + sampler_name="ESS", + num_warmup=100, + num_samples=100, + num_chains=40, + thinning=2, + # moves={AIES.DEMove(): 0.25, AIES.DEMove(g0=1): 0.5, AIES.StretchMove(): 0.25}, + moves={ + ESS.DifferentialMove(): 0.25, + ESS.KDEMove(): 0.25, + ESS.GaussianMove(): 0.5, + }, + chain_method="vectorized", + npoints=100, + sample="acceptance-walk", + naccept=10, + injection_parameters=injection_parameters, + outdir=outdir, + label=label, + ) + print(result) + print(f"Sampling time: {result.sampling_time:.1f}s\n") + + # Make a corner plot. + result.plot_corner() + raise SystemExit() + return result.sampling_time + + +if __name__ == "__main__": + times = dict() + for arg in product([True, False], ["relbin", "mb", "regular"][-1:]): + times[arg] = main(*arg) + print(times) From 2bfc833e4a2a9ad9f32ebf9acb72c7495ddc09be Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Fri, 25 Oct 2024 19:22:34 -0500 Subject: [PATCH 003/140] FEAT: make more conversions backend agnostic --- bilby/gw/conversion.py | 89 ++++++++++++++++++----------------- bilby/gw/detector/networks.py | 24 ++++++++++ bilby/gw/jaxstuff.py | 1 + bilby/gw/likelihood/base.py | 2 + bilby/gw/utils.py | 28 ++--------- 5 files changed, 77 insertions(+), 67 deletions(-) diff --git a/bilby/gw/conversion.py b/bilby/gw/conversion.py index 9bd9cab06..b1a1e3fde 100644 --- a/bilby/gw/conversion.py +++ b/bilby/gw/conversion.py @@ -9,6 +9,7 @@ import pickle import numpy as np +from bilback.utils import array_module from pandas import DataFrame, Series from scipy.stats import norm @@ -204,9 +205,9 @@ def convert_to_lal_binary_black_hole_parameters(parameters): added_keys: list keys which are added to parameters during function call """ - converted_parameters = parameters.copy() original_keys = list(converted_parameters.keys()) + xp = array_module(parameters[original_keys[0]]) if 'luminosity_distance' not in original_keys: if 'redshift' in converted_parameters.keys(): converted_parameters['luminosity_distance'] = \ @@ -244,7 +245,7 @@ def convert_to_lal_binary_black_hole_parameters(parameters): converted_parameters['a_{}'.format(idx)] = abs( converted_parameters[key]) converted_parameters['cos_tilt_{}'.format(idx)] = \ - np.sign(converted_parameters[key]) + xp.sign(converted_parameters[key]) else: with np.errstate(invalid="raise"): try: @@ -267,13 +268,13 @@ def convert_to_lal_binary_black_hole_parameters(parameters): cos_angle = str('cos_' + angle) if cos_angle in converted_parameters.keys(): with np.errstate(invalid="ignore"): - converted_parameters[angle] = np.arccos(converted_parameters[cos_angle]) + converted_parameters[angle] = xp.arccos(converted_parameters[cos_angle]) if "delta_phase" in original_keys: with np.errstate(invalid="ignore"): - converted_parameters["phase"] = np.mod( + converted_parameters["phase"] = xp.mod( converted_parameters["delta_phase"] - - np.sign(np.cos(converted_parameters["theta_jn"])) + - xp.sign(xp.cos(converted_parameters["theta_jn"])) * converted_parameters["psi"], 2 * np.pi) added_keys = [key for key in converted_parameters.keys() @@ -378,19 +379,19 @@ def convert_to_lal_binary_neutron_star_parameters(parameters): g3pca = converted_parameters['eos_spectral_pca_gamma_3'] m1s = converted_parameters['mass_1_source'] m2s = converted_parameters['mass_2_source'] - all_lambda_1 = np.empty(0) - all_lambda_2 = np.empty(0) - all_eos_check = np.empty(0, dtype=bool) + all_lambda_1 = list() + all_lambda_2 = list() + all_eos_check = list() for (g_0pca, g_1pca, g_2pca, g_3pca, m1_s, m2_s) in zip(g0pca, g1pca, g2pca, g3pca, m1s, m2s): g_0, g_1, g_2, g_3 = spectral_pca_to_spectral(g_0pca, g_1pca, g_2pca, g_3pca) lambda_1, lambda_2, eos_check = \ spectral_params_to_lambda_1_lambda_2(g_0, g_1, g_2, g_3, m1_s, m2_s) - all_lambda_1 = np.append(all_lambda_1, lambda_1) - all_lambda_2 = np.append(all_lambda_2, lambda_2) - all_eos_check = np.append(all_eos_check, eos_check) - converted_parameters['lambda_1'] = all_lambda_1 - converted_parameters['lambda_2'] = all_lambda_2 - converted_parameters['eos_check'] = all_eos_check + all_lambda_1.append(lambda_1) + all_lambda_2.append(lambda_2) + all_eos_check.append(eos_check) + converted_parameters['lambda_1'] = np.array(all_lambda_1) + converted_parameters['lambda_2'] = np.array(all_lambda_2) + converted_parameters['eos_check'] = np.array(all_eos_check) for key in float_eos_params.keys(): converted_parameters[key] = float_eos_params[key] elif 'eos_polytrope_gamma_0' and 'eos_polytrope_log10_pressure_1' in converted_parameters.keys(): @@ -630,8 +631,9 @@ def spectral_pca_to_spectral(gamma_pca_0, gamma_pca_1, gamma_pca_2, gamma_pca_3) array of gamma_0, gamma_1, gamma_2, gamma_3 in model space ''' - sampled_pca_gammas = np.array([gamma_pca_0, gamma_pca_1, gamma_pca_2, gamma_pca_3]) - transformation_matrix = np.array( + xp = array_module(gamma_pca_0) + sampled_pca_gammas = xp.array([gamma_pca_0, gamma_pca_1, gamma_pca_2, gamma_pca_3]) + transformation_matrix = xp.array( [ [0.43801, -0.76705, 0.45143, 0.12646], [-0.53573, 0.17169, 0.67968, 0.47070], @@ -640,10 +642,10 @@ def spectral_pca_to_spectral(gamma_pca_0, gamma_pca_1, gamma_pca_2, gamma_pca_3) ] ) - model_space_mean = np.array([0.89421, 0.33878, -0.07894, 0.00393]) - model_space_standard_deviation = np.array([0.35700, 0.25769, 0.05452, 0.00312]) + model_space_mean = xp.array([0.89421, 0.33878, -0.07894, 0.00393]) + model_space_standard_deviation = xp.array([0.35700, 0.25769, 0.05452, 0.00312]) converted_gamma_parameters = \ - model_space_mean + model_space_standard_deviation * np.dot(transformation_matrix, sampled_pca_gammas) + model_space_mean + model_space_standard_deviation * xp.dot(transformation_matrix, sampled_pca_gammas) return converted_gamma_parameters @@ -958,9 +960,9 @@ def chirp_mass_and_primary_mass_to_mass_ratio(chirp_mass, mass_1): Mass ratio (mass_2/mass_1) of the binary """ a = (chirp_mass / mass_1) ** 5 - t0 = np.cbrt(9 * a + np.sqrt(3) * np.sqrt(27 * a ** 2 - 4 * a ** 3)) - t1 = np.cbrt(2) * 3 ** (2 / 3) - t2 = np.cbrt(2 / 3) * a + t0 = (9 * a + 3**0.5 * (27 * a ** 2 - 4 * a ** 3)**0.5)**(1 / 3) + t1 = (2)**(1 / 3) * 3 ** (2 / 3) + t2 = (2 / 3)**(1 / 3) * a return t2 / t0 + t0 / t1 @@ -1043,8 +1045,8 @@ def component_masses_to_symmetric_mass_ratio(mass_1, mass_2): symmetric_mass_ratio: float Symmetric mass ratio of the binary """ - - return np.minimum((mass_1 * mass_2) / (mass_1 + mass_2) ** 2, 1 / 4) + xp = array_module(mass_1) + return xp.minimum((mass_1 * mass_2) / (mass_1 + mass_2) ** 2, 1 / 4) def component_masses_to_mass_ratio(mass_1, mass_2): @@ -1403,17 +1405,17 @@ def binary_love_fit_lambda_symmetric_mass_ratio_to_lambda_antisymmetric(lambda_s lambda_antisymmetric: float Antisymmetric tidal parameter. """ - lambda_symmetric_m1o5 = np.power(lambda_symmetric, -1. / 5.) + lambda_symmetric_m1o5 = lambda_symmetric ** (-1 / 5) lambda_symmetric_m2o5 = lambda_symmetric_m1o5 * lambda_symmetric_m1o5 lambda_symmetric_m3o5 = lambda_symmetric_m2o5 * lambda_symmetric_m1o5 q = mass_ratio - q2 = np.square(mass_ratio) + q2 = mass_ratio ** 2 # Eqn.2 from CHZ, incorporating the dependence on mass ratio n_polytropic = 0.743 # average polytropic index for the EoSs included in the fit - q_for_Fnofq = np.power(q, 10. / (3. - n_polytropic)) + q_for_Fnofq = q ** (10. / (3. - n_polytropic)) Fnofq = (1. - q_for_Fnofq) / (1. + q_for_Fnofq) # b_ij and c_ij coefficients are given in Table I of CHZ @@ -1483,10 +1485,10 @@ def binary_love_lambda_symmetric_to_lambda_1_lambda_2_manual_marginalisation(bin lambda_antisymmetric_fitOnly = binary_love_fit_lambda_symmetric_mass_ratio_to_lambda_antisymmetric(lambda_symmetric, mass_ratio) - lambda_symmetric_sqrt = np.sqrt(lambda_symmetric) + lambda_symmetric_sqrt = lambda_symmetric ** 0.5 q = mass_ratio - q2 = np.square(mass_ratio) + q2 = mass_ratio ** 2 # mu_i and sigma_i coefficients are given in Table II of CHZ @@ -1546,9 +1548,10 @@ def binary_love_lambda_symmetric_to_lambda_1_lambda_2_manual_marginalisation(bin # Eqn 5 from CHZ, averaging the corrections from the # standard deviations of the residual fits - lambda_antisymmetric_stdCorr = \ - np.sqrt(np.square(lambda_antisymmetric_lambda_symmetric_stdCorr) + - np.square(lambda_antisymmetric_mass_ratio_stdCorr)) + lambda_antisymmetric_stdCorr = ( + lambda_antisymmetric_lambda_symmetric_stdCorr ** 2 + + lambda_antisymmetric_mass_ratio_stdCorr ** 2 + ) ** 0.5 # Draw a correction on the fit from a # Gaussian distribution with width lambda_antisymmetric_stdCorr @@ -2066,28 +2069,29 @@ def generate_spin_parameters(sample): output_sample = sample.copy() output_sample = generate_component_spins(output_sample) + xp = array_module(sample["spin_1z"]) output_sample['chi_eff'] = (output_sample['spin_1z'] + output_sample['spin_2z'] * output_sample['mass_ratio']) /\ (1 + output_sample['mass_ratio']) - output_sample['chi_1_in_plane'] = np.sqrt( + output_sample['chi_1_in_plane'] = ( output_sample['spin_1x'] ** 2 + output_sample['spin_1y'] ** 2 - ) - output_sample['chi_2_in_plane'] = np.sqrt( + ) ** 0.5 + output_sample['chi_2_in_plane'] = ( output_sample['spin_2x'] ** 2 + output_sample['spin_2y'] ** 2 - ) + ) ** 0.5 - output_sample['chi_p'] = np.maximum( + output_sample['chi_p'] = xp.maximum( output_sample['chi_1_in_plane'], (4 * output_sample['mass_ratio'] + 3) / (3 * output_sample['mass_ratio'] + 4) * output_sample['mass_ratio'] * output_sample['chi_2_in_plane']) try: - output_sample['cos_tilt_1'] = np.cos(output_sample['tilt_1']) - output_sample['cos_tilt_2'] = np.cos(output_sample['tilt_2']) + output_sample['cos_tilt_1'] = xp.cos(output_sample['tilt_1']) + output_sample['cos_tilt_2'] = xp.cos(output_sample['tilt_2']) except KeyError: pass @@ -2116,12 +2120,13 @@ def generate_component_spins(sample): ['theta_jn', 'phi_jl', 'tilt_1', 'tilt_2', 'phi_12', 'a_1', 'a_2', 'mass_1', 'mass_2', 'reference_frequency', 'phase'] if all(key in output_sample.keys() for key in spin_conversion_parameters): + xp = array_module(output_sample["theta_jn"]) ( output_sample['iota'], output_sample['spin_1x'], output_sample['spin_1y'], output_sample['spin_1z'], output_sample['spin_2x'], output_sample['spin_2y'], output_sample['spin_2z'] - ) = np.vectorize(bilby_to_lalsimulation_spins)( + ) = xp.vectorize(bilby_to_lalsimulation_spins)( output_sample['theta_jn'], output_sample['phi_jl'], output_sample['tilt_1'], output_sample['tilt_2'], output_sample['phi_12'], output_sample['a_1'], output_sample['a_2'], @@ -2131,10 +2136,10 @@ def generate_component_spins(sample): ) output_sample['phi_1'] =\ - np.fmod(2 * np.pi + np.arctan2( + xp.fmod(2 * np.pi + xp.arctan2( output_sample['spin_1y'], output_sample['spin_1x']), 2 * np.pi) output_sample['phi_2'] =\ - np.fmod(2 * np.pi + np.arctan2( + xp.fmod(2 * np.pi + xp.arctan2( output_sample['spin_2y'], output_sample['spin_2x']), 2 * np.pi) elif 'chi_1' in output_sample and 'chi_2' in output_sample: diff --git a/bilby/gw/detector/networks.py b/bilby/gw/detector/networks.py index 6ed70fd44..726350870 100644 --- a/bilby/gw/detector/networks.py +++ b/bilby/gw/detector/networks.py @@ -2,6 +2,7 @@ import numpy as np import math +from bilback.geometry import zenith_azimuth_to_theta_phi from ...core import utils from ...core.utils import logger, safe_file_dump @@ -476,3 +477,26 @@ def load_interferometer(filename): "{} could not be loaded. Invalid parameter 'shape'.".format(filename) ) return ifo + + +@zenith_azimuth_to_theta_phi.dispatch +def zenith_azimuth_to_theta_phi(zenith, azimuth, ifos: InterferometerList | list): + """ + Convert from the 'detector frame' to the Earth frame. + + Parameters + ========== + kappa: float + The zenith angle in the detector frame + eta: float + The azimuthal angle in the detector frame + ifos: list + List of Interferometer objects defining the detector frame + + Returns + ======= + theta, phi: float + The zenith and azimuthal angles in the earth frame. + """ + delta_x = ifos[0].geometry.vertex - ifos[1].geometry.vertex + return zenith_azimuth_to_theta_phi(zenith, azimuth, delta_x) diff --git a/bilby/gw/jaxstuff.py b/bilby/gw/jaxstuff.py index 464a7abb7..f1e1c57b0 100644 --- a/bilby/gw/jaxstuff.py +++ b/bilby/gw/jaxstuff.py @@ -86,6 +86,7 @@ def generic_bilby_likelihood_function(likelihood, parameters, use_ratio=True): Whether to evaluate the likelihood ratio or the full likelihood. Default is :code:`True`. """ + parameters = {k: jnp.array(v) for k, v in parameters.items()} likelihood.parameters.update(parameters) if use_ratio: return likelihood.log_likelihood_ratio() diff --git a/bilby/gw/likelihood/base.py b/bilby/gw/likelihood/base.py index 7781ca836..dbee4c41c 100644 --- a/bilby/gw/likelihood/base.py +++ b/bilby/gw/likelihood/base.py @@ -4,6 +4,7 @@ import attr import numpy as np +from bilback.utils import array_module from scipy.special import logsumexp from ...core.likelihood import Likelihood @@ -169,6 +170,7 @@ def __init__( if "geocent" not in time_reference: self.time_reference = time_reference self.reference_ifo = get_empty_interferometer(self.time_reference) + self.reference_ifo.set_array_backend(array_module(self.interferometers[0].vertex)) if self.time_marginalization: logger.info("Cannot marginalise over non-geocenter time.") self.time_marginalization = False diff --git a/bilby/gw/utils.py b/bilby/gw/utils.py index ad0f14e64..7ad4ab40d 100644 --- a/bilby/gw/utils.py +++ b/bilby/gw/utils.py @@ -5,9 +5,7 @@ import numpy as np from scipy.interpolate import interp1d from scipy.special import i0e -from bilback.geometry import ( - zenith_azimuth_to_theta_phi as _zenith_azimuth_to_theta_phi, -) +from bilback.geometry import zenith_azimuth_to_theta_phi from bilback.time import greenwich_mean_sidereal_time from bilback.utils import array_module from plum import dispatch @@ -230,28 +228,6 @@ def overlap(signal_a, signal_b, power_spectral_density=None, delta_frequency=Non return sum(integral).real -def zenith_azimuth_to_theta_phi(zenith, azimuth, ifos): - """ - Convert from the 'detector frame' to the Earth frame. - - Parameters - ========== - kappa: float - The zenith angle in the detector frame - eta: float - The azimuthal angle in the detector frame - ifos: list - List of Interferometer objects defining the detector frame - - Returns - ======= - theta, phi: float - The zenith and azimuthal angles in the earth frame. - """ - delta_x = ifos[0].geometry.vertex - ifos[1].geometry.vertex - return _zenith_azimuth_to_theta_phi(zenith, azimuth, delta_x) - - def zenith_azimuth_to_ra_dec(zenith, azimuth, geocent_time, ifos): """ Convert from the 'detector frame' to the Earth frame. @@ -272,6 +248,8 @@ def zenith_azimuth_to_ra_dec(zenith, azimuth, geocent_time, ifos): ra, dec: float The zenith and azimuthal angles in the sky frame. """ + # delta_x = ifos[0].geometry.vertex - ifos[1].geometry.vertex + # theta, phi = zenith_azimuth_to_theta_phi(zenith, azimuth, delta_x) theta, phi = zenith_azimuth_to_theta_phi(zenith, azimuth, ifos) gmst = greenwich_mean_sidereal_time(geocent_time) ra, dec = theta_phi_to_ra_dec(theta, phi, gmst) From d29b860061c53d830724dd452c628a79b03ecda6 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Mon, 28 Oct 2024 10:36:55 -0500 Subject: [PATCH 004/140] FEAT: use more normal conversions --- bilby/gw/conversion.py | 47 ++++++++- bilby/gw/likelihood/base.py | 37 +++---- .../injection_examples/jax_fast_tutorial.py | 99 ++++++++++++++++--- 3 files changed, 149 insertions(+), 34 deletions(-) diff --git a/bilby/gw/conversion.py b/bilby/gw/conversion.py index b1a1e3fde..c369fee04 100644 --- a/bilby/gw/conversion.py +++ b/bilby/gw/conversion.py @@ -179,6 +179,40 @@ def transform_precessing_spins(*args): return lalsim_SimInspiralTransformPrecessingNewInitialConditions(*args) +def convert_orientation_quaternion(parameters): + xp = array_module(parameters["orientation_w"]) + norm = ( + parameters["orientation_w"]**2 + + parameters["orientation_x"]**2 + + parameters["orientation_y"]**2 + + parameters["orientation_z"]**2 + )**0.5 + parameters["theta_jn"] = 2 * xp.arccos( + parameters["orientation_z"] / norm + ) + parameters["psi"] = xp.arctan2( + parameters["orientation_w"], + parameters["orientation_y"] + + parameters["orientation_x"], + ) / 2 + parameters["delta_phase"] = xp.arctan2( + parameters["orientation_y"], + parameters["orientation_x"], + ) / 2 + + +def convert_cartesian(parameters, label): + spin_norm = ( + parameters[f"{label}_x"]**2 + + parameters[f"{label}_y"]**2 + + parameters[f"{label}_z"]**2 + )**0.5 + xp = array_module(spin_norm) + zenith = xp.arccos(parameters[f"{label}_z"] / spin_norm) + azimuth = xp.arctan2(parameters[f"{label}_y"], parameters[f"{label}_x"]) + return zenith, azimuth + + def convert_to_lal_binary_black_hole_parameters(parameters): """ Convert parameters we have into parameters we need. @@ -230,6 +264,14 @@ def convert_to_lal_binary_black_hole_parameters(parameters): converted_parameters = generate_component_masses(converted_parameters, require_add=False) for idx in ['1', '2']: + if f"spin_{idx}_x" in original_keys: + converted_parameters["tilt_1"], converted_parameters["phi_jl"] = ( + convert_cartesian(converted_parameters, "spin_1") + ) + converted_parameters["tilt_2"], converted_parameters["phi_12"] = ( + convert_cartesian(converted_parameters, "spin_2") + ) + converted_parameters["phi_12"] -= converted_parameters["phi_jl"] key = 'chi_{}'.format(idx) if key in original_keys: if "chi_{}_in_plane".format(idx) in original_keys: @@ -260,6 +302,9 @@ def convert_to_lal_binary_black_hole_parameters(parameters): ) converted_parameters[f"cos_tilt_{idx}"] = 1.0 + if "orientation_w" in original_keys: + convert_orientation_quaternion(converted_parameters) + for key in ["phi_jl", "phi_12"]: if key not in converted_parameters: converted_parameters[key] = 0.0 @@ -270,7 +315,7 @@ def convert_to_lal_binary_black_hole_parameters(parameters): with np.errstate(invalid="ignore"): converted_parameters[angle] = xp.arccos(converted_parameters[cos_angle]) - if "delta_phase" in original_keys: + if "delta_phase" in converted_parameters: with np.errstate(invalid="ignore"): converted_parameters["phase"] = xp.mod( converted_parameters["delta_phase"] diff --git a/bilby/gw/likelihood/base.py b/bilby/gw/likelihood/base.py index dbee4c41c..7c27c5a96 100644 --- a/bilby/gw/likelihood/base.py +++ b/bilby/gw/likelihood/base.py @@ -413,6 +413,7 @@ def noise_log_likelihood(self): def log_likelihood_ratio(self, parameters): parameters = copy.deepcopy(parameters) + parameters.update(self.get_sky_frame_parameters(parameters)) waveform_polarizations = \ self.waveform_generator.frequency_domain_strain(parameters) if waveform_polarizations is None: @@ -421,8 +422,6 @@ def log_likelihood_ratio(self, parameters): if self.time_marginalization and self.jitter_time: parameters['geocent_time'] += parameters['time_jitter'] - parameters.update(self.get_sky_frame_parameters(parameters)) - total_snrs = self._CalculatedSNRs() for interferometer in self.interferometers: @@ -473,14 +472,13 @@ def compute_log_likelihood_from_snrs(self, total_snrs, parameters): return log_l def compute_per_detector_log_likelihood(self, parameters): + parameters.update(self.get_sky_frame_parameters(parameters)) waveform_polarizations = \ self.waveform_generator.frequency_domain_strain(parameters) if self.time_marginalization and self.jitter_time: parameters['geocent_time'] += parameters['time_jitter'] - parameters.update(self.get_sky_frame_parameters(parameters)) - for interferometer in self.interferometers: per_detector_snr = self.calculate_snrs( waveform_polarizations=waveform_polarizations, @@ -1102,20 +1100,25 @@ def get_sky_frame_parameters(self, parameters): "Falling back to geocent time" ) if not self.reference_frame == "sky": - try: + if "sky_x" in parameters: + zenith, azimuth = convert_cartesian(parameters, "sky") + elif "zenith" in parameters: + zenith = parameters["zenith"] + azimuth = parameters["azimuth"] + elif "ra" in parameters and "dec" in parameters: + ra = parameters["ra"] + dec = parameters["dec"] + logger.warning( + "Cannot convert from zenith/azimuth to ra/dec falling " + "back to provided ra/dec" + ) + zenith = None + else: + raise KeyError("No sky location parameters recognised") + if zenith is not None: ra, dec = zenith_azimuth_to_ra_dec( - parameters['zenith'], parameters['azimuth'], - time, self.reference_frame) - except KeyError: - if "ra" in parameters and "dec" in parameters: - ra = parameters["ra"] - dec = parameters["dec"] - logger.warning( - "Cannot convert from zenith/azimuth to ra/dec falling " - "back to provided ra/dec" - ) - else: - raise + zenith, azimuth, time, self.reference_frame + ) else: ra = parameters["ra"] dec = parameters["dec"] diff --git a/examples/gw_examples/injection_examples/jax_fast_tutorial.py b/examples/gw_examples/injection_examples/jax_fast_tutorial.py index 930cc6de5..b1b586ecf 100644 --- a/examples/gw_examples/injection_examples/jax_fast_tutorial.py +++ b/examples/gw_examples/injection_examples/jax_fast_tutorial.py @@ -18,11 +18,14 @@ import bilby import bilby.gw.jaxstuff import jax +import jax.numpy as jnp +from jax import random from numpyro.infer import AIES, ESS # noqa +from numpyro.infer.ensemble_util import get_nondiagonal_indices jax.config.update("jax_enable_x64", True) -bilby.core.utils.setup_logger(log_level="WARNING") +bilby.core.utils.setup_logger() # log_level="WARNING") def main(use_jax, model): @@ -146,7 +149,28 @@ def main(use_jax, model): ]: priors[key] = injection_parameters[key] del priors["mass_1"], priors["mass_2"] - priors["L1_time"] = bilby.core.prior.Uniform(1126259642.313, 1126259642.513) + priors["L1_time"] = bilby.core.prior.Uniform(1126259642.41, 1126259642.45) + del priors["ra"], priors["dec"] + # priors["zenith"] = bilby.core.prior.Cosine() + # priors["azimuth"] = bilby.core.prior.Uniform(minimum=0, maximum=2 * np.pi) + priors["sky_x"] = bilby.core.prior.Normal(mu=0, sigma=1) + priors["sky_y"] = bilby.core.prior.Normal(mu=0, sigma=1) + priors["sky_z"] = bilby.core.prior.Normal(mu=0, sigma=1) + priors["delta_phase"] = priors.pop("phase") + priors["chirp_mass"].minimum = 20 + priors["chirp_mass"].maximum = 35 + del priors["tilt_1"], priors["tilt_2"], priors["phi_12"], priors["phi_jl"] + priors["spin_1_x"] = bilby.core.prior.Normal(mu=0, sigma=1) + priors["spin_1_y"] = bilby.core.prior.Normal(mu=0, sigma=1) + priors["spin_1_z"] = bilby.core.prior.Normal(mu=0, sigma=1) + priors["spin_2_x"] = bilby.core.prior.Normal(mu=0, sigma=1) + priors["spin_2_y"] = bilby.core.prior.Normal(mu=0, sigma=1) + priors["spin_2_z"] = bilby.core.prior.Normal(mu=0, sigma=1) + del priors["theta_jn"], priors["psi"], priors["delta_phase"] + priors["orientation_w"] = bilby.core.prior.Normal(mu=0, sigma=1) + priors["orientation_x"] = bilby.core.prior.Normal(mu=0, sigma=1) + priors["orientation_y"] = bilby.core.prior.Normal(mu=0, sigma=1) + priors["orientation_z"] = bilby.core.prior.Normal(mu=0, sigma=1) # Perform a check that the prior does not extend to a parameter space longer than the data if not use_jax: @@ -167,7 +191,7 @@ def main(use_jax, model): interferometers=ifos, waveform_generator=waveform_generator, priors=priors, - phase_marginalization=True, + # phase_marginalization=True, reference_frame=ifos, time_reference="L1", ) @@ -190,7 +214,6 @@ def sample(): jit_likelihood = bilby.gw.jaxstuff.JittedLikelihood( likelihood, cast_to_float=False, - jit=True, ) jit_likelihood.parameters.update(sample()) jit_likelihood.log_likelihood_ratio() @@ -216,29 +239,36 @@ def likelihood_func(parameters): result = bilby.run_sampler( likelihood=sample_likelihood, priors=priors, - # sampler="dynesty", - sampler="numpyro", + sampler="dynesty", + # sampler="numpyro", sampler_name="ESS", - num_warmup=100, - num_samples=100, - num_chains=40, - thinning=2, - # moves={AIES.DEMove(): 0.25, AIES.DEMove(g0=1): 0.5, AIES.StretchMove(): 0.25}, + # sampler_name="NUTS", + num_warmup=500, + num_samples=500, + num_chains=100, + thinning=5, + # moves={ + # AIES.DEMove(): 0.35, + # ModeHopping(): 0.3, + # AIES.StretchMove(): 0.35, + # }, moves={ ESS.DifferentialMove(): 0.25, ESS.KDEMove(): 0.25, ESS.GaussianMove(): 0.5, }, chain_method="vectorized", - npoints=100, - sample="acceptance-walk", + npoints=500, + # sample="acceptance-walk", + sample="act-walk", naccept=10, injection_parameters=injection_parameters, outdir=outdir, label=label, + npool=4, ) - print(result) - print(f"Sampling time: {result.sampling_time:.1f}s\n") + # print(result) + # print(f"Sampling time: {result.sampling_time:.1f}s\n") # Make a corner plot. result.plot_corner() @@ -246,8 +276,45 @@ def likelihood_func(parameters): return result.sampling_time +def ModeHopping(): + """ + A proposal using differential evolution. + + This `Differential evolution proposal + `_ is + implemented following `Nelson et al. (2013) + `_. + + :param sigma: (optional) + The standard deviation of the Gaussian used to stretch the proposal vector. + Defaults to `1.0.e-5`. + :param g0 (optional): + The mean stretch factor for the proposal vector. By default, + it is `2.38 / sqrt(2*ndim)` as recommended by the two references. + """ + + def make_de_move(n_chains): + PAIRS = get_nondiagonal_indices(n_chains // 2) + + def de_move(rng_key, active, inactive): + n_active_chains, _ = inactive.shape + + selected_pairs = random.choice(rng_key, PAIRS, shape=(n_active_chains,)) + + # Compute diff vectors + diffs = jnp.diff(inactive[selected_pairs], axis=1).squeeze(axis=1) + + proposal = active + diffs + + return proposal, jnp.zeros(n_active_chains) + + return de_move + + return make_de_move + + if __name__ == "__main__": times = dict() - for arg in product([True, False], ["relbin", "mb", "regular"][-1:]): + for arg in product([True, False][1:], ["relbin", "mb", "regular"][1:2]): times[arg] = main(*arg) print(times) From c5eb323511c8d92b11a062a834ac0ca286d2aca4 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Wed, 13 Nov 2024 13:25:36 -0800 Subject: [PATCH 005/140] FEAT: move backend switching code to bilby --- bilby/compat/__init__.py | 0 bilby/compat/types.py | 6 + bilby/compat/utils.py | 22 +++ bilby/core/utils/series.py | 2 +- bilby/gw/__init__.py | 1 + bilby/gw/compat/__init__.py | 4 + bilby/gw/compat/jax.py | 18 ++ bilby/gw/detector/geometry.py | 2 +- bilby/gw/detector/interferometer.py | 13 +- bilby/gw/detector/networks.py | 2 +- bilby/gw/geometry.py | 258 +++++++++++++++++++++++++++ bilby/gw/likelihood/base.py | 2 +- bilby/gw/likelihood/multiband.py | 2 +- bilby/gw/likelihood/relative.py | 2 +- bilby/gw/time.py | 259 ++++++++++++++++++++++++++++ bilby/gw/utils.py | 6 +- 16 files changed, 583 insertions(+), 16 deletions(-) create mode 100644 bilby/compat/__init__.py create mode 100644 bilby/compat/types.py create mode 100644 bilby/compat/utils.py create mode 100644 bilby/gw/compat/__init__.py create mode 100644 bilby/gw/compat/jax.py create mode 100644 bilby/gw/geometry.py create mode 100644 bilby/gw/time.py diff --git a/bilby/compat/__init__.py b/bilby/compat/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/bilby/compat/types.py b/bilby/compat/types.py new file mode 100644 index 000000000..8a3391c44 --- /dev/null +++ b/bilby/compat/types.py @@ -0,0 +1,6 @@ +from typing import Union +import numpy as np + +Real = Union[float, int] +ArrayLike = Union[np.ndarray, list, tuple] + diff --git a/bilby/compat/utils.py b/bilby/compat/utils.py new file mode 100644 index 000000000..2943c1ae6 --- /dev/null +++ b/bilby/compat/utils.py @@ -0,0 +1,22 @@ +import numpy as np +from array_api_compat import array_namespace + +__all__ = ["array_module", "promote_to_array"] + + +def array_module(arr): + if arr.__class__.__module__ == "builtins": + return np + else: + return array_namespace(arr) + + +def promote_to_array(args, backend, skip=None): + if skip is None: + skip = len(args) + else: + skip = len(args) - skip + if backend.__name__ != "numpy": + args = tuple(backend.array(arg) for arg in args[:skip]) + args[skip:] + return args + diff --git a/bilby/core/utils/series.py b/bilby/core/utils/series.py index c3d71e3e2..8affa61be 100644 --- a/bilby/core/utils/series.py +++ b/bilby/core/utils/series.py @@ -1,5 +1,5 @@ import numpy as np -from bilback.utils import array_module +from ...compat.utils import array_module _TOL = 14 diff --git a/bilby/gw/__init__.py b/bilby/gw/__init__.py index b5115766b..cd09bc6f6 100644 --- a/bilby/gw/__init__.py +++ b/bilby/gw/__init__.py @@ -3,4 +3,5 @@ from .waveform_generator import WaveformGenerator, LALCBCWaveformGenerator from .likelihood import GravitationalWaveTransient from .detector import calibration +from . import compat diff --git a/bilby/gw/compat/__init__.py b/bilby/gw/compat/__init__.py new file mode 100644 index 000000000..8e2e63c62 --- /dev/null +++ b/bilby/gw/compat/__init__.py @@ -0,0 +1,4 @@ +try: + from .jax import n_leap_seconds +except ModuleNotFoundError: + pass diff --git a/bilby/gw/compat/jax.py b/bilby/gw/compat/jax.py new file mode 100644 index 000000000..9b0732112 --- /dev/null +++ b/bilby/gw/compat/jax.py @@ -0,0 +1,18 @@ +import jax.numpy as jnp +from jax import Array +from plum import dispatch + +from ..time import LEAP_SECONDS as _LEAP_SECONDS, n_leap_seconds + +__all__ = ["n_leap_seconds"] + +LEAP_SECONDS = jnp.array(_LEAP_SECONDS) + + +@dispatch +def n_leap_seconds(date: Array): + """ + Find the number of leap seconds required for the specified date. + """ + return n_leap_seconds(date, LEAP_SECONDS) + diff --git a/bilby/gw/detector/geometry.py b/bilby/gw/detector/geometry.py index ea2a509ab..627f6a143 100644 --- a/bilby/gw/detector/geometry.py +++ b/bilby/gw/detector/geometry.py @@ -1,5 +1,5 @@ import numpy as np -from bilback.geometry import calculate_arm, detector_tensor +from ..geometry import calculate_arm, detector_tensor from .. import utils as gwutils diff --git a/bilby/gw/detector/interferometer.py b/bilby/gw/detector/interferometer.py index 38b00aaf4..ac98d8b82 100644 --- a/bilby/gw/detector/interferometer.py +++ b/bilby/gw/detector/interferometer.py @@ -1,17 +1,16 @@ import os import numpy as np -from bilback.geometry import ( - get_polarization_tensor, - three_by_three_matrix_contraction, - time_delay_from_geocenter, -) -from bilback.utils import array_module from ...core import utils from ...core.utils import docstring, logger, PropertyAccessor, safe_file_dump -from ...core.utils.env import string_to_boolean +from ...compat.utils import array_module from .. import utils as gwutils +from ..geometry import ( + get_polarization_tensor, + three_by_three_matrix_contraction, + time_delay_from_geocenter, +) from .calibration import Recalibrate from .geometry import InterferometerGeometry from .strain_data import InterferometerStrainData diff --git a/bilby/gw/detector/networks.py b/bilby/gw/detector/networks.py index 726350870..5b9072846 100644 --- a/bilby/gw/detector/networks.py +++ b/bilby/gw/detector/networks.py @@ -2,10 +2,10 @@ import numpy as np import math -from bilback.geometry import zenith_azimuth_to_theta_phi from ...core import utils from ...core.utils import logger, safe_file_dump +from ..geometry import zenith_azimuth_to_theta_phi from .interferometer import Interferometer from .psd import PowerSpectralDensity diff --git a/bilby/gw/geometry.py b/bilby/gw/geometry.py new file mode 100644 index 000000000..60d55f8be --- /dev/null +++ b/bilby/gw/geometry.py @@ -0,0 +1,258 @@ +import numpy as np +from plum import dispatch +from bilby_rust import geometry as _geometry + +from .time import greenwich_mean_sidereal_time +from ..compat.types import Real, ArrayLike +from ..compat.utils import array_module, promote_to_array + + +__all__ = [ + "antenna_response", + "calculate_arm", + "detector_tensor", + "get_polarization_tensor", + "get_polarization_tensor_multiple_modes", + "rotation_matrix_from_delta", + "three_by_three_matrix_contraction", + "time_delay_geocentric", + "time_delay_from_geocenter", + "zenith_azimuth_to_theta_phi", +] + + +@dispatch +def antenna_response(detector_tensor, ra, dec, time, psi, mode): + """""" + xp = array_module(detector_tensor) + polarization_tensor = get_polarization_tensor(*promote_to_array((ra, dec, time, psi), xp), mode) + return three_by_three_matrix_contraction(detector_tensor, polarization_tensor) + + +@dispatch +def calculate_arm(arm_tilt, arm_azimuth, longitude, latitude): + """""" + xp = array_module(arm_tilt) + e_long = xp.array([-xp.sin(longitude), xp.cos(longitude), longitude * 0]) + e_lat = xp.array( + [ + -xp.sin(latitude) * xp.cos(longitude), + -xp.sin(latitude) * xp.sin(longitude), + xp.cos(latitude), + ] + ) + e_h = xp.array( + [ + xp.cos(latitude) * xp.cos(longitude), + xp.cos(latitude) * xp.sin(longitude), + xp.sin(latitude), + ] + ) + + return ( + xp.cos(arm_tilt) * xp.cos(arm_azimuth) * e_long + + xp.cos(arm_tilt) * xp.sin(arm_azimuth) * e_lat + + xp.sin(arm_tilt) * e_h + ) + + +@dispatch +def detector_tensor(x, y): + """""" + xp = array_module(x) + return (xp.outer(x, x) - xp.outer(y, y)) / 2 + + +@dispatch +def get_polarization_tensor(ra, dec, time, psi, mode): + """""" + from functools import partial + + xp = array_module(ra) + + gmst = greenwich_mean_sidereal_time(time) % (2 * xp.pi) + phi = ra - gmst + theta = xp.atleast_1d(xp.pi / 2 - dec).squeeze() + u = xp.array( + [ + xp.cos(phi) * xp.cos(theta), + xp.cos(theta) * xp.sin(phi), + -xp.sin(theta) * xp.ones_like(phi), + ] + ) + v = xp.array([ + -xp.sin(phi), xp.cos(phi), xp.zeros_like(phi) + ]) * xp.ones_like(theta) + omega = xp.array([ + xp.sin(xp.pi - theta) * xp.cos(xp.pi + phi), + xp.sin(xp.pi - theta) * xp.sin(xp.pi + phi), + xp.cos(xp.pi - theta) * xp.ones_like(phi), + ]) + m = -u * xp.sin(psi) - v * xp.cos(psi) + n = -u * xp.cos(psi) + v * xp.sin(psi) + if xp.__name__ == "mlx.core": + einsum_shape = "i,j->ij" + else: + einsum_shape = "i...,j...->ij..." + product = partial(xp.einsum, einsum_shape) + + match mode.lower(): + case "plus": + return product(m, m) - product(n, n) + case "cross": + return product(m, n) + product(n, m) + case "breathing": + return product(m, m) + product(n, n) + case "longitudinal": + return product(omega, omega) + case "x": + return product(m, omega) + product(omega, m) + case "y": + return product(n, omega) + product(omega, n) + case _: + raise ValueError(f"{mode} not a polarization mode!") + + +@dispatch +def get_polarization_tensor_multiple_modes(ra, dec, time, psi, modes): + """""" + return [get_polarization_tensor(ra, dec, time, psi, mode) for mode in modes] + + +@dispatch +def rotation_matrix_from_delta(delta_x): + """""" + xp = array_module(delta_x) + delta_x = delta_x / (delta_x**2).sum() ** 0.5 + alpha = xp.arctan2(-delta_x[1] * delta_x[2], delta_x[0]) + beta = xp.arccos(delta_x[2]) + gamma = xp.arctan2(delta_x[1], delta_x[0]) + rotation_1 = xp.array( + [ + [xp.cos(alpha), -xp.sin(alpha), xp.zeros(alpha.shape)], + [xp.sin(alpha), xp.cos(alpha), xp.zeros(alpha.shape)], + [xp.zeros(alpha.shape), xp.zeros(alpha.shape), xp.ones(alpha.shape)], + ] + ) + rotation_2 = xp.array( + [ + [xp.cos(beta), xp.zeros(beta.shape), xp.sin(beta)], + [xp.zeros(beta.shape), xp.ones(beta.shape), xp.zeros(beta.shape)], + [-xp.sin(beta), xp.zeros(beta.shape), xp.cos(beta)], + ] + ) + rotation_3 = xp.array( + [ + [xp.cos(gamma), -xp.sin(gamma), xp.zeros(gamma.shape)], + [xp.sin(gamma), xp.cos(gamma), xp.zeros(gamma.shape)], + [xp.zeros(gamma.shape), xp.zeros(gamma.shape), xp.ones(gamma.shape)], + ] + ) + return rotation_3 @ rotation_2 @ rotation_1 + + + +@dispatch +def three_by_three_matrix_contraction(a, b): + """""" + xp = array_module(a) + return xp.einsum("ij,ij->", a, b) + + +@dispatch +def time_delay_geocentric(detector1, detector2, ra, dec, time): + """""" + xp = array_module(detector1) + gmst = greenwich_mean_sidereal_time(time) % (2 * xp.pi) + speed_of_light = 299792458.0 + phi = ra - gmst + theta = xp.pi / 2 - dec + omega = xp.array( + [xp.sin(theta) * xp.cos(phi), xp.sin(theta) * xp.sin(phi), xp.cos(theta)] + ) + delta_d = detector2 - detector1 + return omega @ delta_d / speed_of_light + + + +@dispatch +def time_delay_from_geocenter(detector1, ra, dec, time): + """""" + xp = array_module(detector1) + return time_delay_geocentric(detector1, xp.zeros(3), ra, dec, time) + + +@dispatch +def zenith_azimuth_to_theta_phi(zenith, azimuth, delta_x): + """""" + xp = array_module(delta_x) + omega_prime = xp.array( + [ + xp.sin(zenith) * xp.cos(azimuth), + xp.sin(zenith) * xp.sin(azimuth), + xp.cos(zenith), + ] + ) + rotation_matrix = rotation_matrix_from_delta(delta_x) + omega = rotation_matrix @ omega_prime + theta = xp.arccos(omega[2]) + phi = xp.arctan2(omega[1], omega[0]) % (2 * xp.pi) + return theta, phi + + + +# @dispatch(precedence=1) +# def antenna_response(detector_tensor: np.ndarray, ra: FloatOrInt, dec: FloatOrInt, time: FloatOrInt, psi: FloatOrInt, mode: str): +# return _geometry.antenna_response(detector_tensor, ra, dec, time, psi, mode) + + +@dispatch(precedence=1) +def calculate_arm(arm_tilt: Real, arm_azimuth: Real, longitude: Real, latitude: Real): + return _geometry.calculate_arm(arm_tilt, arm_azimuth, longitude, latitude) + + +@dispatch(precedence=1) +def detector_tensor(x: ArrayLike, y: ArrayLike): + return _geometry.detector_tensor(x, y) + + +@dispatch(precedence=1) +def get_polarization_tensor(ra: Real, dec: Real, time: Real, psi: Real, mode: str): + return _geometry.get_polarization_tensor(ra, dec, time, psi, mode) + + +# @dispatch(precedence=1) +# def get_polarization_tensor_multiple_modes(ra: FloatOrInt, dec: FloatOrInt, time: FloatOrInt, psi: FloatOrInt, modes: list[str]): +# return [geometry.get_polarization_tensor(ra, dec, time, psi, mode) for mode in modes] + + +@dispatch(precedence=1) +def rotation_matrix_from_delta(delta: ArrayLike): + return _geometry.rotation_matrix_from_delta_x(delta) + + +# @dispatch(precedence=1) +# def three_by_three_matrix_contraction(a: ArrayLike, b: ArrayLike): +# return _geometry.three_by_three_matrix_contraction(a, b) + + +@dispatch(precedence=1) +def time_delay_geocentric(detector1: ArrayLike, detector2: ArrayLike, ra, dec, time): + return _geometry.time_delay_geocentric(detector1, detector2, ra, dec, time) + + +@dispatch(precedence=1) +def time_delay_from_geocenter(detector1: ArrayLike, ra: Real, dec: Real, time: Real): + return _geometry.time_delay_from_geocenter(detector1, ra, dec, time) + + +@dispatch(precedence=1) +def time_delay_from_geocenter(detector1: ArrayLike, ra: Real, dec: Real, time: ArrayLike): + return _geometry.time_delay_from_geocenter_vectorized(detector1, ra, dec, time) + + +@dispatch(precedence=1) +def zenith_azimuth_to_theta_phi(zenith: Real, azimuth: Real, delta_x: np.ndarray): + theta, phi = _geometry.zenith_azimuth_to_theta_phi(zenith, azimuth, delta_x) + return theta, phi % (2 * np.pi) + diff --git a/bilby/gw/likelihood/base.py b/bilby/gw/likelihood/base.py index 7c27c5a96..a7c5c35cb 100644 --- a/bilby/gw/likelihood/base.py +++ b/bilby/gw/likelihood/base.py @@ -4,9 +4,9 @@ import attr import numpy as np -from bilback.utils import array_module from scipy.special import logsumexp +from ...compat.utils import array_module from ...core.likelihood import Likelihood from ...core.utils import logger, BoundedRectBivariateSpline, create_time_series from ...core.prior import Interped, Prior, Uniform, DeltaFunction diff --git a/bilby/gw/likelihood/multiband.py b/bilby/gw/likelihood/multiband.py index dd743835e..cc5d46ed7 100644 --- a/bilby/gw/likelihood/multiband.py +++ b/bilby/gw/likelihood/multiband.py @@ -3,9 +3,9 @@ import numbers import numpy as np -from bilback.utils import array_module from .base import GravitationalWaveTransient +from ...compat.utils import array_module from ...core.utils import ( logger, speed_of_light, solar_mass, radius_of_earth, gravitational_constant, round_up_to_power_of_two, diff --git a/bilby/gw/likelihood/relative.py b/bilby/gw/likelihood/relative.py index fbf43e944..152c120f7 100644 --- a/bilby/gw/likelihood/relative.py +++ b/bilby/gw/likelihood/relative.py @@ -2,9 +2,9 @@ import numpy as np from scipy.optimize import differential_evolution -from bilback.utils import array_module from .base import GravitationalWaveTransient +from ...compat.utils import array_module from ...core.utils import logger from ...core.prior.base import Constraint from ...core.prior import DeltaFunction diff --git a/bilby/gw/time.py b/bilby/gw/time.py new file mode 100644 index 000000000..cb56b3bab --- /dev/null +++ b/bilby/gw/time.py @@ -0,0 +1,259 @@ +from typing import Union + +import numpy as np +from plum import dispatch +from bilby_rust import time as _time + +from ..compat.types import Real, ArrayLike +from ..compat.utils import array_module + + +__all__ = [ + "datetime", + "gps_time_to_utc", + "greenwich_mean_sidereal_time", + "greenwich_sidereal_time", + "n_leap_seconds", + "utc_to_julian_day", + "LEAP_SECONDS", +] + + +class datetime: + """ + A barebones datetime class for use in the GPS to GMST conversion. + """ + + def __init__( + self, + year: int = 0, + month: int = 0, + day: int = 0, + hour: int = 0, + minute: int = 0, + second: float = 0, + ): + self.year = year + self.month = month + self.day = day + self.hour = hour + self.minute = minute + self.second = second + + def __repr__(self): + return f"{self.year}-{self.month}-{self.day} {self.hour}:{self.minute}:{self.second}" + + def __add__(self, other): + """ + Add two datetimes together. + Note that this does not handle overflow and can lead to unphysical + values for the various attributes. + """ + return datetime( + self.year + other.year, + self.month + other.month, + self.day + other.day, + self.hour + other.hour, + self.minute + other.minute, + self.second + other.second, + ) + + @property + def julian_day(self): + return ( + 367 * self.year + - 7 * (self.year + (self.month + 9) // 12) // 4 + + 275 * self.month // 9 + + self.day + + self.second / SECONDS_PER_DAY + + JULIAN_GPS_EPOCH + ) + + +GPS_EPOCH = datetime(1980, 1, 6, 0, 0, 0) +JULIAN_GPS_EPOCH = 1721013.5 +EPOCH_J2000_0_JD = 2451545.0 +DAYS_PER_CENTURY = 36525.0 +SECONDS_PER_DAY = 86400.0 +LEAP_SECONDS = [ + 46828800, + 78364801, + 109900802, + 173059203, + 252028804, + 315187205, + 346723206, + 393984007, + 425520008, + 457056009, + 504489610, + 551750411, + 599184012, + 820108813, + 914803214, + 1025136015, + 1119744016, + 1167264017, +] + + +@dispatch +def gps_time_to_utc(gps_time): + """ + Convert GPS time to UTC. + + Parameters + ---------- + gps_time : float + GPS time in seconds. + + Returns + ------- + datetime + UTC time. + """ + return GPS_EPOCH + datetime(second=gps_time - n_leap_seconds(gps_time)) + + +@dispatch +def greenwich_mean_sidereal_time(gps_time): + """ + Calculate the Greenwich Mean Sidereal Time. + + This is a thin wrapper around :py:func:`greenwich_sidereal_time` with the + equation of the equinoxes set to zero. + + Parameters + ---------- + gps_time : float + GPS time in seconds. + + Returns + ------- + float + Greenwich Mean Sidereal Time in radians. + """ + return greenwich_sidereal_time(gps_time, gps_time * 0) + + +@dispatch +def greenwich_sidereal_time(gps_time, equation_of_equinoxes): + """ + Calculate the Greenwich Sidereal Time. + + Parameters + ---------- + gps_time : float + GPS time in seconds. + equation_of_equinoxes : float + Equation of the equinoxes in seconds. + + Returns + ------- + float + """ + julian_day = utc_to_julian_day(gps_time_to_utc(gps_time // 1)) + t_hi = (julian_day - EPOCH_J2000_0_JD) / DAYS_PER_CENTURY + t_lo = (gps_time % 1) / (DAYS_PER_CENTURY * SECONDS_PER_DAY) + + t = t_hi + t_lo + + sidereal_time = ( + equation_of_equinoxes + (-6.2e-6 * t + 0.093104) * t**2 + 67310.54841 + ) + sidereal_time += 8640184.812866 * t_lo + sidereal_time += 3155760000.0 * t_lo + sidereal_time += 8640184.812866 * t_hi + sidereal_time += 3155760000.0 * t_hi + + return sidereal_time * 2 * np.pi / SECONDS_PER_DAY + + +@dispatch +def n_leap_seconds(gps_time, leap_seconds): + """ + Calculate the number of leap seconds that have occurred up to a given GPS time. + + Parameters + ---------- + gps_time : float + GPS time in seconds. + leap_seconds : array_like + GPS time of leap seconds. + + Returns + ------- + float + Number of leap seconds + """ + xp = array_module(gps_time) + return xp.sum(gps_time > leap_seconds[:, None], axis=0).squeeze() + + +@dispatch +def n_leap_seconds(gps_time: Union[np.ndarray, float, int]): + """ + Calculate the number of leap seconds that have occurred up to a given GPS time. + + Parameters + ---------- + gps_time : float + GPS time in seconds. + + Returns + ------- + float + Number of leap seconds + """ + xp = array_module(gps_time) + return n_leap_seconds(gps_time, xp.array(LEAP_SECONDS)) + + +@dispatch +def utc_to_julian_day(utc_time): + """ + Convert UTC time to Julian day. + + Parameters + ---------- + utc_time : datetime + UTC time. + + Returns + ------- + float + Julian day. + + """ + return utc_time.julian_day + + +@dispatch(precedence=1) +def gps_time_to_utc(gps_time: Real): + return _time.gps_time_to_utc(gps_time) + + +@dispatch(precedence=1) +def greenwich_mean_sidereal_time(gps_time: Real): + return _time.greenwich_mean_sidereal_time(gps_time) + + +@dispatch(precedence=1) +def greenwich_mean_sidereal_time(gps_time: ArrayLike): + return _time.greenwich_mean_sidereal_time_vectorized(gps_time) + + +@dispatch(precedence=1) +def greenwich_sidereal_time(gps_time: Real, equation_of_equinoxes: Real): + return _time.greenwich_sidereal_time(gps_time, equation_of_equinoxes) + + +@dispatch(precedence=1) +def n_leap_seconds(gps_time: Real): + return _time.n_leap_seconds(gps_time) + + +@dispatch(precedence=1) +def utc_to_julian_day(utc_time: Real): + return _time.utc_to_julian_day(utc_time) + diff --git a/bilby/gw/utils.py b/bilby/gw/utils.py index 7ad4ab40d..80dc48c53 100644 --- a/bilby/gw/utils.py +++ b/bilby/gw/utils.py @@ -5,11 +5,11 @@ import numpy as np from scipy.interpolate import interp1d from scipy.special import i0e -from bilback.geometry import zenith_azimuth_to_theta_phi -from bilback.time import greenwich_mean_sidereal_time -from bilback.utils import array_module from plum import dispatch +from .geometry import zenith_azimuth_to_theta_phi +from .time import greenwich_mean_sidereal_time +from ...compat.utils import array_module from ..core.utils import (logger, run_commandline, check_directory_exists_and_if_not_mkdir, SamplesSummary, theta_phi_to_ra_dec) From 9bec666cf980bbbc830e77c19a5a305fcf626649 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Thu, 14 Nov 2024 08:29:15 -0800 Subject: [PATCH 006/140] FEAT: make core prior backend agnostic --- bilby/compat/utils.py | 14 +- bilby/core/prior/analytical.py | 338 +++++++++++++++++++-------------- bilby/core/prior/dict.py | 16 +- 3 files changed, 214 insertions(+), 154 deletions(-) diff --git a/bilby/compat/utils.py b/bilby/compat/utils.py index 2943c1ae6..bd97f828e 100644 --- a/bilby/compat/utils.py +++ b/bilby/compat/utils.py @@ -1,5 +1,5 @@ import numpy as np -from array_api_compat import array_namespace +from scipy._lib._array_api import array_namespace __all__ = ["array_module", "promote_to_array"] @@ -20,3 +20,15 @@ def promote_to_array(args, backend, skip=None): args = tuple(backend.array(arg) for arg in args[:skip]) + args[skip:] return args + +def xp_wrap(func): + + def wrapped(self, *args, **kwargs): + if "xp" not in kwargs: + try: + kwargs["xp"] = array_module(*args) + except TypeError: + pass + return func(self, *args, **kwargs) + + return wrapped diff --git a/bilby/core/prior/analytical.py b/bilby/core/prior/analytical.py index bc47cf680..974d5cbe4 100644 --- a/bilby/core/prior/analytical.py +++ b/bilby/core/prior/analytical.py @@ -1,21 +1,24 @@ +import os + import numpy as np +os.environ["SCIPY_ARRAY_API"] = "1" # noqa # flag for scipy backend switching from scipy.special import ( - xlogy, - erf, - erfinv, - log1p, - stdtrit, - gammaln, - stdtr, - betaln, betainc, betaincinv, + betaln, + erf, + # erfinv, # erfinv is not currently backend agnostic gammaincinv, gammainc, + gammaln, + stdtr, + stdtrit, + xlogy, ) from .base import Prior from ..utils import logger +from ...compat.utils import xp_wrap class DeltaFunction(Prior): @@ -67,10 +70,10 @@ def prob(self, val): """ at_peak = (val == self.peak) - return np.nan_to_num(np.multiply(at_peak, np.inf)) + return at_peak * 1.0 def cdf(self, val): - return np.ones_like(val) * (val > self.peak) + return 1.0 * (val > self.peak) class PowerLaw(Prior): @@ -101,7 +104,8 @@ def __init__(self, alpha, minimum, maximum, name=None, latex_label=None, boundary=boundary) self.alpha = alpha - def rescale(self, val): + @xp_wrap + def rescale(self, val, *, xp=np): """ 'Rescale' a sample from the unit line element to the power-law prior. @@ -117,12 +121,13 @@ def rescale(self, val): Union[float, array_like]: Rescaled probability """ if self.alpha == -1: - return self.minimum * np.exp(val * np.log(self.maximum / self.minimum)) + return self.minimum * xp.exp(val * xp.log(self.maximum / self.minimum)) else: return (self.minimum ** (1 + self.alpha) + val * (self.maximum ** (1 + self.alpha) - self.minimum ** (1 + self.alpha))) ** (1. / (1 + self.alpha)) - def prob(self, val): + @xp_wrap + def prob(self, val, *, xp=np): """Return the prior probability of val Parameters @@ -134,13 +139,14 @@ def prob(self, val): float: Prior probability of val """ if self.alpha == -1: - return np.nan_to_num(1 / val / np.log(self.maximum / self.minimum)) * self.is_in_prior_range(val) + return xp.nan_to_num(1 / val / xp.log(self.maximum / self.minimum)) * self.is_in_prior_range(val) else: - return np.nan_to_num(val ** self.alpha * (1 + self.alpha) / + return xp.nan_to_num(val ** self.alpha * (1 + self.alpha) / (self.maximum ** (1 + self.alpha) - self.minimum ** (1 + self.alpha))) * self.is_in_prior_range(val) - def ln_prob(self, val): + @xp_wrap + def ln_prob(self, val, *, xp=np): """Return the logarithmic prior probability of val Parameters @@ -153,28 +159,27 @@ def ln_prob(self, val): """ if self.alpha == -1: - normalising = 1. / np.log(self.maximum / self.minimum) + normalising = 1. / xp.log(self.maximum / self.minimum) else: normalising = (1 + self.alpha) / (self.maximum ** (1 + self.alpha) - self.minimum ** (1 + self.alpha)) with np.errstate(divide='ignore', invalid='ignore'): - ln_in_range = np.log(1. * self.is_in_prior_range(val)) - ln_p = self.alpha * np.nan_to_num(np.log(val)) + np.log(normalising) + ln_in_range = xp.log(1. * self.is_in_prior_range(val)) + ln_p = self.alpha * xp.nan_to_num(xp.log(val)) + xp.log(normalising) return ln_p + ln_in_range - def cdf(self, val): + @xp_wrap + def cdf(self, val, *, xp=np): if self.alpha == -1: - _cdf = (np.log(val / self.minimum) / - np.log(self.maximum / self.minimum)) + _cdf = xp.log(val / self.minimum) / xp.log(self.maximum / self.minimum) else: _cdf = ( - (val ** (self.alpha + 1) - self.minimum ** (self.alpha + 1)) + val ** (self.alpha + 1) - self.minimum ** (self.alpha + 1) / (self.maximum ** (self.alpha + 1) - self.minimum ** (self.alpha + 1)) ) - _cdf = np.minimum(_cdf, 1) - _cdf = np.maximum(_cdf, 0) + _cdf = xp.clip(_cdf, 0, 1) return _cdf @@ -233,7 +238,8 @@ def prob(self, val): """ return ((val >= self.minimum) & (val <= self.maximum)) / (self.maximum - self.minimum) - def ln_prob(self, val): + @xp_wrap + def ln_prob(self, val, *, xp=np): """Return the log prior probability of val Parameters @@ -244,13 +250,13 @@ def ln_prob(self, val): ======= float: log probability of val """ - return xlogy(1, (val >= self.minimum) & (val <= self.maximum)) - xlogy(1, self.maximum - self.minimum) + with np.errstate(divide="ignore"): + return xp.log(self.prob(val)) - def cdf(self, val): + @xp_wrap + def cdf(self, val, *, xp=np): _cdf = (val - self.minimum) / (self.maximum - self.minimum) - _cdf = np.minimum(_cdf, 1) - _cdf = np.maximum(_cdf, 0) - return _cdf + return xp.clip(_cdf, 0, 1) class LogUniform(PowerLaw): @@ -310,7 +316,8 @@ def __init__(self, minimum, maximum, name=None, latex_label=None, minimum=minimum, maximum=maximum, unit=unit, boundary=boundary) - def rescale(self, val): + @xp_wrap + def rescale(self, val, *, xp=np): """ 'Rescale' a sample from the unit line element to the power-law prior. @@ -327,19 +334,20 @@ def rescale(self, val): """ if isinstance(val, (float, int)): if val < 0.5: - return -self.maximum * np.exp(-2 * val * np.log(self.maximum / self.minimum)) + return -self.maximum * xp.exp(-2 * val * xp.log(self.maximum / self.minimum)) else: - return self.minimum * np.exp(np.log(self.maximum / self.minimum) * (2 * val - 1)) + return self.minimum * xp.exp(xp.log(self.maximum / self.minimum) * (2 * val - 1)) else: vals_less_than_5 = val < 0.5 - rescaled = np.empty_like(val) - rescaled[vals_less_than_5] = -self.maximum * np.exp(-2 * val[vals_less_than_5] * - np.log(self.maximum / self.minimum)) - rescaled[~vals_less_than_5] = self.minimum * np.exp(np.log(self.maximum / self.minimum) * + rescaled = xp.empty_like(val) + rescaled[vals_less_than_5] = -self.maximum * xp.exp(-2 * val[vals_less_than_5] * + xp.log(self.maximum / self.minimum)) + rescaled[~vals_less_than_5] = self.minimum * xp.exp(xp.log(self.maximum / self.minimum) * (2 * val[~vals_less_than_5] - 1)) return rescaled - def prob(self, val): + @xp_wrap + def prob(self, val, *, xp=np): """Return the prior probability of val Parameters @@ -350,11 +358,12 @@ def prob(self, val): ======= float: Prior probability of val """ - val = np.abs(val) - return (np.nan_to_num(0.5 / val / np.log(self.maximum / self.minimum)) * + val = xp.abs(val) + return (xp.nan_to_num(0.5 / val / xp.log(self.maximum / self.minimum)) * self.is_in_prior_range(val)) - def ln_prob(self, val): + @xp_wrap + def ln_prob(self, val, *, xp=np): """Return the logarithmic prior probability of val Parameters @@ -366,19 +375,12 @@ def ln_prob(self, val): float: """ - return np.nan_to_num(- np.log(2 * np.abs(val)) - np.log(np.log(self.maximum / self.minimum))) + return np.nan_to_num(- xp.log(2 * xp.abs(val)) - xp.log(xp.log(self.maximum / self.minimum))) - def cdf(self, val): - norm = 0.5 / np.log(self.maximum / self.minimum) - _cdf = ( - -norm * np.log(abs(val) / self.maximum) - * (val <= -self.minimum) * (val >= -self.maximum) - + (0.5 + norm * np.log(abs(val) / self.minimum)) - * (val >= self.minimum) * (val <= self.maximum) - + 0.5 * (val > -self.minimum) * (val < self.minimum) - + 1 * (val > self.maximum) - ) - return _cdf + @xp_wrap + def cdf(self, val, *, xp=np): + asymmetric = LogUniform.cdf(self, abs(val), xp) + return 0.5 * (1 + xp.sign(val) * asymmetric) class Cosine(Prior): @@ -405,16 +407,18 @@ def __init__(self, minimum=-np.pi / 2, maximum=np.pi / 2, name=None, super(Cosine, self).__init__(minimum=minimum, maximum=maximum, name=name, latex_label=latex_label, unit=unit, boundary=boundary) - def rescale(self, val): + @xp_wrap + def rescale(self, val, *, xp=np): """ 'Rescale' a sample from the unit line element to a uniform in cosine prior. This maps to the inverse CDF. This has been analytically solved for this case. """ - norm = 1 / (np.sin(self.maximum) - np.sin(self.minimum)) - return np.arcsin(val / norm + np.sin(self.minimum)) + norm = 1 / (xp.sin(self.maximum) - xp.sin(self.minimum)) + return xp.arcsin(val / norm + xp.sin(self.minimum)) - def prob(self, val): + @xp_wrap + def prob(self, val, *, xp=np): """Return the prior probability of val. Defined over [-pi/2, pi/2]. Parameters @@ -425,15 +429,17 @@ def prob(self, val): ======= float: Prior probability of val """ - return np.cos(val) / 2 * self.is_in_prior_range(val) + return xp.cos(val) / 2 * self.is_in_prior_range(val) - def cdf(self, val): + @xp_wrap + def cdf(self, val, *, xp=np): _cdf = ( - (np.sin(val) - np.sin(self.minimum)) - / (np.sin(self.maximum) - np.sin(self.minimum)) - * (val >= self.minimum) * (val <= self.maximum) - + 1 * (val > self.maximum) + (xp.sin(val) - xp.sin(self.minimum)) / + (xp.sin(self.maximum) - xp.sin(self.minimum)) ) + _cdf *= val >= self.minimum + _cdf *= val <= self.maximum + _cdf += val > self.maximum return _cdf @@ -461,16 +467,18 @@ def __init__(self, minimum=0, maximum=np.pi, name=None, super(Sine, self).__init__(minimum=minimum, maximum=maximum, name=name, latex_label=latex_label, unit=unit, boundary=boundary) - def rescale(self, val): + @xp_wrap + def rescale(self, val, *, xp=np): """ 'Rescale' a sample from the unit line element to a uniform in sine prior. This maps to the inverse CDF. This has been analytically solved for this case. """ - norm = 1 / (np.cos(self.minimum) - np.cos(self.maximum)) - return np.arccos(np.cos(self.minimum) - val / norm) + norm = 1 / (xp.cos(self.minimum) - xp.cos(self.maximum)) + return xp.arccos(xp.cos(self.minimum) - val / norm) - def prob(self, val): + @xp_wrap + def prob(self, val, *, xp=np): """Return the prior probability of val. Defined over [0, pi]. Parameters @@ -481,15 +489,17 @@ def prob(self, val): ======= Union[float, array_like]: Prior probability of val """ - return np.sin(val) / 2 * self.is_in_prior_range(val) + return xp.sin(val) / 2 * self.is_in_prior_range(val) - def cdf(self, val): + @xp_wrap + def cdf(self, val, *, xp=np): _cdf = ( - (np.cos(val) - np.cos(self.minimum)) - / (np.cos(self.maximum) - np.cos(self.minimum)) - * (val >= self.minimum) * (val <= self.maximum) - + 1 * (val > self.maximum) + (xp.cos(val) - xp.cos(self.minimum)) + / (xp.cos(self.maximum) - xp.cos(self.minimum)) ) + _cdf *= val >= self.minimum + _cdf *= val <= self.maximum + _cdf += val > self.maximum return _cdf @@ -517,7 +527,8 @@ def __init__(self, mu, sigma, name=None, latex_label=None, unit=None, boundary=N self.mu = mu self.sigma = sigma - def rescale(self, val): + @xp_wrap + def rescale(self, val, *, xp=np): """ 'Rescale' a sample from the unit line element to the appropriate Gaussian prior. @@ -527,9 +538,14 @@ def rescale(self, val): This maps to the inverse CDF. This has been analytically solved for this case. """ + if "jax" in xp.__name__: + from jax.scipy.special import erfinv + else: + from scipy.special import erfinv return self.mu + erfinv(2 * val - 1) * 2 ** 0.5 * self.sigma - def prob(self, val): + @xp_wrap + def prob(self, val, *, xp=np): """Return the prior probability of val. Parameters @@ -540,9 +556,10 @@ def prob(self, val): ======= Union[float, array_like]: Prior probability of val """ - return np.exp(-(self.mu - val) ** 2 / (2 * self.sigma ** 2)) / (2 * np.pi) ** 0.5 / self.sigma + return xp.exp(-(self.mu - val) ** 2 / (2 * self.sigma ** 2)) / (2 * np.pi) ** 0.5 / self.sigma - def ln_prob(self, val): + @xp_wrap + def ln_prob(self, val, *, xp=np): """Return the Log prior probability of val. Parameters @@ -553,8 +570,7 @@ def ln_prob(self, val): ======= Union[float, array_like]: Prior probability of val """ - - return -0.5 * ((self.mu - val) ** 2 / self.sigma ** 2 + np.log(2 * np.pi * self.sigma ** 2)) + return -0.5 * ((self.mu - val) ** 2 / self.sigma ** 2 + xp.log(2 * np.pi * self.sigma ** 2)) def cdf(self, val): return (1 - erf((self.mu - val) / 2 ** 0.5 / self.sigma)) / 2 @@ -607,16 +623,22 @@ def normalisation(self): return (erf((self.maximum - self.mu) / 2 ** 0.5 / self.sigma) - erf( (self.minimum - self.mu) / 2 ** 0.5 / self.sigma)) / 2 - def rescale(self, val): + @xp_wrap + def rescale(self, val, *, xp=np): """ 'Rescale' a sample from the unit line element to the appropriate truncated Gaussian prior. This maps to the inverse CDF. This has been analytically solved for this case. """ + if "jax" in xp.__name__: + from jax.scipy.special import erfinv + else: + from scipy.special import erfinv return erfinv(2 * val * self.normalisation + erf( (self.minimum - self.mu) / 2 ** 0.5 / self.sigma)) * 2 ** 0.5 * self.sigma + self.mu - def prob(self, val): + @xp_wrap + def prob(self, val, *, xp=np): """Return the prior probability of val. Parameters @@ -627,17 +649,15 @@ def prob(self, val): ======= float: Prior probability of val """ - return np.exp(-(self.mu - val) ** 2 / (2 * self.sigma ** 2)) / (2 * np.pi) ** 0.5 \ + return xp.exp(-(self.mu - val) ** 2 / (2 * self.sigma ** 2)) / (2 * np.pi) ** 0.5 \ / self.sigma / self.normalisation * self.is_in_prior_range(val) def cdf(self, val): - _cdf = ( - ( - erf((val - self.mu) / 2 ** 0.5 / self.sigma) - - erf((self.minimum - self.mu) / 2 ** 0.5 / self.sigma) - ) / 2 / self.normalisation * (val >= self.minimum) * (val <= self.maximum) - + 1 * (val > self.maximum) - ) + _cdf = (erf((val - self.mu) / 2 ** 0.5 / self.sigma) - erf( + (self.minimum - self.mu) / 2 ** 0.5 / self.sigma)) / 2 / self.normalisation + _cdf *= val >= self.minimum + _cdf *= val <= self.maximum + _cdf += val > self.maximum return _cdf @@ -701,15 +721,21 @@ def __init__(self, mu, sigma, name=None, latex_label=None, unit=None, boundary=N self.mu = mu self.sigma = sigma - def rescale(self, val): + @xp_wrap + def rescale(self, val, *, xp=np): """ 'Rescale' a sample from the unit line element to the appropriate LogNormal prior. This maps to the inverse CDF. This has been analytically solved for this case. """ - return np.exp(self.mu + np.sqrt(2 * self.sigma ** 2) * erfinv(2 * val - 1)) + if "jax" in xp.__name__: + from jax.scipy.special import erfinv + else: + from scipy.special import erfinv + return xp.exp(self.mu + (2 * self.sigma ** 2)**0.5 * erfinv(2 * val - 1)) - def prob(self, val): + @xp_wrap + def prob(self, val, *, xp=np): """Returns the prior probability of val. Parameters @@ -724,16 +750,17 @@ def prob(self, val): if val <= self.minimum: _prob = 0. else: - _prob = np.exp(-(np.log(val) - self.mu) ** 2 / self.sigma ** 2 / 2)\ - / np.sqrt(2 * np.pi) / val / self.sigma + _prob = xp.exp(-(xp.log(val) - self.mu) ** 2 / self.sigma ** 2 / 2)\ + / xp.sqrt(2 * np.pi) / val / self.sigma else: - _prob = np.zeros(val.size) + _prob = xp.zeros(val.size) idx = (val > self.minimum) - _prob[idx] = np.exp(-(np.log(val[idx]) - self.mu) ** 2 / self.sigma ** 2 / 2)\ - / np.sqrt(2 * np.pi) / val[idx] / self.sigma + _prob[idx] = xp.exp(-(xp.log(val[idx]) - self.mu) ** 2 / self.sigma ** 2 / 2)\ + / xp.sqrt(2 * np.pi) / val[idx] / self.sigma return _prob - def ln_prob(self, val): + @xp_wrap + def ln_prob(self, val, *, xp=np): """Returns the log prior probability of val. Parameters @@ -746,27 +773,28 @@ def ln_prob(self, val): """ if isinstance(val, (float, int)): if val <= self.minimum: - _ln_prob = -np.inf + _ln_prob = -xp.inf else: - _ln_prob = -(np.log(val) - self.mu) ** 2 / self.sigma ** 2 / 2\ - - np.log(np.sqrt(2 * np.pi) * val * self.sigma) + _ln_prob = -(xp.log(val) - self.mu) ** 2 / self.sigma ** 2 / 2\ + - xp.log(xp.sqrt(2 * np.pi) * val * self.sigma) else: - _ln_prob = -np.inf * np.ones(val.size) + _ln_prob = -xp.inf * xp.ones(val.size) idx = (val > self.minimum) - _ln_prob[idx] = -(np.log(val[idx]) - self.mu) ** 2\ - / self.sigma ** 2 / 2 - np.log(np.sqrt(2 * np.pi) * val[idx] * self.sigma) + _ln_prob[idx] = -(xp.log(val[idx]) - self.mu) ** 2\ + / self.sigma ** 2 / 2 - xp.log(xp.sqrt(2 * np.pi) * val[idx] * self.sigma) return _ln_prob - def cdf(self, val): + @xp_wrap + def cdf(self, val, *, xp=np): if isinstance(val, (float, int)): if val <= self.minimum: _cdf = 0. else: _cdf = 0.5 + erf((np.log(val) - self.mu) / self.sigma / np.sqrt(2)) / 2 else: - _cdf = np.zeros(val.size) + _cdf = xp.zeros(val.size) _cdf[val > self.minimum] = 0.5 + erf(( - np.log(val[val > self.minimum]) - self.mu) / self.sigma / np.sqrt(2)) / 2 + xp.log(val[val > self.minimum]) - self.mu) / self.sigma / np.sqrt(2)) / 2 return _cdf @@ -795,15 +823,17 @@ def __init__(self, mu, name=None, latex_label=None, unit=None, boundary=None): unit=unit, boundary=boundary) self.mu = mu - def rescale(self, val): + @xp_wrap + def rescale(self, val, *, xp=np): """ 'Rescale' a sample from the unit line element to the appropriate Exponential prior. This maps to the inverse CDF. This has been analytically solved for this case. """ - return -self.mu * log1p(-val) + return -self.mu * xp.log1p(-val) - def prob(self, val): + @xp_wrap + def prob(self, val, *, xp=np): """Return the prior probability of val. Parameters @@ -818,13 +848,14 @@ def prob(self, val): if val < self.minimum: _prob = 0. else: - _prob = np.exp(-val / self.mu) / self.mu + _prob = xp.exp(-val / self.mu) / self.mu else: - _prob = np.zeros(val.size) - _prob[val >= self.minimum] = np.exp(-val[val >= self.minimum] / self.mu) / self.mu + _prob = xp.zeros(val.size) + _prob[val >= self.minimum] = xp.exp(-val[val >= self.minimum] / self.mu) / self.mu return _prob - def ln_prob(self, val): + @xp_wrap + def ln_prob(self, val, *, xp=np): """Returns the log prior probability of val. Parameters @@ -837,23 +868,24 @@ def ln_prob(self, val): """ if isinstance(val, (float, int)): if val < self.minimum: - _ln_prob = -np.inf + _ln_prob = -xp.inf else: - _ln_prob = -val / self.mu - np.log(self.mu) + _ln_prob = -val / self.mu - xp.log(self.mu) else: - _ln_prob = -np.inf * np.ones(val.size) - _ln_prob[val >= self.minimum] = -val[val >= self.minimum] / self.mu - np.log(self.mu) + _ln_prob = -xp.inf * xp.ones(val.size) + _ln_prob[val >= self.minimum] = -val[val >= self.minimum] / self.mu - xp.log(self.mu) return _ln_prob - def cdf(self, val): + @xp_wrap + def cdf(self, val, *, xp=np): if isinstance(val, (float, int)): if val < self.minimum: _cdf = 0. else: - _cdf = 1. - np.exp(-val / self.mu) + _cdf = 1. - xp.exp(-val / self.mu) else: - _cdf = np.zeros(val.size) - _cdf[val >= self.minimum] = 1. - np.exp(-val[val >= self.minimum] / self.mu) + _cdf = xp.zeros(val.size) + _cdf[val >= self.minimum] = 1. - xp.exp(-val[val >= self.minimum] / self.mu) return _cdf @@ -891,7 +923,8 @@ def __init__(self, df, mu=0., scale=1., name=None, latex_label=None, self.mu = mu self.scale = scale - def rescale(self, val): + @xp_wrap + def rescale(self, val, *, xp=np): """ 'Rescale' a sample from the unit line element to the appropriate Student's t-prior. @@ -906,11 +939,12 @@ def rescale(self, val): rescaled = stdtrit(self.df, val) * self.scale + self.mu else: rescaled = stdtrit(self.df, val) * self.scale + self.mu - rescaled[val == 0] = -np.inf - rescaled[val == 1] = np.inf + rescaled[val == 0] = -xp.inf + rescaled[val == 1] = xp.inf return rescaled - def prob(self, val): + @xp_wrap + def prob(self, val, *, xp=np): """Return the prior probability of val. Parameters @@ -921,9 +955,10 @@ def prob(self, val): ======= Union[float, array_like]: Prior probability of val """ - return np.exp(self.ln_prob(val)) + return xp.exp(self.ln_prob(val)) - def ln_prob(self, val): + @xp_wrap + def ln_prob(self, val, *, xp=np): """Returns the log prior probability of val. Parameters @@ -935,8 +970,8 @@ def ln_prob(self, val): Union[float, array_like]: Prior probability of val """ return gammaln(0.5 * (self.df + 1)) - gammaln(0.5 * self.df)\ - - np.log(np.sqrt(np.pi * self.df) * self.scale) - (self.df + 1) / 2 *\ - np.log(1 + ((val - self.mu) / self.scale) ** 2 / self.df) + - xp.log((np.pi * self.df)**0.5 * self.scale) - (self.df + 1) / 2 *\ + xp.log(1 + ((val - self.mu) / self.scale) ** 2 / self.df) def cdf(self, val): return stdtr(self.df, (val - self.mu) / self.scale) @@ -988,7 +1023,8 @@ def rescale(self, val): """ return betaincinv(self.alpha, self.beta, val) * (self.maximum - self.minimum) + self.minimum - def prob(self, val): + @xp_wrap + def prob(self, val, *, xp=np): """Return the prior probability of val. Parameters @@ -999,9 +1035,10 @@ def prob(self, val): ======= Union[float, array_like]: Prior probability of val """ - return np.exp(self.ln_prob(val)) + return xp.exp(self.ln_prob(val)) - def ln_prob(self, val): + @xp_wrap + def ln_prob(self, val, *, xp=np): """Returns the log prior probability of val. Parameters @@ -1012,21 +1049,26 @@ def ln_prob(self, val): ======= Union[float, array_like]: Prior probability of val """ - _ln_prob = xlogy(self.alpha - 1, val - self.minimum) + xlogy(self.beta - 1, self.maximum - val)\ - - betaln(self.alpha, self.beta) - xlogy(self.alpha + self.beta - 1, self.maximum - self.minimum) + _ln_prob = ( + xlogy(self.alpha - 1, val - self.minimum) + + xlogy(self.beta - 1, self.maximum - val) + - betaln(self.alpha, self.beta) + - xlogy(self.alpha + self.beta - 1, self.maximum - self.minimum) + ) # deal with the fact that if alpha or beta are < 1 you get infinities at 0 and 1 if isinstance(val, (float, int)): - if np.isfinite(_ln_prob) and self.minimum <= val <= self.maximum: + if xp.isfinite(_ln_prob) and self.minimum <= val <= self.maximum: return _ln_prob - return -np.inf + return -xp.inf else: - _ln_prob_sub = np.full_like(val, -np.inf) - idx = np.isfinite(_ln_prob) & (val >= self.minimum) & (val <= self.maximum) + _ln_prob_sub = xp.full_like(val, -xp.inf) + idx = xp.isfinite(_ln_prob) & (val >= self.minimum) & (val <= self.maximum) _ln_prob_sub[idx] = _ln_prob[idx] return _ln_prob_sub - def cdf(self, val): + @xp_wrap + def cdf(self, val, *, xp=np): if isinstance(val, (float, int)): if val > self.maximum: return 1. @@ -1040,8 +1082,9 @@ def cdf(self, val): else: _cdf = np.nan_to_num(betainc(self.alpha, self.beta, (val - self.minimum) / (self.maximum - self.minimum))) - _cdf[val < self.minimum] = 0. - _cdf[val > self.maximum] = 1. + _cdf *= val >= self.minimum + _cdf *= val <= self.maximum + _cdf += val > self.maximum return _cdf @@ -1397,7 +1440,8 @@ def rescale(self, val): inv = -1 / self.expr + (1 + self.expr)**-val + (1 + self.expr)**-val / self.expr return -self.sigma * np.log(np.maximum(inv, 0)) - def prob(self, val): + @xp_wrap + def prob(self, val, *, xp=np): """Return the prior probability of val. Parameters @@ -1409,8 +1453,8 @@ def prob(self, val): float: Prior probability of val """ return ( - (np.exp((val - self.mu) / self.sigma) + 1)**-1 - / (self.sigma * np.log1p(self.expr)) + (xp.exp((val - self.mu) / self.sigma) + 1)**-1 + / (self.sigma * xp.log1p(self.expr)) * (val >= self.minimum) ) @@ -1789,3 +1833,5 @@ def cdf(self, val): / (self.mode - self.rescaled_minimum) ) ) + + betaln, diff --git a/bilby/core/prior/dict.py b/bilby/core/prior/dict.py index 3ac54622e..da175b117 100644 --- a/bilby/core/prior/dict.py +++ b/bilby/core/prior/dict.py @@ -6,6 +6,7 @@ from warnings import warn import numpy as np +from scipy._lib._array_api import array_namespace from .analytical import DeltaFunction from .base import Prior, Constraint @@ -635,9 +636,9 @@ def rescale(self, keys, theta): ======= list: List of floats containing the rescaled sample """ - return list( - [self[key].rescale(sample) for key, sample in zip(keys, theta)] - ) + xp = array_namespace(theta) + + return xp.asarray([self[key].rescale(sample) for key, sample in zip(keys, theta)]) def test_redundancy(self, key, disable_logging=False): """Empty redundancy test, should be overwritten in subclasses""" @@ -862,8 +863,9 @@ def rescale(self, keys, theta): ======= list: List of floats containing the rescaled sample """ + xp = array_namespace(theta) + keys = list(keys) - theta = list(theta) self._check_resolved() self._update_rescale_keys(keys) result = dict() @@ -886,9 +888,9 @@ def rescale(self, keys, theta): # {a: [], b: [], c: [1, 2, 3, 4], d: []} # -> [1, 2, 3, 4] # -> {a: 1, b: 2, c: 3, d: 4} - values = list() + values = xp.array([]) for key in names: - values = np.concatenate([values, result[key]]) + values = xp.concatenate([values, result[key]]) for key, value in zip(names, values): result[key] = value @@ -903,7 +905,7 @@ def safe_flatten(value): else: return result[key].flatten() - return [safe_flatten(result[key]) for key in keys] + return xp.array([safe_flatten(result[key]) for key in keys]) def _update_rescale_keys(self, keys): if not keys == self._least_recently_rescaled_keys: From 0d9aba62290ce0e5327ea212f3b21884dbabee80 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Thu, 14 Nov 2024 08:29:53 -0800 Subject: [PATCH 007/140] FEAT: make non-numpy arrays serializable --- bilby/core/utils/io.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/bilby/core/utils/io.py b/bilby/core/utils/io.py index 8299d6816..a5502a1a6 100644 --- a/bilby/core/utils/io.py +++ b/bilby/core/utils/io.py @@ -59,8 +59,12 @@ def default(self, obj): return encode_astropy_unit(obj) except ImportError: logger.debug("Cannot import astropy, cannot write cosmological priors") - if isinstance(obj, np.ndarray): - return {"__array__": True, "content": obj.tolist()} + if hasattr(obj, "__array_namespace__"): + return { + "__array__": True, + "__array_namespace__": obj.__array_namespace__().__name__, + "content": obj.tolist(), + } if isinstance(obj, complex): return {"__complex__": True, "real": obj.real, "imag": obj.imag} if isinstance(obj, pd.DataFrame): @@ -320,7 +324,9 @@ def decode_bilby_json(dct): if dct.get("__astropy_unit__", False): return decode_astropy_unit(dct) if dct.get("__array__", False): - return np.asarray(dct["content"]) + namespace = dct.get("__array_namespace__", "numpy") + xp = import_module(namespace) + return xp.asarray(dct["content"]) if dct.get("__complex__", False): return complex(dct["real"], dct["imag"]) if dct.get("__dataframe__", False): @@ -438,6 +444,10 @@ def encode_for_hdf5(key, item): if item.dtype.kind == 'U': logger.debug(f'converting dtype {item.dtype} for hdf5') item = np.array(item, dtype='S') + elif hasattr(item, "__array_namespace__"): + # temporarily dump all arrays as numpy arrays, we should figure ou + # how to properly deserialize them + item = np.asarray(item) if isinstance(item, (np.ndarray, int, float, complex, str, bytes)): output = item elif isinstance(item, np.random.Generator): From aea0ea88ed737ca039c407e8b4693da715ef5e84 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Thu, 14 Nov 2024 08:30:18 -0800 Subject: [PATCH 008/140] BUG: fix some array conversion methods --- bilby/gw/conversion.py | 4 ++-- bilby/gw/utils.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bilby/gw/conversion.py b/bilby/gw/conversion.py index c369fee04..cedd67b18 100644 --- a/bilby/gw/conversion.py +++ b/bilby/gw/conversion.py @@ -9,7 +9,6 @@ import pickle import numpy as np -from bilback.utils import array_module from pandas import DataFrame, Series from scipy.stats import norm @@ -27,6 +26,7 @@ lalsim_SimNeutronStarRadius, lalsim_SimNeutronStarLoveNumberK2) +from ..compat.utils import array_module from ..core.likelihood import MarginalizedLikelihoodReconstructionError from ..core.utils import logger, solar_mass, gravitational_constant, speed_of_light, command_line_args, safe_file_dump from ..core.prior import DeltaFunction @@ -241,7 +241,7 @@ def convert_to_lal_binary_black_hole_parameters(parameters): """ converted_parameters = parameters.copy() original_keys = list(converted_parameters.keys()) - xp = array_module(parameters[original_keys[0]]) + xp = array_module(parameters[original_keys[5]]) if 'luminosity_distance' not in original_keys: if 'redshift' in converted_parameters.keys(): converted_parameters['luminosity_distance'] = \ diff --git a/bilby/gw/utils.py b/bilby/gw/utils.py index 80dc48c53..cf98cfc38 100644 --- a/bilby/gw/utils.py +++ b/bilby/gw/utils.py @@ -9,7 +9,7 @@ from .geometry import zenith_azimuth_to_theta_phi from .time import greenwich_mean_sidereal_time -from ...compat.utils import array_module +from ..compat.utils import array_module from ..core.utils import (logger, run_commandline, check_directory_exists_and_if_not_mkdir, SamplesSummary, theta_phi_to_ra_dec) From 8e61b7f103f37f04417e21b33be510461bcd919d Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Wed, 11 Dec 2024 23:13:20 +0000 Subject: [PATCH 009/140] DEV: some more prior agnosticism --- bilby/core/prior/analytical.py | 151 ++++++------------------------- bilby/core/prior/interpolated.py | 4 +- bilby/core/prior/joint.py | 20 ++-- test/core/prior/prior_test.py | 23 +++++ 4 files changed, 67 insertions(+), 131 deletions(-) diff --git a/bilby/core/prior/analytical.py b/bilby/core/prior/analytical.py index 974d5cbe4..713c27fb3 100644 --- a/bilby/core/prior/analytical.py +++ b/bilby/core/prior/analytical.py @@ -7,7 +7,6 @@ betaincinv, betaln, erf, - # erfinv, # erfinv is not currently backend agnostic gammaincinv, gammainc, gammaln, @@ -176,7 +175,7 @@ def cdf(self, val, *, xp=np): _cdf = xp.log(val / self.minimum) / xp.log(self.maximum / self.minimum) else: _cdf = ( - val ** (self.alpha + 1) - self.minimum ** (self.alpha + 1) + (val ** (self.alpha + 1) - self.minimum ** (self.alpha + 1)) / (self.maximum ** (self.alpha + 1) - self.minimum ** (self.alpha + 1)) ) _cdf = xp.clip(_cdf, 0, 1) @@ -332,19 +331,7 @@ def rescale(self, val, *, xp=np): ======= Union[float, array_like]: Rescaled probability """ - if isinstance(val, (float, int)): - if val < 0.5: - return -self.maximum * xp.exp(-2 * val * xp.log(self.maximum / self.minimum)) - else: - return self.minimum * xp.exp(xp.log(self.maximum / self.minimum) * (2 * val - 1)) - else: - vals_less_than_5 = val < 0.5 - rescaled = xp.empty_like(val) - rescaled[vals_less_than_5] = -self.maximum * xp.exp(-2 * val[vals_less_than_5] * - xp.log(self.maximum / self.minimum)) - rescaled[~vals_less_than_5] = self.minimum * xp.exp(xp.log(self.maximum / self.minimum) * - (2 * val[~vals_less_than_5] - 1)) - return rescaled + return xp.sign(2 * val - 1) * self.minimum * xp.exp(abs(2 * val - 1) * xp.log(self.maximum / self.minimum)) @xp_wrap def prob(self, val, *, xp=np): @@ -379,7 +366,7 @@ def ln_prob(self, val, *, xp=np): @xp_wrap def cdf(self, val, *, xp=np): - asymmetric = LogUniform.cdf(self, abs(val), xp) + asymmetric = xp.log(abs(val) / self.minimum) / xp.log(self.maximum / self.minimum) return 0.5 * (1 + xp.sign(val) * asymmetric) @@ -746,18 +733,10 @@ def prob(self, val, *, xp=np): ======= Union[float, array_like]: Prior probability of val """ - if isinstance(val, (float, int)): - if val <= self.minimum: - _prob = 0. - else: - _prob = xp.exp(-(xp.log(val) - self.mu) ** 2 / self.sigma ** 2 / 2)\ - / xp.sqrt(2 * np.pi) / val / self.sigma - else: - _prob = xp.zeros(val.size) - idx = (val > self.minimum) - _prob[idx] = xp.exp(-(xp.log(val[idx]) - self.mu) ** 2 / self.sigma ** 2 / 2)\ - / xp.sqrt(2 * np.pi) / val[idx] / self.sigma - return _prob + return ( + xp.exp(-(xp.log(xp.maximum(val, self.minimum)) - self.mu) ** 2 / self.sigma ** 2 / 2) + / xp.sqrt(2 * np.pi) / val / self.sigma + ) * (val > self.minimum) @xp_wrap def ln_prob(self, val, *, xp=np): @@ -771,31 +750,16 @@ def ln_prob(self, val, *, xp=np): ======= Union[float, array_like]: Prior probability of val """ - if isinstance(val, (float, int)): - if val <= self.minimum: - _ln_prob = -xp.inf - else: - _ln_prob = -(xp.log(val) - self.mu) ** 2 / self.sigma ** 2 / 2\ - - xp.log(xp.sqrt(2 * np.pi) * val * self.sigma) - else: - _ln_prob = -xp.inf * xp.ones(val.size) - idx = (val > self.minimum) - _ln_prob[idx] = -(xp.log(val[idx]) - self.mu) ** 2\ - / self.sigma ** 2 / 2 - xp.log(xp.sqrt(2 * np.pi) * val[idx] * self.sigma) - return _ln_prob + return ( + -(xp.log(val) - self.mu) ** 2 / self.sigma ** 2 / 2 + - xp.log(xp.sqrt(2 * np.pi) * val * self.sigma) + ) + xp.log(val > self.minimum) @xp_wrap def cdf(self, val, *, xp=np): - if isinstance(val, (float, int)): - if val <= self.minimum: - _cdf = 0. - else: - _cdf = 0.5 + erf((np.log(val) - self.mu) / self.sigma / np.sqrt(2)) / 2 - else: - _cdf = xp.zeros(val.size) - _cdf[val > self.minimum] = 0.5 + erf(( - xp.log(val[val > self.minimum]) - self.mu) / self.sigma / np.sqrt(2)) / 2 - return _cdf + return 0.5 + erf( + (xp.log(xp.maximum(val, self.minimum)) - self.mu) / self.sigma / np.sqrt(2) + ) / 2 class LogGaussian(LogNormal): @@ -844,15 +808,7 @@ def prob(self, val, *, xp=np): ======= Union[float, array_like]: Prior probability of val """ - if isinstance(val, (float, int)): - if val < self.minimum: - _prob = 0. - else: - _prob = xp.exp(-val / self.mu) / self.mu - else: - _prob = xp.zeros(val.size) - _prob[val >= self.minimum] = xp.exp(-val[val >= self.minimum] / self.mu) / self.mu - return _prob + return xp.exp(-val / self.mu) / self.mu * (val >= self.minimum) @xp_wrap def ln_prob(self, val, *, xp=np): @@ -866,27 +822,11 @@ def ln_prob(self, val, *, xp=np): ======= Union[float, array_like]: Prior probability of val """ - if isinstance(val, (float, int)): - if val < self.minimum: - _ln_prob = -xp.inf - else: - _ln_prob = -val / self.mu - xp.log(self.mu) - else: - _ln_prob = -xp.inf * xp.ones(val.size) - _ln_prob[val >= self.minimum] = -val[val >= self.minimum] / self.mu - xp.log(self.mu) - return _ln_prob + return -val / self.mu - xp.log(self.mu) + xp.log(val > self.minimum) @xp_wrap def cdf(self, val, *, xp=np): - if isinstance(val, (float, int)): - if val < self.minimum: - _cdf = 0. - else: - _cdf = 1. - xp.exp(-val / self.mu) - else: - _cdf = xp.zeros(val.size) - _cdf[val >= self.minimum] = 1. - xp.exp(-val[val >= self.minimum] / self.mu) - return _cdf + return xp.maximum(1. - xp.exp(-val / self.mu), 0) class StudentT(Prior): @@ -930,18 +870,11 @@ def rescale(self, val, *, xp=np): This maps to the inverse CDF. This has been analytically solved for this case. """ - if isinstance(val, (float, int)): - if val == 0: - rescaled = -np.inf - elif val == 1: - rescaled = np.inf - else: - rescaled = stdtrit(self.df, val) * self.scale + self.mu - else: - rescaled = stdtrit(self.df, val) * self.scale + self.mu - rescaled[val == 0] = -xp.inf - rescaled[val == 1] = xp.inf - return rescaled + return ( + xp.nan_to_num(stdtrit(self.df, val) * self.scale + self.mu) + + xp.log(val > 0) + - xp.log(val < 1) + ) @xp_wrap def prob(self, val, *, xp=np): @@ -1117,25 +1050,14 @@ def __init__(self, mu, scale, name=None, latex_label=None, unit=None, boundary=N self.mu = mu self.scale = scale - def rescale(self, val): + @xp_wrap + def rescale(self, val, *, xp=np): """ 'Rescale' a sample from the unit line element to the appropriate Logistic prior. This maps to the inverse CDF. This has been analytically solved for this case. """ - if isinstance(val, (float, int)): - if val == 0: - rescaled = -np.inf - elif val == 1: - rescaled = np.inf - else: - rescaled = self.mu + self.scale * np.log(val / (1. - val)) - else: - rescaled = np.inf * np.ones(val.size) - rescaled[val == 0] = -np.inf - rescaled[(val > 0) & (val < 1)] = self.mu + self.scale\ - * np.log(val[(val > 0) & (val < 1)] / (1. - val[(val > 0) & (val < 1)])) - return rescaled + return self.mu + self.scale * xp.log(xp.maximum(val / (1 - val), 0)) def prob(self, val): """Return the prior probability of val. @@ -1197,21 +1119,14 @@ def __init__(self, alpha, beta, name=None, latex_label=None, unit=None, boundary self.alpha = alpha self.beta = beta - def rescale(self, val): + @xp_wrap + def rescale(self, val, *, xp=np): """ 'Rescale' a sample from the unit line element to the appropriate Cauchy prior. This maps to the inverse CDF. This has been analytically solved for this case. """ - rescaled = self.alpha + self.beta * np.tan(np.pi * (val - 0.5)) - if isinstance(val, (float, int)): - if val == 1: - rescaled = np.inf - elif val == 0: - rescaled = -np.inf - else: - rescaled[val == 1] = np.inf - rescaled[val == 0] = -np.inf + rescaled = self.alpha + self.beta * xp.tan(np.pi * (val - 0.5)) return rescaled def prob(self, val): @@ -1323,15 +1238,7 @@ def ln_prob(self, val): return _ln_prob def cdf(self, val): - if isinstance(val, (float, int)): - if val < self.minimum: - _cdf = 0. - else: - _cdf = gammainc(self.k, val / self.theta) - else: - _cdf = np.zeros(val.size) - _cdf[val >= self.minimum] = gammainc(self.k, val[val >= self.minimum] / self.theta) - return _cdf + return gammainc(self.k, xp.maximum(val, self.minimum) / self.theta) class ChiSquared(Gamma): diff --git a/bilby/core/prior/interpolated.py b/bilby/core/prior/interpolated.py index 5fbf8f9c1..d47f14209 100644 --- a/bilby/core/prior/interpolated.py +++ b/bilby/core/prior/interpolated.py @@ -3,6 +3,7 @@ from .base import Prior from ..utils import logger, WrappedInterp1d as interp1d +from ...compat.utils import xp_wrap class Interped(Prior): @@ -80,7 +81,8 @@ def prob(self, val): def cdf(self, val): return self.cumulative_distribution(val) - def rescale(self, val): + @xp_wrap + def rescale(self, val, *, xp=np): """ 'Rescale' a sample from the unit line element to the prior. diff --git a/bilby/core/prior/joint.py b/bilby/core/prior/joint.py index 43c8913e3..06a740497 100644 --- a/bilby/core/prior/joint.py +++ b/bilby/core/prior/joint.py @@ -7,6 +7,7 @@ from .base import Prior, PriorException from ..utils import logger, infer_args_from_method, get_dict_with_properties from ..utils import random +from ...compat.utils import xp_wrap class BaseJointPriorDist(object): @@ -295,7 +296,8 @@ def _sample(self, size, **kwargs): """ return samps - def rescale(self, value, **kwargs): + @xp_wrap + def rescale(self, value, *, xp=np, **kwargs): """ Rescale from a unit hypercube to JointPriorDist. Note that no bounds are applied in the rescale function. (child classes need to @@ -317,7 +319,7 @@ def rescale(self, value, **kwargs): An vector sample drawn from the multivariate Gaussian distribution. """ - samp = np.array(value) + samp = xp.array(value) if len(samp.shape) == 1: samp = samp.reshape(1, self.num_vars) @@ -327,7 +329,7 @@ def rescale(self, value, **kwargs): raise ValueError("Array is the wrong shape") samp = self._rescale(samp, **kwargs) - return np.squeeze(samp) + return xp.squeeze(samp) def _rescale(self, samp, **kwargs): """ @@ -611,7 +613,9 @@ def add_mode(self, mus=None, sigmas=None, corrcoef=None, cov=None, weight=1.0): scipy.stats.multivariate_normal(mean=np.zeros(self.num_vars), cov=self.corrcoefs[-1]) ) - def _rescale(self, samp, **kwargs): + @xp_wrap + def _rescale(self, samp, *, xp=np, **kwargs): + print(samp, xp) try: mode = kwargs["mode"] except KeyError: @@ -626,7 +630,7 @@ def _rescale(self, samp, **kwargs): samp = erfinv(2.0 * samp - 1) * 2.0 ** 0.5 # rotate and scale to the multivariate normal shape - samp = self.mus[mode] + self.sigmas[mode] * np.einsum( + samp = self.mus[mode] + self.sigmas[mode] * xp.einsum( "ij,kj->ik", samp * self.sqeigvalues[mode], self.eigvectors[mode] ) return samp @@ -778,7 +782,8 @@ def maximum(self, maximum): self._maximum = maximum self.dist.bounds[self.name] = (self.dist.bounds[self.name][0], maximum) - def rescale(self, val, **kwargs): + @xp_wrap + def rescale(self, val, *, xp=np, **kwargs): """ Scale a unit hypercube sample to the prior. @@ -793,11 +798,10 @@ def rescale(self, val, **kwargs): float: A sample from the prior parameter. """ - self.dist.rescale_parameters[self.name] = val if self.dist.filled_rescale(): - values = np.array(list(self.dist.rescale_parameters.values())).T + values = xp.array(list(self.dist.rescale_parameters.values())).T samples = self.dist.rescale(values, **kwargs) self.dist.reset_rescale() return samples diff --git a/test/core/prior/prior_test.py b/test/core/prior/prior_test.py index 17d360d0c..0643dc4df 100644 --- a/test/core/prior/prior_test.py +++ b/test/core/prior/prior_test.py @@ -848,6 +848,29 @@ def test_set_minimum_setting(self): continue prior.minimum = (prior.maximum + prior.minimum) / 2 self.assertTrue(min(prior.sample(10000)) > prior.minimum) + + def test_jax_rescale(self): + import jax + + points = jax.numpy.linspace(1e-3, 1 - 1e-3, 10) + for prior in self.priors: + if isinstance( + prior, ( + bilby.core.prior.StudentT, + bilby.core.prior.Beta, + bilby.core.prior.Gamma, + ), + ) or bilby.core.prior.JointPrior in prior.__class__.__mro__: + continue + print(prior) + scaled = prior.rescale(points) + assert isinstance(scaled, jax.Array) + if isinstance(prior, bilby.core.prior.DeltaFunction): + continue + assert max(abs(prior.cdf(scaled) - points)) < 1e-6 + probs = prior.prob(scaled) + assert min(probs) > 0 + assert max(abs(jax.numpy.log(probs) - prior.ln_prob(scaled))) < 1e-6 if __name__ == "__main__": From b658a02ebff4864ad7982c3e344543708ac90513 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Thu, 12 Dec 2024 22:45:57 +0000 Subject: [PATCH 010/140] TEST: make all prior tests run This required making some changes to the tests for conditional dicts as I've changed the output types and the backend introspection doesn't work on dict_items for some reason --- bilby/core/prior/analytical.py | 154 ++++++++++++++-------------- bilby/core/prior/conditional.py | 1 + bilby/core/prior/dict.py | 34 +----- bilby/core/prior/slabspike.py | 65 +++++------- test/core/prior/conditional_test.py | 9 +- test/core/prior/prior_test.py | 35 +++---- test/core/prior/slabspike_test.py | 13 +++ 7 files changed, 140 insertions(+), 171 deletions(-) diff --git a/bilby/core/prior/analytical.py b/bilby/core/prior/analytical.py index 713c27fb3..876196ec6 100644 --- a/bilby/core/prior/analytical.py +++ b/bilby/core/prior/analytical.py @@ -172,7 +172,8 @@ def ln_prob(self, val, *, xp=np): @xp_wrap def cdf(self, val, *, xp=np): if self.alpha == -1: - _cdf = xp.log(val / self.minimum) / xp.log(self.maximum / self.minimum) + with np.errstate(invalid="ignore"): + _cdf = xp.log(val / self.minimum) / xp.log(self.maximum / self.minimum) else: _cdf = ( (val ** (self.alpha + 1) - self.minimum ** (self.alpha + 1)) @@ -733,10 +734,7 @@ def prob(self, val, *, xp=np): ======= Union[float, array_like]: Prior probability of val """ - return ( - xp.exp(-(xp.log(xp.maximum(val, self.minimum)) - self.mu) ** 2 / self.sigma ** 2 / 2) - / xp.sqrt(2 * np.pi) / val / self.sigma - ) * (val > self.minimum) + return xp.exp(self.ln_prob(val, xp=xp)) @xp_wrap def ln_prob(self, val, *, xp=np): @@ -750,16 +748,18 @@ def ln_prob(self, val, *, xp=np): ======= Union[float, array_like]: Prior probability of val """ - return ( - -(xp.log(val) - self.mu) ** 2 / self.sigma ** 2 / 2 - - xp.log(xp.sqrt(2 * np.pi) * val * self.sigma) - ) + xp.log(val > self.minimum) + with np.errstate(divide="ignore", invalid="ignore"): + return xp.nan_to_num(( + -(xp.log(xp.maximum(val, self.minimum)) - self.mu) ** 2 / self.sigma ** 2 / 2 + - xp.log(xp.sqrt(2 * np.pi) * val * self.sigma) + ) + xp.log(val > self.minimum), nan=-xp.inf, neginf=-xp.inf, posinf=-xp.inf) @xp_wrap def cdf(self, val, *, xp=np): - return 0.5 + erf( - (xp.log(xp.maximum(val, self.minimum)) - self.mu) / self.sigma / np.sqrt(2) - ) / 2 + with np.errstate(divide="ignore"): + return 0.5 + erf( + (xp.log(xp.maximum(val, self.minimum)) - self.mu) / self.sigma / np.sqrt(2) + ) / 2 class LogGaussian(LogNormal): @@ -794,7 +794,8 @@ def rescale(self, val, *, xp=np): This maps to the inverse CDF. This has been analytically solved for this case. """ - return -self.mu * xp.log1p(-val) + with np.errstate(divide="ignore", over="ignore"): + return -self.mu * xp.log1p(-val) @xp_wrap def prob(self, val, *, xp=np): @@ -808,7 +809,7 @@ def prob(self, val, *, xp=np): ======= Union[float, array_like]: Prior probability of val """ - return xp.exp(-val / self.mu) / self.mu * (val >= self.minimum) + return xp.exp(self.ln_prob(val, xp=xp)) @xp_wrap def ln_prob(self, val, *, xp=np): @@ -822,11 +823,13 @@ def ln_prob(self, val, *, xp=np): ======= Union[float, array_like]: Prior probability of val """ - return -val / self.mu - xp.log(self.mu) + xp.log(val > self.minimum) + with np.errstate(divide="ignore"): + return -val / self.mu - xp.log(self.mu) + xp.log(val >= self.minimum) @xp_wrap def cdf(self, val, *, xp=np): - return xp.maximum(1. - xp.exp(-val / self.mu), 0) + with np.errstate(divide="ignore", invalid="ignore", over="ignore"): + return xp.maximum(1. - xp.exp(-val / self.mu), 0) class StudentT(Prior): @@ -869,12 +872,17 @@ def rescale(self, val, *, xp=np): 'Rescale' a sample from the unit line element to the appropriate Student's t-prior. This maps to the inverse CDF. This has been analytically solved for this case. + + Notes + ===== + This explicitly casts to the requested backend, but the computation will be done by scipy. """ - return ( - xp.nan_to_num(stdtrit(self.df, val) * self.scale + self.mu) - + xp.log(val > 0) - - xp.log(val < 1) - ) + with np.errstate(divide="ignore", invalid="ignore"): + return ( + xp.nan_to_num(stdtrit(self.df, val) * self.scale + self.mu) + + xp.log(val > 0) + - xp.log(val < 1) + ) @xp_wrap def prob(self, val, *, xp=np): @@ -902,9 +910,11 @@ def ln_prob(self, val, *, xp=np): ======= Union[float, array_like]: Prior probability of val """ - return gammaln(0.5 * (self.df + 1)) - gammaln(0.5 * self.df)\ - - xp.log((np.pi * self.df)**0.5 * self.scale) - (self.df + 1) / 2 *\ - xp.log(1 + ((val - self.mu) / self.scale) ** 2 / self.df) + return ( + gammaln(0.5 * (self.df + 1)) - gammaln(0.5 * self.df) + - xp.log((np.pi * self.df)**0.5 * self.scale) - (self.df + 1) / 2 + * xp.log(1 + ((val - self.mu) / self.scale) ** 2 / self.df) + ) def cdf(self, val): return stdtr(self.df, (val - self.mu) / self.scale) @@ -948,13 +958,21 @@ def __init__(self, alpha, beta, minimum=0, maximum=1, name=None, self.alpha = alpha self.beta = beta - def rescale(self, val): + @xp_wrap + def rescale(self, val, *, xp=np): """ 'Rescale' a sample from the unit line element to the appropriate Beta prior. This maps to the inverse CDF. This has been analytically solved for this case. + + Notes + ===== + This explicitly casts to the requested backend, but the computation will be done by scipy. """ - return betaincinv(self.alpha, self.beta, val) * (self.maximum - self.minimum) + self.minimum + return ( + xp.asarray(betaincinv(self.alpha, self.beta, val)) * (self.maximum - self.minimum) + + self.minimum + ) @xp_wrap def prob(self, val, *, xp=np): @@ -983,42 +1001,18 @@ def ln_prob(self, val, *, xp=np): Union[float, array_like]: Prior probability of val """ _ln_prob = ( - xlogy(self.alpha - 1, val - self.minimum) - + xlogy(self.beta - 1, self.maximum - val) + xlogy(xp.asarray(self.alpha - 1), val - self.minimum) + + xlogy(xp.asarray(self.beta - 1), self.maximum - val) - betaln(self.alpha, self.beta) - xlogy(self.alpha + self.beta - 1, self.maximum - self.minimum) ) - - # deal with the fact that if alpha or beta are < 1 you get infinities at 0 and 1 - if isinstance(val, (float, int)): - if xp.isfinite(_ln_prob) and self.minimum <= val <= self.maximum: - return _ln_prob - return -xp.inf - else: - _ln_prob_sub = xp.full_like(val, -xp.inf) - idx = xp.isfinite(_ln_prob) & (val >= self.minimum) & (val <= self.maximum) - _ln_prob_sub[idx] = _ln_prob[idx] - return _ln_prob_sub + return xp.nan_to_num(_ln_prob, nan=-xp.inf, neginf=-xp.inf, posinf=-xp.inf) @xp_wrap def cdf(self, val, *, xp=np): - if isinstance(val, (float, int)): - if val > self.maximum: - return 1. - elif val < self.minimum: - return 0. - else: - return betainc( - self.alpha, self.beta, - (val - self.minimum) / (self.maximum - self.minimum) - ) - else: - _cdf = np.nan_to_num(betainc(self.alpha, self.beta, - (val - self.minimum) / (self.maximum - self.minimum))) - _cdf *= val >= self.minimum - _cdf *= val <= self.maximum - _cdf += val > self.maximum - return _cdf + return xp.nan_to_num( + betainc(self.alpha, self.beta, (val - self.minimum) / (self.maximum - self.minimum)) + ) + (val > self.maximum) class Logistic(Prior): @@ -1057,7 +1051,9 @@ def rescale(self, val, *, xp=np): This maps to the inverse CDF. This has been analytically solved for this case. """ - return self.mu + self.scale * xp.log(xp.maximum(val / (1 - val), 0)) + with np.errstate(divide="ignore"): + val = xp.asarray(val) + return self.mu + self.scale * xp.log(xp.maximum(val / (1 - val), 0)) def prob(self, val): """Return the prior probability of val. @@ -1072,7 +1068,8 @@ def prob(self, val): """ return np.exp(self.ln_prob(val)) - def ln_prob(self, val): + @xp_wrap + def ln_prob(self, val, *, xp=np): """Returns the log prior probability of val. Parameters @@ -1083,8 +1080,9 @@ def ln_prob(self, val): ======= Union[float, array_like]: Prior probability of val """ - return -(val - self.mu) / self.scale -\ - 2. * np.log(1. + np.exp(-(val - self.mu) / self.scale)) - np.log(self.scale) + with np.errstate(over="ignore"): + return -(val - self.mu) / self.scale -\ + 2. * np.log1p(xp.exp(-(val - self.mu) / self.scale)) - np.log(self.scale) def cdf(self, val): return 1. / (1. + np.exp(-(val - self.mu) / self.scale)) @@ -1127,7 +1125,8 @@ def rescale(self, val, *, xp=np): This maps to the inverse CDF. This has been analytically solved for this case. """ rescaled = self.alpha + self.beta * xp.tan(np.pi * (val - 0.5)) - return rescaled + with np.errstate(divide="ignore", invalid="ignore"): + return rescaled - xp.log(val < 1) + xp.log(val > 0) def prob(self, val): """Return the prior probability of val. @@ -1193,15 +1192,17 @@ def __init__(self, k, theta=1., name=None, latex_label=None, unit=None, boundary self.k = k self.theta = theta - def rescale(self, val): + @xp_wrap + def rescale(self, val, *, xp=np): """ 'Rescale' a sample from the unit line element to the appropriate Gamma prior. This maps to the inverse CDF. This has been analytically solved for this case. """ - return gammaincinv(self.k, val) * self.theta + return xp.asarray(gammaincinv(self.k, val)) * self.theta - def prob(self, val): + @xp_wrap + def prob(self, val, *, xp=np): """Return the prior probability of val. Parameters @@ -1212,9 +1213,10 @@ def prob(self, val): ======= Union[float, array_like]: Prior probability of val """ - return np.exp(self.ln_prob(val)) + return xp.exp(self.ln_prob(val)) - def ln_prob(self, val): + @xp_wrap + def ln_prob(self, val, *, xp=np): """Returns the log prior probability of val. Parameters @@ -1225,20 +1227,16 @@ def ln_prob(self, val): ======= Union[float, array_like]: Prior probability of val """ - if isinstance(val, (float, int)): - if val < self.minimum: - _ln_prob = -np.inf - else: - _ln_prob = xlogy(self.k - 1, val) - val / self.theta - xlogy(self.k, self.theta) - gammaln(self.k) - else: - _ln_prob = -np.inf * np.ones(val.size) - idx = (val >= self.minimum) - _ln_prob[idx] = xlogy(self.k - 1, val[idx]) - val[idx] / self.theta\ + with np.errstate(divide="ignore"): + ln_prob = ( + xlogy(xp.asarray(self.k - 1), val) - val / self.theta - xlogy(self.k, self.theta) - gammaln(self.k) - return _ln_prob + ) + xp.log(val >= self.minimum) + return xp.nan_to_num(ln_prob, nan=-xp.inf, neginf=-xp.inf, posinf=xp.inf) - def cdf(self, val): - return gammainc(self.k, xp.maximum(val, self.minimum) / self.theta) + @xp_wrap + def cdf(self, val, *, xp=np): + return gammainc(xp.asarray(self.k), xp.maximum(val, self.minimum) / self.theta) class ChiSquared(Gamma): diff --git a/bilby/core/prior/conditional.py b/bilby/core/prior/conditional.py index ad142c2a9..d0c7191a4 100644 --- a/bilby/core/prior/conditional.py +++ b/bilby/core/prior/conditional.py @@ -164,6 +164,7 @@ class depending on the required variables it depends on. self.reference_params will be used. """ + required_variables.pop("xp", None) if sorted(list(required_variables)) == sorted(self.required_variables): parameters = self.condition_func(self.reference_params.copy(), **required_variables) for key, value in parameters.items(): diff --git a/bilby/core/prior/dict.py b/bilby/core/prior/dict.py index da175b117..09480a099 100644 --- a/bilby/core/prior/dict.py +++ b/bilby/core/prior/dict.py @@ -636,6 +636,8 @@ def rescale(self, keys, theta): ======= list: List of floats containing the rescaled sample """ + if isinstance(theta, {}.values().__class__): + theta = list(theta) xp = array_namespace(theta) return xp.asarray([self[key].rescale(sample) for key, sample in zip(keys, theta)]) @@ -863,6 +865,8 @@ def rescale(self, keys, theta): ======= list: List of floats containing the rescaled sample """ + if isinstance(theta, {}.values().__class__): + theta = list(theta) xp = array_namespace(theta) keys = list(keys) @@ -877,35 +881,7 @@ def rescale(self, keys, theta): theta[index], **self.get_required_variables(key) ) self[key].least_recently_sampled = result[key] - if isinstance(self[key], JointPrior) and self[key].dist.distname not in joint: - joint[self[key].dist.distname] = [key] - elif isinstance(self[key], JointPrior): - joint[self[key].dist.distname].append(key) - for names in joint.values(): - # this is needed to unpack how joint prior rescaling works - # as an example of a joint prior over {a, b, c, d} we might - # get the following based on the order within the joint prior - # {a: [], b: [], c: [1, 2, 3, 4], d: []} - # -> [1, 2, 3, 4] - # -> {a: 1, b: 2, c: 3, d: 4} - values = xp.array([]) - for key in names: - values = xp.concatenate([values, result[key]]) - for key, value in zip(names, values): - result[key] = value - - def safe_flatten(value): - """ - this is gross but can be removed whenever we switch to returning - arrays, flatten converts 0-d arrays to 1-d so has to be special - cased - """ - if isinstance(value, (float, int, np.int64)): - return value - else: - return result[key].flatten() - - return xp.array([safe_flatten(result[key]) for key in keys]) + return xp.concatenate([result[key] for key in keys], axis=None) def _update_rescale_keys(self, keys): if not keys == self._least_recently_rescaled_keys: diff --git a/bilby/core/prior/slabspike.py b/bilby/core/prior/slabspike.py index 6910be608..ff823a369 100644 --- a/bilby/core/prior/slabspike.py +++ b/bilby/core/prior/slabspike.py @@ -1,8 +1,8 @@ -from numbers import Number import numpy as np from .base import Prior from ..utils import logger +from ...compat.utils import xp_wrap class SlabSpikePrior(Prior): @@ -72,7 +72,8 @@ def slab_fraction(self): def _find_inverse_cdf_fraction_before_spike(self): return float(self.slab.cdf(self.spike_location)) * self.slab_fraction - def rescale(self, val): + @xp_wrap + def rescale(self, val, *, xp=np): """ 'Rescale' a sample from the unit line element to the prior. @@ -85,25 +86,19 @@ def rescale(self, val): ======= array_like: Associated prior value with input value. """ - original_is_number = isinstance(val, Number) - val = np.atleast_1d(val) - lower_indices = val < self.inverse_cdf_below_spike - intermediate_indices = np.logical_and( - self.inverse_cdf_below_spike <= val, - val <= (self.inverse_cdf_below_spike + self.spike_height)) - higher_indices = val > (self.inverse_cdf_below_spike + self.spike_height) - - res = np.zeros(len(val)) - res[lower_indices] = self._contracted_rescale(val[lower_indices]) - res[intermediate_indices] = self.spike_location - res[higher_indices] = self._contracted_rescale(val[higher_indices] - self.spike_height) - if original_is_number: - try: - res = res[0] - except (KeyError, TypeError): - logger.warning("Based on inputs, a number should be output\ - but this could not be accessed from what was computed") + intermediate_indices = ( + (self.inverse_cdf_below_spike <= val) + * (val < (self.inverse_cdf_below_spike + self.spike_height)) + ) + higher_indices = val >= (self.inverse_cdf_below_spike + self.spike_height) + + slab_scaled = self._contracted_rescale(val - self.spike_height * higher_indices) + + res = xp.select( + [lower_indices | higher_indices, intermediate_indices], + [slab_scaled, self.spike_location], + ) return res def _contracted_rescale(self, val): @@ -122,7 +117,8 @@ def _contracted_rescale(self, val): """ return self.slab.rescale(val / self.slab_fraction) - def prob(self, val): + @xp_wrap + def prob(self, val, *, xp=np): """Return the prior probability of val. Returns np.inf for the spike location @@ -134,19 +130,13 @@ def prob(self, val): ======= array_like: Prior probability of val """ - original_is_number = isinstance(val, Number) res = self.slab.prob(val) * self.slab_fraction - res = np.atleast_1d(res) - res[val == self.spike_location] = np.inf - if original_is_number: - try: - res = res[0] - except (KeyError, TypeError): - logger.warning("Based on inputs, a number should be output\ - but this could not be accessed from what was computed") + with np.errstate(invalid="ignore"): + res += xp.nan_to_num(xp.inf * (val == self.spike_location), posinf=xp.inf) return res - def ln_prob(self, val): + @xp_wrap + def ln_prob(self, val, *, xp=np): """Return the Log prior probability of val. Returns np.inf for the spike location @@ -158,16 +148,9 @@ def ln_prob(self, val): ======= array_like: Prior probability of val """ - original_is_number = isinstance(val, Number) res = self.slab.ln_prob(val) + np.log(self.slab_fraction) - res = np.atleast_1d(res) - res[val == self.spike_location] = np.inf - if original_is_number: - try: - res = res[0] - except (KeyError, TypeError): - logger.warning("Based on inputs, a number should be output\ - but this could not be accessed from what was computed") + with np.errstate(divide="ignore"): + res += xp.nan_to_num(xp.inf * (val == self.spike_location), posinf=xp.inf) return res def cdf(self, val): @@ -185,5 +168,5 @@ def cdf(self, val): """ res = self.slab.cdf(val) * self.slab_fraction - res += self.spike_height * (val > self.spike_location) + res += (val > self.spike_location) * self.spike_height return res diff --git a/test/core/prior/conditional_test.py b/test/core/prior/conditional_test.py index 20c0cda93..68db12ed7 100644 --- a/test/core/prior/conditional_test.py +++ b/test/core/prior/conditional_test.py @@ -324,7 +324,7 @@ def test_rescale(self): expected = [self.test_sample["var_0"]] for ii in range(1, 4): expected.append(expected[-1] * self.test_sample[f"var_{ii}"]) - self.assertListEqual(expected, res) + np.testing.assert_array_equal(expected, res) def test_rescale_with_joint_prior(self): """ @@ -353,7 +353,6 @@ def test_rescale_with_joint_prior(self): keys = list(self.test_sample.keys()) + names res = priordict.rescale(keys=keys, theta=ref_variables) - self.assertIsInstance(res, list) self.assertEqual(np.shape(res), (6,)) self.assertListEqual([isinstance(r, float) for r in res], 6 * [True]) @@ -361,7 +360,7 @@ def test_rescale_with_joint_prior(self): expected = [self.test_sample["var_0"]] for ii in range(1, 4): expected.append(expected[-1] * self.test_sample[f"var_{ii}"]) - self.assertListEqual(expected, res[0:4]) + np.testing.assert_array_equal(expected, res[:4]) def test_cdf(self): """ @@ -370,11 +369,11 @@ def test_cdf(self): Note that the format of inputs/outputs is different between the two methods. """ sample = self.conditional_priors.sample() - self.assertEqual( + np.testing.assert_array_equal( self.conditional_priors.rescale( sample.keys(), self.conditional_priors.cdf(sample=sample).values() - ), list(sample.values()) + ), np.array(list(sample.values())) ) def test_rescale_illegal_conditions(self): diff --git a/test/core/prior/prior_test.py b/test/core/prior/prior_test.py index 0643dc4df..14f864e90 100644 --- a/test/core/prior/prior_test.py +++ b/test/core/prior/prior_test.py @@ -564,10 +564,14 @@ def test_probability_in_domain(self): """Test that the prior probability is non-negative in domain of validity and zero outside.""" for prior in self.priors: if prior.minimum == -np.inf: - prior.minimum = -1e5 + minimum = -1e5 + else: + minimum = prior.minimum if prior.maximum == np.inf: - prior.maximum = 1e5 - domain = np.linspace(prior.minimum, prior.maximum, 1000) + maximum = 1e5 + else: + maximum = prior.maximum + domain = np.linspace(minimum, maximum, 1000) self.assertTrue(all(prior.prob(domain) >= 0)) def test_probability_surrounding_domain(self): @@ -579,13 +583,14 @@ def test_probability_surrounding_domain(self): if isinstance(prior, bilby.core.prior.analytical.SymmetricLogUniform): # SymmetricLogUniform has support down to -maximum continue - surround_domain = np.linspace(prior.minimum - 1, prior.maximum + 1, 1000) - indomain = (surround_domain >= prior.minimum) | ( - surround_domain <= prior.maximum - ) - outdomain = (surround_domain < prior.minimum) | ( - surround_domain > prior.maximum - ) + with np.errstate(invalid="ignore"): + surround_domain = np.linspace(prior.minimum - 1, prior.maximum + 1, 1000) + indomain = (surround_domain >= prior.minimum) | ( + surround_domain <= prior.maximum + ) + outdomain = (surround_domain < prior.minimum) | ( + surround_domain > prior.maximum + ) if bilby.core.prior.JointPrior in prior.__class__.__mro__: if not prior.dist.filled_request(): continue @@ -849,18 +854,12 @@ def test_set_minimum_setting(self): prior.minimum = (prior.maximum + prior.minimum) / 2 self.assertTrue(min(prior.sample(10000)) > prior.minimum) - def test_jax_rescale(self): + def test_jax_methods(self): import jax points = jax.numpy.linspace(1e-3, 1 - 1e-3, 10) for prior in self.priors: - if isinstance( - prior, ( - bilby.core.prior.StudentT, - bilby.core.prior.Beta, - bilby.core.prior.Gamma, - ), - ) or bilby.core.prior.JointPrior in prior.__class__.__mro__: + if bilby.core.prior.JointPrior in prior.__class__.__mro__: continue print(prior) scaled = prior.rescale(points) diff --git a/test/core/prior/slabspike_test.py b/test/core/prior/slabspike_test.py index d2cdcc55a..8cb2fcf1d 100644 --- a/test/core/prior/slabspike_test.py +++ b/test/core/prior/slabspike_test.py @@ -102,6 +102,19 @@ def tearDown(self): del self.test_nodes_finite_support del self.test_nodes_infinite_support + def test_jax_methods(self): + import jax + + points = jax.numpy.linspace(1e-3, 1 - 1e-3, 10) + for prior in self.slab_spikes: + scaled = prior.rescale(points) + assert isinstance(scaled, jax.Array) + if isinstance(prior, bilby.core.prior.DeltaFunction): + continue + probs = prior.prob(scaled) + assert min(probs) > 0 + assert max(abs(jax.numpy.log(probs) - prior.ln_prob(scaled))) < 1e-6 + def test_prob_on_slab(self): for slab, slab_spike, test_nodes in zip(self.slabs, self.slab_spikes, self.test_nodes): expected = slab.prob(test_nodes) * slab_spike.slab_fraction From 3ed92e0101327e050d8e9f8353fe3050718a26a6 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Sat, 25 Jan 2025 03:24:23 -0800 Subject: [PATCH 011/140] DEV: move some jax functionality to compat --- bilby/compat/jax.py | 73 +++++++++++++++++++++++++++++++++++++++++ bilby/gw/jaxstuff.py | 78 -------------------------------------------- 2 files changed, 73 insertions(+), 78 deletions(-) create mode 100644 bilby/compat/jax.py diff --git a/bilby/compat/jax.py b/bilby/compat/jax.py new file mode 100644 index 000000000..8d297487b --- /dev/null +++ b/bilby/compat/jax.py @@ -0,0 +1,73 @@ +from functools import partial + +import jax +import jax.numpy as jnp +from ..core.likelihood import Likelihood + + +def generic_bilby_likelihood_function(likelihood, parameters, use_ratio=True): + """ + A wrapper to allow a :code:`Bilby` likelihood to be used with :code:`jax`. + + Parameters + ========== + likelihood: bilby.core.likelihood.Likelihood + The likelihood to evaluate. + parameters: dict + The parameters to evaluate the likelihood at. + use_ratio: bool, optional + Whether to evaluate the likelihood ratio or the full likelihood. + Default is :code:`True`. + """ + parameters = {k: jnp.array(v) for k, v in parameters.items()} + likelihood.parameters.update(parameters) + if use_ratio: + return likelihood.log_likelihood_ratio() + else: + return likelihood.log_likelihood() + + +class JittedLikelihood(Likelihood): + """ + A wrapper to just-in-time compile a :code:`Bilby` likelihood for use with :code:`jax`. + + .. note:: + + This is currently hardcoded to return the log likelihood ratio, regardless of + the input. + + Parameters + ========== + likelihood: bilby.core.likelihood.Likelihood + The likelihood to wrap. + likelihood_func: callable, optional + The function to use to evaluate the likelihood. Default is + :code:`generic_bilby_likelihood_function`. This function should take the + likelihood and parameters as arguments along with additional keyword arguments. + kwargs: dict, optional + Additional keyword arguments to pass to the likelihood function. + """ + + def __init__( + self, + likelihood, + likelihood_func=generic_bilby_likelihood_function, + kwargs=None, + cast_to_float=True, + ): + if kwargs is None: + kwargs = dict() + self.kwargs = kwargs + self._likelihood = likelihood + self.likelihood_func = jax.jit(partial(likelihood_func, likelihood)) + self.cast_to_float = cast_to_float + super().__init__(dict()) + + def __getattr__(self, name): + return getattr(self._likelihood, name) + + def log_likelihood_ratio(self): + ln_l = jnp.nan_to_num(self.likelihood_func(self.parameters, **self.kwargs)) + if self.cast_to_float: + ln_l = float(ln_l) + return ln_l diff --git a/bilby/gw/jaxstuff.py b/bilby/gw/jaxstuff.py index f1e1c57b0..6046e51bd 100644 --- a/bilby/gw/jaxstuff.py +++ b/bilby/gw/jaxstuff.py @@ -4,14 +4,8 @@ idea of how much pain is being added. """ -from functools import partial - -from bilby.core.likelihood import Likelihood - import jax import jax.numpy as jnp -from plum import dispatch -from jax.scipy.special import i0e from ripple.waveforms import IMRPhenomPv2 @@ -71,75 +65,3 @@ def ripple_bbh( hp, hc = wf_func(frequencies, theta, jax.numpy.array(20.0)) return dict(plus=hp, cross=hc) - -def generic_bilby_likelihood_function(likelihood, parameters, use_ratio=True): - """ - A wrapper to allow a :code:`Bilby` likelihood to be used with :code:`jax`. - - Parameters - ========== - likelihood: bilby.core.likelihood.Likelihood - The likelihood to evaluate. - parameters: dict - The parameters to evaluate the likelihood at. - use_ratio: bool, optional - Whether to evaluate the likelihood ratio or the full likelihood. - Default is :code:`True`. - """ - parameters = {k: jnp.array(v) for k, v in parameters.items()} - likelihood.parameters.update(parameters) - if use_ratio: - return likelihood.log_likelihood_ratio() - else: - return likelihood.log_likelihood() - - -class JittedLikelihood(Likelihood): - """ - A wrapper to just-in-time compile a :code:`Bilby` likelihood for use with :code:`jax`. - - .. note:: - - This is currently hardcoded to return the log likelihood ratio, regardless of - the input. - - Parameters - ========== - likelihood: bilby.core.likelihood.Likelihood - The likelihood to wrap. - likelihood_func: callable, optional - The function to use to evaluate the likelihood. Default is - :code:`generic_bilby_likelihood_function`. This function should take the - likelihood and parameters as arguments along with additional keyword arguments. - kwargs: dict, optional - Additional keyword arguments to pass to the likelihood function. - """ - - def __init__( - self, - likelihood, - likelihood_func=generic_bilby_likelihood_function, - kwargs=None, - cast_to_float=True, - ): - if kwargs is None: - kwargs = dict() - self.kwargs = kwargs - self._likelihood = likelihood - self.likelihood_func = jax.jit(partial(likelihood_func, likelihood)) - self.cast_to_float = cast_to_float - super().__init__(dict()) - - def __getattr__(self, name): - return getattr(self._likelihood, name) - - def log_likelihood_ratio(self): - ln_l = jnp.nan_to_num(self.likelihood_func(self.parameters, **self.kwargs)) - if self.cast_to_float: - ln_l = float(ln_l) - return ln_l - - -@dispatch -def ln_i0(value: jax.Array): - return jnp.log(i0e(value)) + jnp.abs(value) From c49e6efacbf8f2a6c357f260cf4021aec35d7708 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Sat, 25 Jan 2025 03:25:55 -0800 Subject: [PATCH 012/140] REFACTOR: use array backend for ln_i0 --- bilby/gw/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bilby/gw/utils.py b/bilby/gw/utils.py index cf98cfc38..2457c5ea8 100644 --- a/bilby/gw/utils.py +++ b/bilby/gw/utils.py @@ -1003,7 +1003,6 @@ def plot_spline_pos(log_freqs, samples, nfreqs=100, level=0.9, color='k', label= plt.xlim(freq_points.min() - .5, freq_points.max() + 50) -@dispatch def ln_i0(value): """ A numerically stable method to evaluate ln(I_0) a modified Bessel function @@ -1019,7 +1018,8 @@ def ln_i0(value): array-like: The natural logarithm of the bessel function """ - return np.log(i0e(value)) + np.abs(value) + xp = array_module(value) + return xp.log(i0e(value)) + xp.abs(value) def calculate_time_to_merger(frequency, mass_1, mass_2, chi=0, safety=1.1): From ef3d482dc20ee480db1e628002e3ccfb5eab13f3 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Sat, 25 Jan 2025 03:28:34 -0800 Subject: [PATCH 013/140] make distance marginalizatio backend transparent --- bilby/gw/likelihood/base.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/bilby/gw/likelihood/base.py b/bilby/gw/likelihood/base.py index a7c5c35cb..a14a17eae 100644 --- a/bilby/gw/likelihood/base.py +++ b/bilby/gw/likelihood/base.py @@ -771,12 +771,12 @@ def distance_marginalized_likelihood(self, d_inner_h, h_inner_h, *, parameters): d_inner_h_ref, h_inner_h_ref = self._setup_rho( d_inner_h, h_inner_h, parameters=parameters) if self.phase_marginalization: - d_inner_h_ref = np.abs(d_inner_h_ref) + d_inner_h_ref = abs(d_inner_h_ref) else: - d_inner_h_ref = np.real(d_inner_h_ref) + d_inner_h_ref = d_inner_h_ref.real return self._interp_dist_margd_loglikelihood( - d_inner_h_ref, h_inner_h_ref, grid=False) + d_inner_h_ref, h_inner_h_ref) def phase_marginalized_likelihood(self, d_inner_h, h_inner_h): d_inner_h = ln_i0(abs(d_inner_h)) @@ -920,9 +920,19 @@ def _setup_distance_marginalization(self, lookup_table=None): 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) + if "jax" in array_module(self.interferometers.frequency_array).__name__: + from interpax import Interpolator2D + import jax.numpy as jnp + self._interp_dist_margd_loglikelihood = Interpolator2D( + jnp.asarray(self._d_inner_h_ref_array), + jnp.asarray(self._optimal_snr_squared_ref_array), + jnp.asarray(self._dist_margd_loglikelihood_array.T), + extrap=-jnp.inf, + ) + else: + 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) @property def cached_lookup_table_filename(self): From 95be371ddac71eaaf011db32e2824bb5088d3196 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Sat, 25 Jan 2025 03:29:50 -0800 Subject: [PATCH 014/140] DEV: some more prior dict array refactoring --- bilby/core/prior/dict.py | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/bilby/core/prior/dict.py b/bilby/core/prior/dict.py index 09480a099..7a9a8dcfd 100644 --- a/bilby/core/prior/dict.py +++ b/bilby/core/prior/dict.py @@ -55,6 +55,9 @@ def __init__(self, dictionary=None, filename=None, conversion_function=None): else: self.conversion_function = self.default_conversion_function + def __hash__(self): + return hash(str(self)) + def evaluate_constraints(self, sample): out_sample = self.conversion_function(sample) try: @@ -539,9 +542,11 @@ def prob(self, sample, **kwargs): float: Joint probability of all individual sample probabilities """ - prob = np.prod([self[key].prob(sample[key]) for key in sample], **kwargs) + xp = array_namespace(*sample.values()) + prob = xp.prod(xp.asarray([self[key].prob(sample[key]) for key in sample]), **kwargs) - return self.check_prob(sample, prob) + return prob + # return self.check_prob(sample, prob) def check_prob(self, sample, prob): ratio = self.normalize_constraint_factor(tuple(sample.keys())) @@ -809,12 +814,14 @@ def prob(self, sample, **kwargs): """ self._prepare_evaluation(*zip(*sample.items())) - res = [ + xp = array_namespace(*sample.values()) + res = xp.asarray([ self[key].prob(sample[key], **self.get_required_variables(key)) for key in sample - ] - prob = np.prod(res, **kwargs) - return self.check_prob(sample, prob) + ]) + prob = xp.prod(res, **kwargs) + return prob + # return self.check_prob(sample, prob) def ln_prob(self, sample, axis=None, normalized=True): """ @@ -835,13 +842,15 @@ def ln_prob(self, sample, axis=None, normalized=True): """ self._prepare_evaluation(*zip(*sample.items())) - res = [ + xp = array_namespace(*sample.values()) + res = xp.array([ self[key].ln_prob(sample[key], **self.get_required_variables(key)) for key in sample - ] - ln_prob = np.sum(res, axis=axis) - return self.check_ln_prob(sample, ln_prob, - normalized=normalized) + ]) + ln_prob = xp.sum(res, axis=axis) + return ln_prob + # return self.check_ln_prob(sample, ln_prob, + # normalized=normalized) def cdf(self, sample): self._prepare_evaluation(*zip(*sample.items())) @@ -881,7 +890,7 @@ def rescale(self, keys, theta): theta[index], **self.get_required_variables(key) ) self[key].least_recently_sampled = result[key] - return xp.concatenate([result[key] for key in keys], axis=None) + return xp.array([result[key] for key in keys]) def _update_rescale_keys(self, keys): if not keys == self._least_recently_rescaled_keys: From 9f7eb38f48ab86e9ed45fbb714e6597de0a9c572 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Wed, 29 Jan 2025 13:12:59 -0800 Subject: [PATCH 015/140] fix jax logic for distance marginalization --- bilby/gw/likelihood/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bilby/gw/likelihood/base.py b/bilby/gw/likelihood/base.py index a14a17eae..42a2f0127 100644 --- a/bilby/gw/likelihood/base.py +++ b/bilby/gw/likelihood/base.py @@ -920,7 +920,7 @@ def _setup_distance_marginalization(self, lookup_table=None): self._create_lookup_table() else: self._create_lookup_table() - if "jax" in array_module(self.interferometers.frequency_array).__name__: + if "jax" in array_module(self.interferometers[0].vertex).__name__: from interpax import Interpolator2D import jax.numpy as jnp self._interp_dist_margd_loglikelihood = Interpolator2D( From 553689f0a10279cd5e216a43f0da5016c3fa4af1 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Wed, 29 Jan 2025 13:13:40 -0800 Subject: [PATCH 016/140] improve efficiency of setting up multibanding --- bilby/gw/likelihood/multiband.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bilby/gw/likelihood/multiband.py b/bilby/gw/likelihood/multiband.py index cc5d46ed7..9331d9e94 100644 --- a/bilby/gw/likelihood/multiband.py +++ b/bilby/gw/likelihood/multiband.py @@ -551,7 +551,7 @@ def _setup_quadratic_coefficients_linear_interp(self): linear-interpolation algorithm""" logger.info("Linear-interpolation algorithm is used for (h, h).") self.quadratic_coeffs = dict((ifo.name, np.array([])) for ifo in self.interferometers) - original_duration = self.interferometers.duration + original_duration = float(self.interferometers.duration) for b in range(self.number_of_bands): logger.info(f"Pre-computing quadratic coefficients for the {b}-th band") @@ -575,7 +575,7 @@ def _setup_quadratic_coefficients_linear_interp(self): start_idx_in_band + len(window_sequence) - 1, len(ifo.power_spectral_density_array) - 1 ) - _frequency_mask = ifo.frequency_mask[start_idx_in_band:end_idx_in_band + 1] + _frequency_mask = np.asarray(ifo.frequency_mask[start_idx_in_band:end_idx_in_band + 1]) window_over_psd = np.zeros(end_idx_in_band + 1 - start_idx_in_band) window_over_psd[_frequency_mask] = \ 1. / ifo.power_spectral_density_array[start_idx_in_band:end_idx_in_band + 1][_frequency_mask] From 55c3cefd0fc79d6c38141d7c2adbbf2de25ef62c Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Wed, 29 Jan 2025 13:14:26 -0800 Subject: [PATCH 017/140] make high-dimensional gaussians jax compatible --- bilby/core/likelihood.py | 50 +++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/bilby/core/likelihood.py b/bilby/core/likelihood.py index 210dc501f..73b59a7ac 100644 --- a/bilby/core/likelihood.py +++ b/bilby/core/likelihood.py @@ -5,6 +5,7 @@ from scipy.stats import multivariate_normal from .utils import infer_parameters_from_function, infer_args_from_function_except_n_args +from ..compat.utils import array_module class Likelihood: @@ -467,19 +468,27 @@ class AnalyticalMultidimensionalCovariantGaussian(Likelihood): """ def __init__(self, mean, cov): - self.cov = np.atleast_2d(cov) - self.mean = np.atleast_1d(mean) - self.sigma = np.sqrt(np.diag(self.cov)) - self.pdf = multivariate_normal(mean=self.mean, cov=self.cov) - super(AnalyticalMultidimensionalCovariantGaussian, self).__init__() + xp = array_module(cov) + self.cov = xp.atleast_2d(cov) + self.mean = xp.atleast_1d(mean) + self.sigma = xp.sqrt(np.diag(self.cov)) + if xp == np: + self.logpdf = multivariate_normal(mean=self.mean, cov=self.cov).logpdf + else: + from functools import partial + from jax.scipy.stats.multivariate_normal import logpdf + self.logpdf = partial(logpdf, mean=self.mean, cov=self.cov) + parameters = {"x{0}".format(i): 0 for i in range(self.dim)} + super(AnalyticalMultidimensionalCovariantGaussian, self).__init__(parameters=parameters) @property def dim(self): return len(self.cov[0]) def log_likelihood(self, parameters): - x = np.array([parameters["x{0}".format(i)] for i in range(self.dim)]) - return self.pdf.logpdf(x) + xp = array_module(self.cov) + x = xp.array([parameters["x{0}".format(i)] for i in range(self.dim)]) + return self.logpdf(x) class AnalyticalMultidimensionalBimodalCovariantGaussian(Likelihood): @@ -497,21 +506,30 @@ class AnalyticalMultidimensionalBimodalCovariantGaussian(Likelihood): """ def __init__(self, mean_1, mean_2, cov): - self.cov = np.atleast_2d(cov) - self.sigma = np.sqrt(np.diag(self.cov)) - self.mean_1 = np.atleast_1d(mean_1) - self.mean_2 = np.atleast_1d(mean_2) - self.pdf_1 = multivariate_normal(mean=self.mean_1, cov=self.cov) - self.pdf_2 = multivariate_normal(mean=self.mean_2, cov=self.cov) - super(AnalyticalMultidimensionalBimodalCovariantGaussian, self).__init__() + xp = array_module(cov) + self.cov = xp.atleast_2d(cov) + self.sigma = xp.sqrt(np.diag(self.cov)) + self.mean_1 = xp.atleast_1d(mean_1) + self.mean_2 = xp.atleast_1d(mean_2) + if xp == np: + self.logpdf_1 = multivariate_normal(mean=self.mean_1, cov=self.cov).logpdf + self.logpdf_2 = multivariate_normal(mean=self.mean_2, cov=self.cov).logpdf + else: + from functools import partial + from jax.scipy.stats.multivariate_normal import logpdf + self.logpdf_1 = partial(logpdf, mean=self.mean_1, cov=self.cov) + self.logpdf_2 = partial(logpdf, mean=self.mean_2, cov=self.cov) + parameters = {"x{0}".format(i): 0 for i in range(self.dim)} + super(AnalyticalMultidimensionalBimodalCovariantGaussian, self).__init__(parameters=parameters) @property def dim(self): return len(self.cov[0]) def log_likelihood(self, parameters): - x = np.array([parameters["x{0}".format(i)] for i in range(self.dim)]) - return -np.log(2) + np.logaddexp(self.pdf_1.logpdf(x), self.pdf_2.logpdf(x)) + xp = array_module(self.cov) + x = xp.array([self.parameters["x{0}".format(i)] for i in range(self.dim)]) + return -xp.log(2) + xp.logaddexp(self.logpdf_1(x), self.logpdf_2(x)) class JointLikelihood(Likelihood): From fd7c3c3d399a9d037dfddc2fef993e13884bc780 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Thu, 30 Jan 2025 12:53:42 -0800 Subject: [PATCH 018/140] make cubic spline calibration work with jax backend --- bilby/gw/detector/calibration.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bilby/gw/detector/calibration.py b/bilby/gw/detector/calibration.py index 729b9e332..229bdba25 100644 --- a/bilby/gw/detector/calibration.py +++ b/bilby/gw/detector/calibration.py @@ -46,6 +46,7 @@ import pandas as pd from scipy.interpolate import interp1d +from ...compat.utils import array_module from ...core.utils.log import logger from ...core.prior.dict import PriorDict from ..prior import CalibrationPriorDict @@ -330,9 +331,11 @@ def __repr__(self): def _evaluate_spline(self, kind, a, b, c, d, previous_nodes): """Evaluate Eq. (1) in https://dcc.ligo.org/LIGO-T2300140""" - parameters = np.array([self.params[f"{kind}_{ii}"] for ii in range(self.n_points)]) + xp = array_module(self.params[f"{kind}_0"]) + parameters = xp.array([self.params[f"{kind}_{ii}"] for ii in range(self.n_points)]) next_nodes = previous_nodes + 1 - spline_coefficients = self.nodes_to_spline_coefficients.dot(parameters) + nodes = xp.array(self.nodes_to_spline_coefficients) + spline_coefficients = nodes.dot(parameters) return ( a * parameters[previous_nodes] + b * parameters[next_nodes] From a062ca3d108c981c959aa801495a8d36e0df97e5 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Tue, 4 Feb 2025 11:54:08 -0600 Subject: [PATCH 019/140] BUG: fix linspace calls --- bilby/core/utils/series.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/bilby/core/utils/series.py b/bilby/core/utils/series.py index 8affa61be..c60362ab3 100644 --- a/bilby/core/utils/series.py +++ b/bilby/core/utils/series.py @@ -101,9 +101,11 @@ def create_time_series(sampling_frequency, duration, starting_time=0.): xp = array_module(sampling_frequency) _check_legal_sampling_frequency_and_duration(sampling_frequency, duration) number_of_samples = int(duration * sampling_frequency) - return xp.linspace(start=starting_time, - stop=duration + starting_time - 1 / sampling_frequency, - num=number_of_samples) + return xp.linspace( + starting_time, + duration + starting_time - 1 / sampling_frequency, + num=number_of_samples, + ) def create_frequency_series(sampling_frequency, duration): @@ -124,9 +126,7 @@ def create_frequency_series(sampling_frequency, duration): number_of_samples = int(xp.round(duration * sampling_frequency)) number_of_frequencies = int(xp.round(number_of_samples / 2) + 1) - return xp.linspace(start=0, - stop=sampling_frequency / 2, - num=number_of_frequencies) + return xp.linspace(0, sampling_frequency / 2, num=number_of_frequencies) def _check_legal_sampling_frequency_and_duration(sampling_frequency, duration): @@ -209,10 +209,11 @@ def nfft(time_domain_strain, sampling_frequency): strain / Hz, and the associated frequency_array. """ - frequency_domain_strain = np.fft.rfft(time_domain_strain) + xp = array_module(time_domain_strain) + frequency_domain_strain = xp.fft.rfft(time_domain_strain) frequency_domain_strain /= sampling_frequency - frequency_array = np.linspace( + frequency_array = xp.linspace( 0, sampling_frequency / 2, len(frequency_domain_strain)) return frequency_domain_strain, frequency_array @@ -234,7 +235,8 @@ def infft(frequency_domain_strain, sampling_frequency): time_domain_strain: array_like An array of the time domain strain """ - time_domain_strain_norm = np.fft.irfft(frequency_domain_strain) + xp = array_module(frequency_domain_strain) + time_domain_strain_norm = xp.fft.irfft(frequency_domain_strain) time_domain_strain = time_domain_strain_norm * sampling_frequency return time_domain_strain From a9587a87301e4616a03359fab0612b0e83c8c82f Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Tue, 4 Feb 2025 14:56:33 -0600 Subject: [PATCH 020/140] ENH: fix bottleneck in relative binning for JAX --- bilby/gw/likelihood/relative.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bilby/gw/likelihood/relative.py b/bilby/gw/likelihood/relative.py index 152c120f7..80a7b0c8a 100644 --- a/bilby/gw/likelihood/relative.py +++ b/bilby/gw/likelihood/relative.py @@ -333,7 +333,7 @@ def compute_summary_data(self): masked_bin_inds[-1] += 1 masked_strain = interferometer.frequency_domain_strain[mask] - masked_h0 = self.per_detector_fiducial_waveforms[interferometer.name][mask] + masked_h0 = np.asarray(self.per_detector_fiducial_waveforms[interferometer.name][mask]) masked_psd = interferometer.power_spectral_density_array[mask] duration = interferometer.duration a0, b0, a1, b1 = np.zeros((4, self.number_of_bins), dtype=complex) From 77b39a11344d8bc9fa2955d4c12eafdd5caa8211 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Tue, 4 Feb 2025 14:58:15 -0600 Subject: [PATCH 021/140] ENH: make interpolated prior backend friendly --- bilby/core/prior/interpolated.py | 3 ++- bilby/core/utils/calculus.py | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/bilby/core/prior/interpolated.py b/bilby/core/prior/interpolated.py index d47f14209..57e04738d 100644 --- a/bilby/core/prior/interpolated.py +++ b/bilby/core/prior/interpolated.py @@ -2,7 +2,8 @@ from scipy.integrate import trapezoid from .base import Prior -from ..utils import logger, WrappedInterp1d as interp1d +from ..utils import logger +from ..utils.calculus import interp1d from ...compat.utils import xp_wrap diff --git a/bilby/core/utils/calculus.py b/bilby/core/utils/calculus.py index e10ce6111..137dd894c 100644 --- a/bilby/core/utils/calculus.py +++ b/bilby/core/utils/calculus.py @@ -1,10 +1,11 @@ import math import numpy as np -from scipy.interpolate import interp1d, RectBivariateSpline +from scipy.interpolate import RectBivariateSpline, interp1d as _interp1d from scipy.special import logsumexp from .log import logger +from ...compat.utils import array_module def derivatives( @@ -189,6 +190,20 @@ def logtrapzexp(lnf, dx): return C + logsumexp([logsumexp(lnfdx1), logsumexp(lnfdx2)]) +class interp1d(_interp1d): + + def __call__(self, x): + xp = array_module(x) + if "jax" in xp.__name__: + if isinstance(self.fill_value, tuple): + left, right = self.fill_value + else: + left = right = self.fill_value + return xp.interp(x , xp.asarray(self.x), xp.asarray(self.y), left=left, right=right) + else: + return super().__call__(x) + + class BoundedRectBivariateSpline(RectBivariateSpline): def __init__(self, x, y, z, bbox=[None] * 4, kx=3, ky=3, s=0, fill_value=None): From f18dd0e0249c033e30c1f4776050cfca243c1826 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Wed, 5 Feb 2025 12:14:25 -0600 Subject: [PATCH 022/140] REFACTOR: refactor backend-specific interpolation code --- bilby/core/utils/calculus.py | 51 +++++++++++++++++++++++++++++------- bilby/gw/likelihood/base.py | 19 +++++--------- 2 files changed, 47 insertions(+), 23 deletions(-) diff --git a/bilby/core/utils/calculus.py b/bilby/core/utils/calculus.py index 137dd894c..bb89f48ee 100644 --- a/bilby/core/utils/calculus.py +++ b/bilby/core/utils/calculus.py @@ -192,16 +192,27 @@ def logtrapzexp(lnf, dx): class interp1d(_interp1d): - def __call__(self, x): - xp = array_module(x) - if "jax" in xp.__name__: - if isinstance(self.fill_value, tuple): - left, right = self.fill_value - else: - left = right = self.fill_value - return xp.interp(x , xp.asarray(self.x), xp.asarray(self.y), left=left, right=right) - else: - return super().__call__(x) + def __call__(self, x): + from array_api_compat import is_numpy_namespace + + xp = array_module(x) + if is_numpy_namespace(xp): + return super().__call__(x) + else: + return self._call_alt(x, xp=xp) + + def _call_alt(self, x, *, xp=np): + if isinstance(self.fill_value, tuple): + left, right = self.fill_value + else: + left = right = self.fill_value + return xp.interp( + x, + xp.asarray(self.x), + xp.asarray(self.y), + left=left, + right=right, + ) class BoundedRectBivariateSpline(RectBivariateSpline): @@ -217,9 +228,16 @@ def __init__(self, x, y, z, bbox=[None] * 4, kx=3, ky=3, s=0, fill_value=None): 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): + from array_api_compat import is_jax_namespace + xp = array_module(x) + if is_jax_namespace(xp): + return self._call_jax(x, y) 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) @@ -232,6 +250,19 @@ def __call__(self, x, y, dx=0, dy=0, grid=False): return result.item() else: return result + + def _call_jax(self, x, y): + import jax.numpy as jnp + from interpax import interp2d + + return interp2d( + x, + y, + jnp.asarray(self.x), + jnp.asarray(self.y), + jnp.asarray(self.z), + extrap=self.fill_value, + ) class WrappedInterp1d(interp1d): diff --git a/bilby/gw/likelihood/base.py b/bilby/gw/likelihood/base.py index 42a2f0127..9f4869d11 100644 --- a/bilby/gw/likelihood/base.py +++ b/bilby/gw/likelihood/base.py @@ -920,19 +920,12 @@ def _setup_distance_marginalization(self, lookup_table=None): self._create_lookup_table() else: self._create_lookup_table() - if "jax" in array_module(self.interferometers[0].vertex).__name__: - from interpax import Interpolator2D - import jax.numpy as jnp - self._interp_dist_margd_loglikelihood = Interpolator2D( - jnp.asarray(self._d_inner_h_ref_array), - jnp.asarray(self._optimal_snr_squared_ref_array), - jnp.asarray(self._dist_margd_loglikelihood_array.T), - extrap=-jnp.inf, - ) - else: - 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) + 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, + ) @property def cached_lookup_table_filename(self): From fd28b858b26e723a81da1b3041fa94def1b82655 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Wed, 5 Feb 2025 12:35:04 -0600 Subject: [PATCH 023/140] ENH: make sine gaussian model backend independent --- bilby/gw/source.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/bilby/gw/source.py b/bilby/gw/source.py index 78da709ba..11f441fba 100644 --- a/bilby/gw/source.py +++ b/bilby/gw/source.py @@ -1,5 +1,6 @@ import numpy as np +from ..compat.utils import array_module from ..core import utils from ..core.utils import logger from .conversion import bilby_to_lalsimulation_spins @@ -1188,20 +1189,22 @@ def sinegaussian(frequency_array, hrss, Q, frequency, **kwargs): dict: Dictionary containing the plus and cross components of the strain. """ - tau = Q / (np.sqrt(2.0) * np.pi * frequency) - temp = Q / (4.0 * np.sqrt(np.pi) * frequency) + xp = array_module(frequency_array) + tau = Q / (2.0**0.5 * np.pi * frequency) + temp = Q / (4.0 * np.pi**0.5 * frequency) fm = frequency_array - frequency fp = frequency_array + frequency - h_plus = ((hrss / np.sqrt(temp * (1 + np.exp(-Q**2)))) * - ((np.sqrt(np.pi) * tau) / 2.0) * - (np.exp(-fm**2 * np.pi**2 * tau**2) + - np.exp(-fp**2 * np.pi**2 * tau**2))) + negative_term = xp.exp(-fm**2 * np.pi**2 * tau**2) + positive_term = xp.exp(-fp**2 * np.pi**2 * tau**2) - h_cross = (-1j * (hrss / np.sqrt(temp * (1 - np.exp(-Q**2)))) * - ((np.sqrt(np.pi) * tau) / 2.0) * - (np.exp(-fm**2 * np.pi**2 * tau**2) - - np.exp(-fp**2 * np.pi**2 * tau**2))) + h_plus = hrss * np.pi**0.5 * tau / 2 * ( + negative_term + positive_term + ) / (temp * (1 + xp.exp(-Q**2)))**0.5 + + h_cross = -1j * hrss * np.pi**0.5 * tau / 2 * ( + negative_term - positive_term + ) / (temp * (1 - np.exp(-Q**2)))**0.5 return {'plus': h_plus, 'cross': h_cross} From 7b98e7ccf4b78609e5958012a3230a336c9e91d7 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Wed, 5 Feb 2025 12:35:52 -0600 Subject: [PATCH 024/140] ENH: make roq likelihood backend independent --- bilby/gw/likelihood/roq.py | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/bilby/gw/likelihood/roq.py b/bilby/gw/likelihood/roq.py index 46a88d09c..55aecd2a3 100644 --- a/bilby/gw/likelihood/roq.py +++ b/bilby/gw/likelihood/roq.py @@ -2,6 +2,7 @@ import numpy as np from .base import GravitationalWaveTransient +from ...compat.utils import array_module from ...core.utils import ( logger, create_frequency_series, speed_of_light, radius_of_earth ) @@ -456,8 +457,8 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array quadratic_indices = self.waveform_generator.waveform_arguments['quadratic_indices'] size_linear = len(linear_indices) size_quadratic = len(quadratic_indices) - h_linear = np.zeros(size_linear, dtype=complex) - h_quadratic = np.zeros(size_quadratic, dtype=complex) + h_linear = 0j + h_quadratic = 0j for mode in waveform_polarizations['linear']: response = interferometer.antenna_response( parameters['ra'], parameters['dec'], @@ -468,13 +469,14 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array h_linear += waveform_polarizations['linear'][mode] * response h_quadratic += waveform_polarizations['quadratic'][mode] * response + xp = array_module(h_linear) calib_factor = interferometer.calibration_model.get_calibration_factor( frequency_nodes, prefix='recalib_{}_'.format(interferometer.name), **parameters) h_linear *= calib_factor[linear_indices] h_quadratic *= calib_factor[quadratic_indices] - optimal_snr_squared = np.vdot( - np.abs(h_quadratic)**2, + optimal_snr_squared = xp.vdot( + xp.abs(h_quadratic)**2, self.weights[interferometer.name + '_quadratic'][self.basis_number_quadratic] ) @@ -485,20 +487,18 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array indices, in_bounds = self._closest_time_indices( ifo_time, self.weights['time_samples']) - if not in_bounds: - logger.debug("SNR calculation error: requested time at edge of ROQ time samples") - d_inner_h = -np.inf - complex_matched_filter_snr = -np.inf - else: - d_inner_h_tc_array = np.einsum( - 'i,ji->j', np.conjugate(h_linear), - self.weights[interferometer.name + '_linear'][self.basis_number_linear][indices]) + d_inner_h_tc_array = xp.einsum( + 'i,ji->j', xp.conjugate(h_linear), + self.weights[interferometer.name + '_linear'][self.basis_number_linear][indices]) + + d_inner_h = self._interp_five_samples( + self.weights['time_samples'][indices], d_inner_h_tc_array, ifo_time) - d_inner_h = self._interp_five_samples( - self.weights['time_samples'][indices], d_inner_h_tc_array, ifo_time) + with np.errstate(invalid="ignore"): + complex_matched_filter_snr = d_inner_h / (optimal_snr_squared**0.5) - with np.errstate(invalid="ignore"): - complex_matched_filter_snr = d_inner_h / (optimal_snr_squared**0.5) + d_inner_h += xp.log(in_bounds) + complex_matched_filter_snr += xp.log(in_bounds) if return_array and self.time_marginalization: ifo_times = self._times - interferometer.strain_data.start_time @@ -535,10 +535,11 @@ def _closest_time_indices(time, samples): in_bounds: bool Whether the indices are for valid times """ - closest = int((time - samples[0]) / (samples[1] - samples[0])) + xp = array_module(time) + closest = xp.floor((time - samples[0]) / (samples[1] - samples[0])) indices = [closest + ii for ii in [-2, -1, 0, 1, 2]] in_bounds = (indices[0] >= 0) & (indices[-1] < samples.size) - return indices, in_bounds + return xp.asarray(indices).astype(int), in_bounds @staticmethod def _interp_five_samples(time_samples, values, time): From 221cd5707b04fae7ddb999f78495ad9e2d676027 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Wed, 5 Feb 2025 12:39:23 -0600 Subject: [PATCH 025/140] BUG: fix roq slicing --- bilby/gw/likelihood/roq.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/bilby/gw/likelihood/roq.py b/bilby/gw/likelihood/roq.py index 55aecd2a3..a74e194b1 100644 --- a/bilby/gw/likelihood/roq.py +++ b/bilby/gw/likelihood/roq.py @@ -488,11 +488,16 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array indices, in_bounds = self._closest_time_indices( ifo_time, self.weights['time_samples']) d_inner_h_tc_array = xp.einsum( - 'i,ji->j', xp.conjugate(h_linear), - self.weights[interferometer.name + '_linear'][self.basis_number_linear][indices]) + 'i,ji->j', + xp.conjugate(h_linear), + xp.asarray( + self.weights[interferometer.name + '_linear'][self.basis_number_linear] + )[indices], + ) d_inner_h = self._interp_five_samples( - self.weights['time_samples'][indices], d_inner_h_tc_array, ifo_time) + xp.asarray(self.weights['time_samples'])[indices], d_inner_h_tc_array, ifo_time + ) with np.errstate(invalid="ignore"): complex_matched_filter_snr = d_inner_h / (optimal_snr_squared**0.5) From a9d34909edfa2561b20e23dc26178c151fa4a7cb Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Tue, 3 Jun 2025 10:15:55 -0500 Subject: [PATCH 026/140] FEAT: make condition chi evaluable --- bilby/gw/prior.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/bilby/gw/prior.py b/bilby/gw/prior.py index e262eaaf3..04c1d0db8 100644 --- a/bilby/gw/prior.py +++ b/bilby/gw/prior.py @@ -7,6 +7,7 @@ from scipy.special import hyp2f1 from scipy.stats import norm +from ..compat.utils import xp_wrap from ..core.prior import ( PriorDict, Uniform, Prior, DeltaFunction, Gaussian, Interped, Constraint, conditional_prior_factory, PowerLaw, ConditionalLogUniform, @@ -600,19 +601,23 @@ def __init__(self, minimum, maximum, name, latex_label=None, unit=None, boundary self.__class__.__name__ = "ConditionalChiInPlane" self.__class__.__qualname__ = "ConditionalChiInPlane" - def prob(self, val, **required_variables): - self.update_conditions(**required_variables) + @xp_wrap + def prob(self, val, *, xp=np, **required_variables): + parameters = self.condition_func(self.reference_params.copy(), **required_variables) chi_aligned = abs(required_variables[self._required_variables[0]]) + minimum = parameters.get("minimum", self.minimum) + maximum = parameters.get("maximum", self.maximum) return ( - (val >= self.minimum) * (val <= self.maximum) + (val >= minimum) * (val <= maximum) * val / (chi_aligned ** 2 + val ** 2) - / np.log(self._reference_maximum / chi_aligned) + / xp.log(self._reference_maximum / chi_aligned) ) - def ln_prob(self, val, **required_variables): + @xp_wrap + def ln_prob(self, val, *, xp=np, **required_variables): with np.errstate(divide="ignore"): - return np.log(self.prob(val, **required_variables)) + return xp.log(self.prob(val, **required_variables)) def cdf(self, val, **required_variables): r""" @@ -664,9 +669,9 @@ def rescale(self, val, **required_variables): def _condition_function(self, reference_params, **kwargs): with np.errstate(invalid="ignore"): - maximum = np.sqrt( + maximum = ( self._reference_maximum ** 2 - kwargs[self._required_variables[0]] ** 2 - ) + )**0.5 return dict(minimum=0, maximum=maximum) def __repr__(self): From 93fefb930af4b9acaf6643aeb115979a515e2d80 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Thu, 12 Jun 2025 12:22:58 -0500 Subject: [PATCH 027/140] MAINT: make whitening work for non-numpy --- bilby/gw/detector/interferometer.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/bilby/gw/detector/interferometer.py b/bilby/gw/detector/interferometer.py index ac98d8b82..a85c8ffd7 100644 --- a/bilby/gw/detector/interferometer.py +++ b/bilby/gw/detector/interferometer.py @@ -498,7 +498,7 @@ def inject_signal_from_waveform_polarizations(self, parameters, injection_polari self.strain_data.frequency_domain_strain += signal_ifo self.meta_data['optimal_SNR'] = ( - np.sqrt(self.optimal_snr_squared(signal=signal_ifo)).real) + self.optimal_snr_squared(signal=signal_ifo)).real ** 0.5 self.meta_data['matched_filter_SNR'] = ( self.matched_filter_snr(signal=signal_ifo)) self.meta_data['parameters'] = parameters @@ -684,7 +684,7 @@ 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)) + return frequency_series / (self.amplitude_spectral_density_array * (self.duration / 4)**0.5) def get_whitened_time_series_from_whitened_frequency_series( self, @@ -715,14 +715,13 @@ def get_whitened_time_series_from_whitened_frequency_series( w = \\sqrt{N W} = \\sqrt{\\sum_{k=0}^N \\Theta(f_{max} - f_k)\\Theta(f_k - f_{min})} """ - frequency_window_factor = ( - np.sum(self.frequency_mask) - / len(self.frequency_mask) - ) + xp = array_module(whitened_frequency_series) + + frequency_window_factor = self.frequency_mask.mean() whitened_time_series = ( - np.fft.irfft(whitened_frequency_series) - * np.sqrt(np.sum(self.frequency_mask)) / frequency_window_factor + xp.fft.irfft(whitened_frequency_series) + * self.frequency_mask.sum()**0.5 / frequency_window_factor ) return whitened_time_series From 997d094caca7accd67f09002eb7686708e7b9674 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Wed, 20 Aug 2025 06:35:42 -0700 Subject: [PATCH 028/140] EXAMPLE: update jax example --- .../injection_examples/jax_fast_tutorial.py | 274 ++++++------------ 1 file changed, 86 insertions(+), 188 deletions(-) diff --git a/examples/gw_examples/injection_examples/jax_fast_tutorial.py b/examples/gw_examples/injection_examples/jax_fast_tutorial.py index b1b586ecf..6bfd87ea8 100644 --- a/examples/gw_examples/injection_examples/jax_fast_tutorial.py +++ b/examples/gw_examples/injection_examples/jax_fast_tutorial.py @@ -17,6 +17,7 @@ import bilby import bilby.gw.jaxstuff +import numpy as np import jax import jax.numpy as jnp from jax import random @@ -28,10 +29,58 @@ bilby.core.utils.setup_logger() # log_level="WARNING") -def main(use_jax, model): +def setup_prior(): + # Set up a PriorDict, which inherits from dict. + # By default we will sample all terms in the signal models. However, this will + # take a long time for the calculation, so for this example we will set almost + # all of the priors to be equall to their injected values. This implies the + # prior is a delta function at the true, injected value. In reality, the + # sampler implementation is smart enough to not sample any parameter that has + # a delta-function prior. + # The above list does *not* include mass_1, mass_2, theta_jn and luminosity + # distance, which means those are the parameters that will be included in the + # sampler. If we do nothing, then the default priors get used. + priors = bilby.gw.prior.BBHPriorDict() + del priors["mass_1"], priors["mass_2"] + priors["geocent_time"] = bilby.core.prior.Uniform(1126249642, 1126269642) + priors["luminosity_distance"].minimum = 1 + priors["luminosity_distance"].maximum = 500 + priors["chirp_mass"].minimum = 2.35 + priors["chirp_mass"].maximum = 2.45 + # priors["luminosity_distance"] = bilby.core.prior.PowerLaw(2.0, 10.0, 500.0) + # priors["sky_x"] = bilby.core.prior.Normal(mu=0, sigma=1) + # priors["sky_y"] = bilby.core.prior.Normal(mu=0, sigma=1) + # priors["sky_z"] = bilby.core.prior.Normal(mu=0, sigma=1) + # priors["delta_phase"] = priors.pop("phase") + # del priors["tilt_1"], priors["tilt_2"], priors["phi_12"], priors["phi_jl"] + # priors["spin_1_x"] = bilby.core.prior.Normal(mu=0, sigma=1) + # priors["spin_1_y"] = bilby.core.prior.Normal(mu=0, sigma=1) + # priors["spin_1_z"] = bilby.core.prior.Normal(mu=0, sigma=1) + # priors["spin_2_x"] = bilby.core.prior.Normal(mu=0, sigma=1) + # priors["spin_2_y"] = bilby.core.prior.Normal(mu=0, sigma=1) + # priors["spin_2_z"] = bilby.core.prior.Normal(mu=0, sigma=1) + # # del priors["a_1"], priors["a_2"] + # # priors["chi_1"] = bilby.core.prior.Uniform(-0.05, 0.05) + # # priors["chi_2"] = bilby.core.prior.Uniform(-0.05, 0.05) + # del priors["theta_jn"], priors["psi"], priors["delta_phase"] + # priors["orientation_w"] = bilby.core.prior.Normal(mu=0, sigma=1) + # priors["orientation_x"] = bilby.core.prior.Normal(mu=0, sigma=1) + # priors["orientation_y"] = bilby.core.prior.Normal(mu=0, sigma=1) + # priors["orientation_z"] = bilby.core.prior.Normal(mu=0, sigma=1) + return priors + + +def original_to_sampling_priors(priors, truth): + del priors["ra"], priors["dec"] + priors["zenith"] = bilby.core.prior.Cosine() + priors["azimuth"] = bilby.core.prior.Uniform(minimum=0, maximum=2 * np.pi) + priors["L1_time"] = bilby.core.prior.Uniform(truth["geocent_time"] - 0.1, truth["geocent_time"] + 0.1) + + +def main(use_jax, model, idx): # Set the duration and sampling frequency of the data segment that we're # going to inject the signal into - duration = 4.0 + duration = 64.0 sampling_frequency = 2048.0 minimum_frequency = 20.0 if use_jax: @@ -40,35 +89,17 @@ def main(use_jax, model): minimum_frequency = jax.numpy.array(minimum_frequency) # Specify the output directory and the name of the simulation. - outdir = "outdir" - label = f"{model}_{'jax' if use_jax else 'numpy'}" + outdir = "pp-test-2" + label = f"{model}_{'jax' if use_jax else 'numpy'}_{idx}" # Set up a random seed for result reproducibility. This is optional! - bilby.core.utils.random.seed(88170235) + bilby.core.utils.random.seed(88170235 + idx * 1000) - # We are going to inject a binary black hole waveform. We first establish a - # dictionary of parameters that includes all of the different waveform - # parameters, including masses of the two black holes (mass_1, mass_2), - # spins of both black holes (a, tilt, phi), etc. - injection_parameters = dict( - mass_1=36.0, - mass_2=29.0, - a_1=0.4, - a_2=0.3, - tilt_1=0.5, - tilt_2=1.0, - phi_12=1.7, - phi_jl=0.3, - luminosity_distance=2000.0, - theta_jn=0.4, - psi=2.659, - phase=1.3, - geocent_time=1126259642.413, - ra=1.375, - dec=-1.2108, - ) + priors = setup_prior() + injection_parameters = priors.sample() if model == "relbin": injection_parameters["fiducial"] = 1 + original_to_sampling_priors(priors, injection_parameters) # Fixed arguments passed into the source model waveform_arguments = dict( @@ -89,13 +120,14 @@ def main(use_jax, model): fdsm = bilby.gw.source.lal_binary_black_hole_relative_binning case _: fdsm = bilby.gw.source.lal_binary_black_hole + # fdsm = bilby.gw.source.sinegaussian # Create the waveform_generator using a LAL BinaryBlackHole source function waveform_generator = bilby.gw.WaveformGenerator( duration=duration, sampling_frequency=sampling_frequency, frequency_domain_source_model=fdsm, - parameter_conversion=bilby.gw.conversion.convert_to_lal_binary_black_hole_parameters, + # parameter_conversion=bilby.gw.conversion.convert_to_lal_binary_black_hole_parameters, waveform_arguments=waveform_arguments, use_cache=not use_jax, ) @@ -107,10 +139,11 @@ def main(use_jax, model): ifos.set_strain_data_from_power_spectral_densities( sampling_frequency=sampling_frequency, duration=duration, - start_time=injection_parameters["geocent_time"] - 2, + start_time=injection_parameters["geocent_time"] - duration + 2, ) ifos.inject_signal( - waveform_generator=waveform_generator, parameters=injection_parameters + waveform_generator=waveform_generator, parameters=injection_parameters, + raise_error=False, ) if use_jax: ifos.set_array_backend(jax.numpy) @@ -124,58 +157,6 @@ def main(use_jax, model): ) del waveform_generator.waveform_arguments["minimum_frequency"] - # Set up a PriorDict, which inherits from dict. - # By default we will sample all terms in the signal models. However, this will - # take a long time for the calculation, so for this example we will set almost - # all of the priors to be equall to their injected values. This implies the - # prior is a delta function at the true, injected value. In reality, the - # sampler implementation is smart enough to not sample any parameter that has - # a delta-function prior. - # The above list does *not* include mass_1, mass_2, theta_jn and luminosity - # distance, which means those are the parameters that will be included in the - # sampler. If we do nothing, then the default priors get used. - priors = bilby.gw.prior.BBHPriorDict() - for key in [ - # "a_1", - # "a_2", - # "tilt_1", - # "tilt_2", - # "phi_12", - # "phi_jl", - # "psi", - # "ra", - # "dec", - # "geocent_time", - ]: - priors[key] = injection_parameters[key] - del priors["mass_1"], priors["mass_2"] - priors["L1_time"] = bilby.core.prior.Uniform(1126259642.41, 1126259642.45) - del priors["ra"], priors["dec"] - # priors["zenith"] = bilby.core.prior.Cosine() - # priors["azimuth"] = bilby.core.prior.Uniform(minimum=0, maximum=2 * np.pi) - priors["sky_x"] = bilby.core.prior.Normal(mu=0, sigma=1) - priors["sky_y"] = bilby.core.prior.Normal(mu=0, sigma=1) - priors["sky_z"] = bilby.core.prior.Normal(mu=0, sigma=1) - priors["delta_phase"] = priors.pop("phase") - priors["chirp_mass"].minimum = 20 - priors["chirp_mass"].maximum = 35 - del priors["tilt_1"], priors["tilt_2"], priors["phi_12"], priors["phi_jl"] - priors["spin_1_x"] = bilby.core.prior.Normal(mu=0, sigma=1) - priors["spin_1_y"] = bilby.core.prior.Normal(mu=0, sigma=1) - priors["spin_1_z"] = bilby.core.prior.Normal(mu=0, sigma=1) - priors["spin_2_x"] = bilby.core.prior.Normal(mu=0, sigma=1) - priors["spin_2_y"] = bilby.core.prior.Normal(mu=0, sigma=1) - priors["spin_2_z"] = bilby.core.prior.Normal(mu=0, sigma=1) - del priors["theta_jn"], priors["psi"], priors["delta_phase"] - priors["orientation_w"] = bilby.core.prior.Normal(mu=0, sigma=1) - priors["orientation_x"] = bilby.core.prior.Normal(mu=0, sigma=1) - priors["orientation_y"] = bilby.core.prior.Normal(mu=0, sigma=1) - priors["orientation_z"] = bilby.core.prior.Normal(mu=0, sigma=1) - - # Perform a check that the prior does not extend to a parameter space longer than the data - if not use_jax: - priors.validate_prior(duration, minimum_frequency) - # Initialise the likelihood by passing in the interferometer data (ifos) and # the waveform generator match model: @@ -191,130 +172,47 @@ def main(use_jax, model): interferometers=ifos, waveform_generator=waveform_generator, priors=priors, - # phase_marginalization=True, + phase_marginalization=True, + distance_marginalization=True, reference_frame=ifos, time_reference="L1", + # epsilon=0.1, + # update_fiducial_parameters=True, ) - if use_jax: - - def sample(): - parameters = priors.sample() - parameters = {key: jax.numpy.array(val) for key, val in parameters.items()} - return parameters - - # burn a few likelihood calls to check that we don't get - # repeated compilation - likelihood.parameters.update(sample()) - likelihood.log_likelihood_ratio() - likelihood.log_likelihood() - likelihood.noise_log_likelihood() - - with jax.log_compiles(): - jit_likelihood = bilby.gw.jaxstuff.JittedLikelihood( - likelihood, - cast_to_float=False, - ) - jit_likelihood.parameters.update(sample()) - jit_likelihood.log_likelihood_ratio() - jit_likelihood.log_likelihood() - jit_likelihood.noise_log_likelihood() - jit_likelihood.parameters.update(sample()) - jit_likelihood.log_likelihood_ratio() - jit_likelihood.log_likelihood() - jit_likelihood.noise_log_likelihood() - sample_likelihood = jit_likelihood - else: - sample_likelihood = likelihood - - def likelihood_func(parameters): - return sample_likelihood.likelihood_func(parameters, **sample_likelihood.kwargs) - - # import IPython; IPython.embed() - # raise SystemExit() - # use the log_compiles context so we can make sure there aren't recompilations # inside the sampling loop - with jax.log_compiles(): + if True: + # with jax.log_compiles(): result = bilby.run_sampler( - likelihood=sample_likelihood, + likelihood=likelihood, priors=priors, - sampler="dynesty", - # sampler="numpyro", - sampler_name="ESS", - # sampler_name="NUTS", - num_warmup=500, - num_samples=500, - num_chains=100, - thinning=5, - # moves={ - # AIES.DEMove(): 0.35, - # ModeHopping(): 0.3, - # AIES.StretchMove(): 0.35, - # }, - moves={ - ESS.DifferentialMove(): 0.25, - ESS.KDEMove(): 0.25, - ESS.GaussianMove(): 0.5, - }, - chain_method="vectorized", - npoints=500, - # sample="acceptance-walk", - sample="act-walk", - naccept=10, + sampler="jaxted" if use_jax else "dynesty", + nlive=1000, + sample="acceptance-walk", + method="nest", + nsteps=100, + naccept=30, injection_parameters=injection_parameters, outdir=outdir, label=label, - npool=4, + npool=None if use_jax else 16, + # save="hdf5", + save=False, + rseed=np.random.randint(0, 100000), ) - # print(result) - # print(f"Sampling time: {result.sampling_time:.1f}s\n") # Make a corner plot. - result.plot_corner() - raise SystemExit() + # result.plot_corner() + import IPython; IPython.embed() return result.sampling_time -def ModeHopping(): - """ - A proposal using differential evolution. - - This `Differential evolution proposal - `_ is - implemented following `Nelson et al. (2013) - `_. - - :param sigma: (optional) - The standard deviation of the Gaussian used to stretch the proposal vector. - Defaults to `1.0.e-5`. - :param g0 (optional): - The mean stretch factor for the proposal vector. By default, - it is `2.38 / sqrt(2*ndim)` as recommended by the two references. - """ - - def make_de_move(n_chains): - PAIRS = get_nondiagonal_indices(n_chains // 2) - - def de_move(rng_key, active, inactive): - n_active_chains, _ = inactive.shape - - selected_pairs = random.choice(rng_key, PAIRS, shape=(n_active_chains,)) - - # Compute diff vectors - diffs = jnp.diff(inactive[selected_pairs], axis=1).squeeze(axis=1) - - proposal = active + diffs - - return proposal, jnp.zeros(n_active_chains) - - return de_move - - return make_de_move - - if __name__ == "__main__": times = dict() - for arg in product([True, False][1:], ["relbin", "mb", "regular"][1:2]): - times[arg] = main(*arg) + # for arg in product([True, False][:], ["relbin", "mb", "regular"][2:3]): + # times[arg] = main(*arg) + with jax.log_compiles(): + for idx in np.arange(100): + times[idx] = main(True, "mb", idx) print(times) From 2a6d0f0309c700a224b28680ec2cb1ca5b1170b4 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Wed, 20 Aug 2025 09:48:20 -0700 Subject: [PATCH 029/140] BUG: fix interpax interpolation method --- bilby/core/utils/calculus.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bilby/core/utils/calculus.py b/bilby/core/utils/calculus.py index bb89f48ee..f20973f4e 100644 --- a/bilby/core/utils/calculus.py +++ b/bilby/core/utils/calculus.py @@ -262,6 +262,7 @@ def _call_jax(self, x, y): jnp.asarray(self.y), jnp.asarray(self.z), extrap=self.fill_value, + method="cubic2", ) From 590de47844c62396da7fc8fed544486b036ff3d2 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Thu, 2 Oct 2025 16:06:10 +0000 Subject: [PATCH 030/140] REFACTOR: update variable backend for new parameter method --- bilby/compat/jax.py | 8 ++------ bilby/gw/detector/interferometer.py | 1 + bilby/gw/likelihood/base.py | 1 - requirements.txt | 2 ++ 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/bilby/compat/jax.py b/bilby/compat/jax.py index 8d297487b..7c35aa6f3 100644 --- a/bilby/compat/jax.py +++ b/bilby/compat/jax.py @@ -2,7 +2,7 @@ import jax import jax.numpy as jnp -from ..core.likelihood import Likelihood +from ..core.likelihood import Likelihood, _safe_likelihood_call def generic_bilby_likelihood_function(likelihood, parameters, use_ratio=True): @@ -20,11 +20,7 @@ def generic_bilby_likelihood_function(likelihood, parameters, use_ratio=True): Default is :code:`True`. """ parameters = {k: jnp.array(v) for k, v in parameters.items()} - likelihood.parameters.update(parameters) - if use_ratio: - return likelihood.log_likelihood_ratio() - else: - return likelihood.log_likelihood() + return _safe_likelihood_call(likelihood, parameters, use_ratio) class JittedLikelihood(Likelihood): diff --git a/bilby/gw/detector/interferometer.py b/bilby/gw/detector/interferometer.py index a85c8ffd7..774f26315 100644 --- a/bilby/gw/detector/interferometer.py +++ b/bilby/gw/detector/interferometer.py @@ -4,6 +4,7 @@ from ...core import utils from ...core.utils import docstring, logger, PropertyAccessor, safe_file_dump +from ...core.utils.env import string_to_boolean from ...compat.utils import array_module from .. import utils as gwutils from ..geometry import ( diff --git a/bilby/gw/likelihood/base.py b/bilby/gw/likelihood/base.py index 9f4869d11..73aa65658 100644 --- a/bilby/gw/likelihood/base.py +++ b/bilby/gw/likelihood/base.py @@ -412,7 +412,6 @@ def noise_log_likelihood(self): def log_likelihood_ratio(self, parameters): parameters = copy.deepcopy(parameters) - parameters.update(self.get_sky_frame_parameters(parameters)) waveform_polarizations = \ self.waveform_generator.frequency_domain_strain(parameters) diff --git a/requirements.txt b/requirements.txt index b045db212..2b3cc4405 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,3 +12,5 @@ tqdm h5py attrs importlib-metadata>=3.6; python_version < '3.10' +plum-dispatch +array_api_compat From c7e7525ed3e2acd761f783f655fec10ad2a5658e Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Thu, 2 Oct 2025 19:38:44 +0000 Subject: [PATCH 031/140] some simplifications of array transparency --- bilby/compat/jax.py | 58 +++++++++-------------------- bilby/compat/utils.py | 2 +- bilby/core/likelihood.py | 40 ++++++++++++-------- bilby/core/prior/analytical.py | 48 ++++++++++++++---------- bilby/gw/detector/geometry.py | 10 +++++ bilby/gw/detector/interferometer.py | 18 +++------ bilby/gw/detector/networks.py | 4 ++ bilby/gw/likelihood/base.py | 12 +++--- bilby/gw/likelihood/basic.py | 18 +++++---- bilby/gw/sampler/proposal.py | 2 +- bilby/gw/source.py | 7 ++-- 11 files changed, 112 insertions(+), 107 deletions(-) diff --git a/bilby/compat/jax.py b/bilby/compat/jax.py index 7c35aa6f3..f94f64cd8 100644 --- a/bilby/compat/jax.py +++ b/bilby/compat/jax.py @@ -1,26 +1,6 @@ -from functools import partial - import jax import jax.numpy as jnp -from ..core.likelihood import Likelihood, _safe_likelihood_call - - -def generic_bilby_likelihood_function(likelihood, parameters, use_ratio=True): - """ - A wrapper to allow a :code:`Bilby` likelihood to be used with :code:`jax`. - - Parameters - ========== - likelihood: bilby.core.likelihood.Likelihood - The likelihood to evaluate. - parameters: dict - The parameters to evaluate the likelihood at. - use_ratio: bool, optional - Whether to evaluate the likelihood ratio or the full likelihood. - Default is :code:`True`. - """ - parameters = {k: jnp.array(v) for k, v in parameters.items()} - return _safe_likelihood_call(likelihood, parameters, use_ratio) +from ..core.likelihood import Likelihood class JittedLikelihood(Likelihood): @@ -36,34 +16,30 @@ class JittedLikelihood(Likelihood): ========== likelihood: bilby.core.likelihood.Likelihood The likelihood to wrap. - likelihood_func: callable, optional - The function to use to evaluate the likelihood. Default is - :code:`generic_bilby_likelihood_function`. This function should take the - likelihood and parameters as arguments along with additional keyword arguments. - kwargs: dict, optional - Additional keyword arguments to pass to the likelihood function. + cast_to_float: bool + Whether to return a float instead of a :code:`jax.Array`. """ - def __init__( - self, - likelihood, - likelihood_func=generic_bilby_likelihood_function, - kwargs=None, - cast_to_float=True, - ): - if kwargs is None: - kwargs = dict() - self.kwargs = kwargs + def __init__(self, likelihood, cast_to_float=True): self._likelihood = likelihood - self.likelihood_func = jax.jit(partial(likelihood_func, likelihood)) + self._ll = jax.jit(likelihood.log_likelihood) + self._llr = jax.jit(likelihood.log_likelihood_ratio) self.cast_to_float = cast_to_float - super().__init__(dict()) + super().__init__() def __getattr__(self, name): return getattr(self._likelihood, name) - def log_likelihood_ratio(self): - ln_l = jnp.nan_to_num(self.likelihood_func(self.parameters, **self.kwargs)) + def log_likelihood(self, parameters): + parameters = {k: jnp.array(v) for k, v in parameters.items()} + ln_l = self._ll(parameters) + if self.cast_to_float: + ln_l = float(ln_l) + return ln_l + + def log_likelihood_ratio(self, parameters): + parameters = {k: jnp.array(v) for k, v in parameters.items()} + ln_l = self._llr(parameters) if self.cast_to_float: ln_l = float(ln_l) return ln_l diff --git a/bilby/compat/utils.py b/bilby/compat/utils.py index bd97f828e..b19473e92 100644 --- a/bilby/compat/utils.py +++ b/bilby/compat/utils.py @@ -1,5 +1,5 @@ import numpy as np -from scipy._lib._array_api import array_namespace +from array_api_compat import array_namespace __all__ = ["array_module", "promote_to_array"] diff --git a/bilby/core/likelihood.py b/bilby/core/likelihood.py index 73b59a7ac..613b68806 100644 --- a/bilby/core/likelihood.py +++ b/bilby/core/likelihood.py @@ -1,6 +1,7 @@ import copy import numpy as np +from array_api_compat import is_array_api_obj from scipy.special import gammaln, xlogy from scipy.stats import multivariate_normal @@ -196,9 +197,10 @@ def __init__(self, x, y, func, sigma=None, **kwargs): self.sigma = sigma def log_likelihood(self, parameters): + xp = array_module(self.x) sigma = parameters.get("sigma", self.sigma) - log_l = np.sum(- (self.residual(parameters) / sigma)**2 / 2 - - np.log(2 * np.pi * sigma**2) / 2) + log_l = xp.sum(- (self.residual(parameters) / sigma)**2 / 2 - + xp.log(2 * np.pi * sigma**2) / 2) return log_l def __repr__(self): @@ -254,17 +256,18 @@ def __init__(self, x, y, func, **kwargs): def log_likelihood(self, parameters): rate = self.func(self.x, **self.model_parameters(parameters=parameters), **self.kwargs) - if not isinstance(rate, np.ndarray): + if not is_array_api_obj(rate): raise ValueError( "Poisson rate function returns wrong value type! " "Is {} when it should be numpy.ndarray".format(type(rate))) - elif np.any(rate < 0.): + elif any(rate < 0.): raise ValueError(("Poisson rate function returns a negative", " value!")) - elif np.any(rate == 0.): + elif any(rate == 0.): return -np.inf else: - return np.sum(-rate + self.y * np.log(rate) - gammaln(self.y + 1)) + xp = array_module(rate) + return xp.sum(-rate + self.y * xp.log(rate) - gammaln(self.y + 1)) def __repr__(self): return Analytical1DLikelihood.__repr__(self) @@ -305,9 +308,10 @@ def __init__(self, x, y, func, **kwargs): def log_likelihood(self, parameters): mu = self.func(self.x, **self.model_parameters(parameters=parameters), **self.kwargs) - if np.any(mu < 0.): + if any(mu < 0.): return -np.inf - return -np.sum(np.log(mu) + (self.y / mu)) + xp = array_module(mu) + return -xp.sum(xp.log(mu) + (self.y / mu)) def __repr__(self): return Analytical1DLikelihood.__repr__(self) @@ -321,7 +325,7 @@ def y(self): def y(self, y): if not isinstance(y, np.ndarray): y = np.array([y]) - if np.any(y < 0): + if any(y < 0): raise ValueError("Data must be non-negative") self._y = y @@ -367,9 +371,10 @@ def log_likelihood(self, parameters): raise ValueError("Number of degrees of freedom for Student's " "t-likelihood must be positive") + xp = array_module(self.x) log_l =\ - np.sum(- (nu + 1) * np.log1p(self.lam * self.residual(parameters=parameters)**2 / nu) / 2 + - np.log(self.lam / (nu * np.pi)) / 2 + + xp.sum(- (nu + 1) * xp.log1p(self.lam * self.residual(parameters=parameters)**2 / nu) / 2 + + xp.log(self.lam / (nu * np.pi)) / 2 + gammaln((nu + 1) / 2) - gammaln(nu / 2)) return log_l @@ -413,8 +418,10 @@ def __init__(self, data, n_dimensions, base="parameter_"): base: str The base of the parameter labels """ - self.data = np.array(data) - self._total = np.sum(self.data) + if not is_array_api_obj(data): + data = np.array(data) + self.data = data + self._total = self.data.sum() super(Multinomial, self).__init__() self.n = n_dimensions self.base = base @@ -449,7 +456,8 @@ def noise_log_likelihood(self): def _multinomial_ln_pdf(self, probs): """Lifted from scipy.stats.multinomial._logpdf""" - ln_prob = gammaln(self._total + 1) + np.sum( + xp = array_module(self.data) + ln_prob = gammaln(self._total + 1) + xp.sum( xlogy(self.data, probs) - gammaln(self.data + 1), axis=-1) return ln_prob @@ -471,7 +479,7 @@ def __init__(self, mean, cov): xp = array_module(cov) self.cov = xp.atleast_2d(cov) self.mean = xp.atleast_1d(mean) - self.sigma = xp.sqrt(np.diag(self.cov)) + self.sigma = xp.sqrt(xp.diag(self.cov)) if xp == np: self.logpdf = multivariate_normal(mean=self.mean, cov=self.cov).logpdf else: @@ -508,7 +516,7 @@ class AnalyticalMultidimensionalBimodalCovariantGaussian(Likelihood): def __init__(self, mean_1, mean_2, cov): xp = array_module(cov) self.cov = xp.atleast_2d(cov) - self.sigma = xp.sqrt(np.diag(self.cov)) + self.sigma = xp.sqrt(xp.diag(self.cov)) self.mean_1 = xp.atleast_1d(mean_1) self.mean_2 = xp.atleast_1d(mean_2) if xp == np: diff --git a/bilby/core/prior/analytical.py b/bilby/core/prior/analytical.py index 876196ec6..755fd3f41 100644 --- a/bilby/core/prior/analytical.py +++ b/bilby/core/prior/analytical.py @@ -17,7 +17,7 @@ from .base import Prior from ..utils import logger -from ...compat.utils import xp_wrap +from ...compat.utils import array_module, xp_wrap class DeltaFunction(Prior): @@ -363,7 +363,7 @@ def ln_prob(self, val, *, xp=np): float: """ - return np.nan_to_num(- xp.log(2 * xp.abs(val)) - xp.log(xp.log(self.maximum / self.minimum))) + return xp.nan_to_num(- xp.log(2 * xp.abs(val)) - xp.log(xp.log(self.maximum / self.minimum))) @xp_wrap def cdf(self, val, *, xp=np): @@ -1055,7 +1055,8 @@ def rescale(self, val, *, xp=np): val = xp.asarray(val) return self.mu + self.scale * xp.log(xp.maximum(val / (1 - val), 0)) - def prob(self, val): + @xp_wrap + def prob(self, val, *, xp=np): """Return the prior probability of val. Parameters @@ -1066,7 +1067,7 @@ def prob(self, val): ======= Union[float, array_like]: Prior probability of val """ - return np.exp(self.ln_prob(val)) + return xp.exp(self.ln_prob(val)) @xp_wrap def ln_prob(self, val, *, xp=np): @@ -1082,10 +1083,11 @@ def ln_prob(self, val, *, xp=np): """ with np.errstate(over="ignore"): return -(val - self.mu) / self.scale -\ - 2. * np.log1p(xp.exp(-(val - self.mu) / self.scale)) - np.log(self.scale) + 2. * xp.log1p(xp.exp(-(val - self.mu) / self.scale)) - xp.log(self.scale) - def cdf(self, val): - return 1. / (1. + np.exp(-(val - self.mu) / self.scale)) + @xp_wrap + def cdf(self, val, *, xp=np): + return 1. / (1. + xp.exp(-(val - self.mu) / self.scale)) class Cauchy(Prior): @@ -1141,7 +1143,8 @@ def prob(self, val): """ return 1. / self.beta / np.pi / (1. + ((val - self.alpha) / self.beta) ** 2) - def ln_prob(self, val): + @xp_wrap + def ln_prob(self, val, *, xp=np): """Return the log prior probability of val. Parameters @@ -1152,10 +1155,11 @@ def ln_prob(self, val): ======= Union[float, array_like]: Log prior probability of val """ - return - np.log(self.beta * np.pi) - np.log(1. + ((val - self.alpha) / self.beta) ** 2) + return - xp.log(self.beta * np.pi) - xp.log(1. + ((val - self.alpha) / self.beta) ** 2) - def cdf(self, val): - return 0.5 + np.arctan((val - self.alpha) / self.beta) / np.pi + @xp_wrap + def cdf(self, val, *, xp=np): + return 0.5 + xp.arctan((val - self.alpha) / self.beta) / np.pi class Lorentzian(Cauchy): @@ -1323,9 +1327,11 @@ def __init__(self, sigma, mu=None, r=None, name=None, latex_label=None, raise ValueError("For the Fermi-Dirac prior the values of sigma and r " "must be positive.") - self.expr = np.exp(self.r) + xp = array_module(np) + self.expr = xp.exp(self.r) - def rescale(self, val): + @xp_wrap + def rescale(self, val, *, xp=np): """ 'Rescale' a sample from the unit line element to the appropriate Fermi-Dirac prior. @@ -1343,7 +1349,7 @@ def rescale(self, val): `_, 2017. """ inv = -1 / self.expr + (1 + self.expr)**-val + (1 + self.expr)**-val / self.expr - return -self.sigma * np.log(np.maximum(inv, 0)) + return -self.sigma * xp.log(xp.maximum(inv, 0)) @xp_wrap def prob(self, val, *, xp=np): @@ -1363,7 +1369,8 @@ def prob(self, val, *, xp=np): * (val >= self.minimum) ) - def ln_prob(self, val): + @xp_wrap + def ln_prob(self, val, *, xp=np): """Return the log prior probability of val. Parameters @@ -1374,9 +1381,10 @@ def ln_prob(self, val): ======= Union[float, array_like]: Log prior probability of val """ - return np.log(self.prob(val)) + return xp.log(self.prob(val)) - def cdf(self, val): + @xp_wrap + def cdf(self, val, *, xp=np): """ Evaluate the CDF of the Fermi-Dirac distribution using a slightly modified form of Equation 23 of [1]_. @@ -1398,10 +1406,10 @@ def cdf(self, val): `_, 2017. """ result = ( - (np.logaddexp(0, -self.r) - np.logaddexp(-val / self.sigma, -self.r)) - / np.logaddexp(0, self.r) + (xp.logaddexp(0, -self.r) - xp.logaddexp(-val / self.sigma, -self.r)) + / xp.logaddexp(0, self.r) ) - return np.clip(result, 0, 1) + return xp.clip(result, 0, 1) class WeightedDiscreteValues(Prior): diff --git a/bilby/gw/detector/geometry.py b/bilby/gw/detector/geometry.py index 627f6a143..5d0de9b9f 100644 --- a/bilby/gw/detector/geometry.py +++ b/bilby/gw/detector/geometry.py @@ -304,3 +304,13 @@ def unit_vector_along_arm(self, arm): ) else: raise ValueError("Arm must either be 'x' or 'y'.") + + def set_array_backend(self, xp): + self.length = xp.array(self.length) + self.latitude = xp.array(self.latitude) + self.longitude = xp.array(self.longitude) + self.elevation = xp.array(self.elevation) + self.xarm_azimuth = xp.array(self.xarm_azimuth) + self.yarm_azimuth = xp.array(self.yarm_azimuth) + self.xarm_tilt = xp.array(self.xarm_tilt) + self.yarm_tilt = xp.array(self.yarm_tilt) diff --git a/bilby/gw/detector/interferometer.py b/bilby/gw/detector/interferometer.py index 774f26315..8bc79e399 100644 --- a/bilby/gw/detector/interferometer.py +++ b/bilby/gw/detector/interferometer.py @@ -3,7 +3,7 @@ import numpy as np from ...core import utils -from ...core.utils import docstring, logger, PropertyAccessor, safe_file_dump +from ...core.utils import PropertyAccessor, docstring, logger, safe_file_dump from ...core.utils.env import string_to_boolean from ...compat.utils import array_module from .. import utils as gwutils @@ -942,14 +942,8 @@ def from_pickle(cls, filename=None): return res def set_array_backend(self, xp): - for attr in [ - "length", - "latitude", - "longitude", - "elevation", - "xarm_azimuth", - "yarm_azimuth", - "xarm_tilt", - "yarm_tilt", - ]: - setattr(self, attr, xp.array(getattr(self, attr))) + self.geometry.set_array_backend(xp=xp) + + @property + def array_backend(self): + return array_module(self.geometry.length) diff --git a/bilby/gw/detector/networks.py b/bilby/gw/detector/networks.py index 5b9072846..4ac52454a 100644 --- a/bilby/gw/detector/networks.py +++ b/bilby/gw/detector/networks.py @@ -346,6 +346,10 @@ def set_array_backend(self, xp): for ifo in self: ifo.set_array_backend(xp) + @property + def array_backend(self): + return self[0].array_backend + class TriangularInterferometer(InterferometerList): def __init__( diff --git a/bilby/gw/likelihood/base.py b/bilby/gw/likelihood/base.py index 73aa65658..7a8a39f75 100644 --- a/bilby/gw/likelihood/base.py +++ b/bilby/gw/likelihood/base.py @@ -6,7 +6,6 @@ import numpy as np from scipy.special import logsumexp -from ...compat.utils import array_module from ...core.likelihood import Likelihood from ...core.utils import logger, BoundedRectBivariateSpline, create_time_series from ...core.prior import Interped, Prior, Uniform, DeltaFunction @@ -158,6 +157,7 @@ def __init__( self.waveform_generator = waveform_generator super(GravitationalWaveTransient, self).__init__() self.interferometers = InterferometerList(interferometers) + self.interferometers.set_array_backend(interferometers.array_backend) self.time_marginalization = time_marginalization self.distance_marginalization = distance_marginalization self.phase_marginalization = phase_marginalization @@ -170,7 +170,7 @@ def __init__( if "geocent" not in time_reference: self.time_reference = time_reference self.reference_ifo = get_empty_interferometer(self.time_reference) - self.reference_ifo.set_array_backend(array_module(self.interferometers[0].vertex)) + self.reference_ifo.set_array_backend(self.interferometers.array_backend) if self.time_marginalization: logger.info("Cannot marginalise over non-geocenter time.") self.time_marginalization = False @@ -397,12 +397,12 @@ def _calculate_noise_log_likelihood(self): log_l = 0 for interferometer in self.interferometers: mask = interferometer.frequency_mask - log_l -= noise_weighted_inner_product( + log_l -= abs(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 - return float(np.real(log_l)) + self.waveform_generator.duration) / 2) + return log_l def noise_log_likelihood(self): # only compute likelihood if called for the 1st time @@ -1078,6 +1078,8 @@ def reference_frame(self, frame): self._reference_frame = InterferometerList([frame[:2], frame[2:4]]) else: raise ValueError("Unable to parse reference frame {}".format(frame)) + if isinstance(self._reference_frame, InterferometerList): + self._reference_frame.set_array_backend(self.interferometers.array_backend) def get_sky_frame_parameters(self, parameters): """ diff --git a/bilby/gw/likelihood/basic.py b/bilby/gw/likelihood/basic.py index da67481f0..35b5994a6 100644 --- a/bilby/gw/likelihood/basic.py +++ b/bilby/gw/likelihood/basic.py @@ -43,10 +43,11 @@ def noise_log_likelihood(self): """ log_l = 0 for interferometer in self.interferometers: - log_l -= 2. / self.waveform_generator.duration * np.sum( - abs(interferometer.frequency_domain_strain) ** 2 / - interferometer.power_spectral_density_array) - return log_l.real + log_l -= 2. / self.waveform_generator.duration * ( + abs(interferometer.frequency_domain_strain) ** 2 + / interferometer.power_spectral_density_array + ).sum() + return log_l def log_likelihood(self, parameters): """ Calculates the real part of log-likelihood value @@ -85,8 +86,9 @@ def log_likelihood_interferometer(self, waveform_polarizations, signal_ifo = interferometer.get_detector_response( waveform_polarizations, parameters) - log_l = - 2. / self.waveform_generator.duration * np.vdot( - interferometer.frequency_domain_strain - signal_ifo, - (interferometer.frequency_domain_strain - signal_ifo) / - interferometer.power_spectral_density_array) + residual = interferometer.frequency_domain_strain - signal_ifo + + log_l = - 2. / self.waveform_generator.duration * ( + abs(residual)**2 / interferometer.power_spectral_density_array + ).sum() return log_l.real diff --git a/bilby/gw/sampler/proposal.py b/bilby/gw/sampler/proposal.py index 79e1ec92c..2ac84687e 100644 --- a/bilby/gw/sampler/proposal.py +++ b/bilby/gw/sampler/proposal.py @@ -13,7 +13,7 @@ class SkyLocationWanderJump(JumpProposal): def __call__(self, sample, **kwargs): temperature = 1 / kwargs.get('inverse_temperature', 1.0) - sigma = np.sqrt(temperature) / 2 / np.pi + sigma = temperature**0.5 / 2 / np.pi sample['ra'] += random.gauss(0, sigma) sample['dec'] += random.gauss(0, sigma) return super(SkyLocationWanderJump, self).__call__(sample) diff --git a/bilby/gw/source.py b/bilby/gw/source.py index 11f441fba..21ba19eac 100644 --- a/bilby/gw/source.py +++ b/bilby/gw/source.py @@ -1204,7 +1204,7 @@ def sinegaussian(frequency_array, hrss, Q, frequency, **kwargs): h_cross = -1j * hrss * np.pi**0.5 * tau / 2 * ( negative_term - positive_term - ) / (temp * (1 - np.exp(-Q**2)))**0.5 + ) / (temp * (1 - xp.exp(-Q**2)))**0.5 return {'plus': h_plus, 'cross': h_cross} @@ -1287,12 +1287,13 @@ def supernova_pca_model( dict: The plus and cross polarizations of the signal """ + xp = array_module(frequency_array) principal_components = kwargs["realPCs"] + 1j * kwargs["imagPCs"] coefficients = [pc_coeff1, pc_coeff2, pc_coeff3, pc_coeff4, pc_coeff5] - strain = np.sum( - [coeff * principal_components[:, ii] for ii, coeff in enumerate(coefficients)], + strain = xp.sum( + xp.array([coeff * principal_components[:, ii] for ii, coeff in enumerate(coefficients)]), axis=0 ) From 8d99e5664472051ad9e3c2ae73beb57fb655ef35 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Thu, 2 Oct 2025 19:39:11 +0000 Subject: [PATCH 032/140] HYPER: make hyperparameter likelihood handle array backends --- bilby/hyper/likelihood.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/bilby/hyper/likelihood.py b/bilby/hyper/likelihood.py index 2ca63bbbd..fbde98f38 100644 --- a/bilby/hyper/likelihood.py +++ b/bilby/hyper/likelihood.py @@ -3,6 +3,7 @@ import numpy as np +from ..compat.utils import array_module from ..core.likelihood import Likelihood from .model import Model from ..core.prior import PriorDict @@ -29,11 +30,13 @@ class HyperparameterLikelihood(Likelihood): the sampling prior and the hyperparameterised model. max_samples: int, optional Maximum number of samples to use from each set. + xp: module + The array backend to use for the data. """ def __init__(self, posteriors, hyper_prior, sampling_prior=None, - log_evidences=None, max_samples=1e100): + log_evidences=None, max_samples=1e100, xp=np): if not isinstance(hyper_prior, Model): hyper_prior = Model([hyper_prior]) if sampling_prior is None: @@ -53,17 +56,18 @@ def __init__(self, posteriors, hyper_prior, sampling_prior=None, self.max_samples = max_samples super(HyperparameterLikelihood, self).__init__() - self.data = self.resample_posteriors() + self.data = self.resample_posteriors(xp=xp) self.n_posteriors = len(self.posteriors) self.samples_per_posterior = self.max_samples self.samples_factor =\ - self.n_posteriors * np.log(self.samples_per_posterior) def log_likelihood_ratio(self, parameters): - log_l = np.sum(np.log(np.sum(self.hyper_prior.prob(self.data, **parameters) / - self.data['prior'], axis=-1))) + probs = self.hyper_prior.prob(self.data, **parameters) + xp = array_module(probs) + log_l = xp.sum(xp.log(xp.sum(probs / self.data['prior'], axis=-1))) log_l += self.samples_factor - return np.nan_to_num(log_l) + return xp.nan_to_num(log_l) def noise_log_likelihood(self): return self.evidence_factor @@ -71,7 +75,7 @@ def noise_log_likelihood(self): def log_likelihood(self, parameters): return self.noise_log_likelihood() + self.log_likelihood_ratio(parameters=parameters) - def resample_posteriors(self, max_samples=None): + def resample_posteriors(self, max_samples=None, xp=np): """ Convert list of pandas DataFrame object to dict of arrays. @@ -106,5 +110,5 @@ def resample_posteriors(self, max_samples=None): for key in data: data[key].append(temp[key]) for key in data: - data[key] = np.array(data[key]) + data[key] = xp.array(data[key]) return data From 53fdb6db112e9052cafa3d7e05e759dd92cabefb Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Thu, 11 Dec 2025 15:46:40 +0000 Subject: [PATCH 033/140] MAINT: switch back to bilby_cython --- bilby/gw/geometry.py | 2 +- bilby/gw/time.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bilby/gw/geometry.py b/bilby/gw/geometry.py index 60d55f8be..8b0cf2855 100644 --- a/bilby/gw/geometry.py +++ b/bilby/gw/geometry.py @@ -1,6 +1,6 @@ import numpy as np from plum import dispatch -from bilby_rust import geometry as _geometry +from bilby_cython import geometry as _geometry from .time import greenwich_mean_sidereal_time from ..compat.types import Real, ArrayLike diff --git a/bilby/gw/time.py b/bilby/gw/time.py index cb56b3bab..29a2b9c77 100644 --- a/bilby/gw/time.py +++ b/bilby/gw/time.py @@ -2,7 +2,7 @@ import numpy as np from plum import dispatch -from bilby_rust import time as _time +from bilby_cython import time as _time from ..compat.types import Real, ArrayLike from ..compat.utils import array_module From cf2a033ebac85a16dafcf79f6383396fdbe4800d Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Mon, 22 Dec 2025 16:12:50 -0500 Subject: [PATCH 034/140] TYPO: fix typo in multiband time-marginalized likelihood --- bilby/gw/likelihood/multiband.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bilby/gw/likelihood/multiband.py b/bilby/gw/likelihood/multiband.py index 9331d9e94..a11ae7ab3 100644 --- a/bilby/gw/likelihood/multiband.py +++ b/bilby/gw/likelihood/multiband.py @@ -799,7 +799,7 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array start_idx, end_idx = self.start_end_idxs[b] self._full_d_h[self._full_to_multiband[start_idx:end_idx + 1]] += \ strain[start_idx:end_idx + 1] * self.linear_coeffs[interferometer.name][start_idx:end_idx + 1] - d_inner_h_array = np.fft.fft(self._full_d_h) + d_inner_h_array = xp.fft.fft(self._full_d_h) else: d_inner_h_array = None From 51b42e61c2add83f44c50bcfd23f91030ec4e00d Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Mon, 22 Dec 2025 16:13:05 -0500 Subject: [PATCH 035/140] MAINT: removed unused import --- bilby/gw/utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bilby/gw/utils.py b/bilby/gw/utils.py index 2457c5ea8..9c2cc8015 100644 --- a/bilby/gw/utils.py +++ b/bilby/gw/utils.py @@ -5,7 +5,6 @@ import numpy as np from scipy.interpolate import interp1d from scipy.special import i0e -from plum import dispatch from .geometry import zenith_azimuth_to_theta_phi from .time import greenwich_mean_sidereal_time From 0e8739c72aa1e1c7e292291790865ab3ecf76958 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Mon, 22 Dec 2025 16:13:24 -0500 Subject: [PATCH 036/140] BUG: add explicit array cast in conversion --- bilby/gw/conversion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bilby/gw/conversion.py b/bilby/gw/conversion.py index cedd67b18..74608c2ea 100644 --- a/bilby/gw/conversion.py +++ b/bilby/gw/conversion.py @@ -287,7 +287,7 @@ def convert_to_lal_binary_black_hole_parameters(parameters): converted_parameters['a_{}'.format(idx)] = abs( converted_parameters[key]) converted_parameters['cos_tilt_{}'.format(idx)] = \ - xp.sign(converted_parameters[key]) + xp.sign(xp.asarray(converted_parameters[key])) else: with np.errstate(invalid="raise"): try: From b1cb48ab8afd831c4462e0e7970f8c54ae0090ee Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Mon, 22 Dec 2025 16:13:56 -0500 Subject: [PATCH 037/140] REFACTOR: some refactoring of array edge cases --- bilby/compat/patches.py | 37 ++++++++++++++++++++++++++++++++++ bilby/compat/utils.py | 21 ++++++++++++++++--- bilby/core/likelihood.py | 32 ++++++++++++++--------------- bilby/core/prior/analytical.py | 29 ++++++++++++++------------ bilby/core/prior/dict.py | 12 +++++------ bilby/core/utils/calculus.py | 12 +++++++++-- 6 files changed, 103 insertions(+), 40 deletions(-) create mode 100644 bilby/compat/patches.py diff --git a/bilby/compat/patches.py b/bilby/compat/patches.py new file mode 100644 index 000000000..02cbc3394 --- /dev/null +++ b/bilby/compat/patches.py @@ -0,0 +1,37 @@ +import array_api_compat as aac + +from .utils import BackendNotImplementedError + + +def erfinv_import(xp): + if aac.is_numpy_namespace(xp): + from scipy.special import erfinv + elif aac.is_jax_namespace(xp): + from jax.scipy.special import erfinv + elif aac.is_torch_namespace(xp): + from torch.special import erfinv + elif aac.is_cupy_namespace(xp): + from cupyx.scipy.special import erfinv + else: + raise BackendNotImplementedError + return erfinv + + +def multivariate_logpdf(xp, mean, cov): + if aac.is_numpy_namespace(xp): + from scipy.stats import multivariate_normal + + logpdf = multivariate_normal(mean=mean, cov=cov).logpdf + elif aac.is_jax_namespace(xp): + from functools import partial + from jax.scipy.stats.multivariate_normal import logpdf + + logpdf = partial(logpdf, mean=mean, cov=cov) + elif aac.is_torch_namespace(xp): + from torch.distributions.multivariate_normal import MultivariateNormal + + mvn = MultivariateNormal(loc=mean, covariance_matrix=xp.array(cov)) + logpdf = mvn.log_prob + else: + raise BackendNotImplementedError + return logpdf diff --git a/bilby/compat/utils.py b/bilby/compat/utils.py index b19473e92..9f83ac9e5 100644 --- a/bilby/compat/utils.py +++ b/bilby/compat/utils.py @@ -1,14 +1,25 @@ import numpy as np from array_api_compat import array_namespace +from ..core.utils.log import logger + __all__ = ["array_module", "promote_to_array"] def array_module(arr): - if arr.__class__.__module__ == "builtins": - return np - else: + try: return array_namespace(arr) + except TypeError: + if arr.__class__.__module__ == "builtins": + return np + elif arr.__module__.startswith("pandas"): + return np + else: + logger.warning( + f"Unknown array module for type: {type(arr)} Defaulting to numpy." + ) + return np + def promote_to_array(args, backend, skip=None): @@ -32,3 +43,7 @@ def wrapped(self, *args, **kwargs): return func(self, *args, **kwargs) return wrapped + + +class BackendNotImplementedError(NotImplementedError): + pass diff --git a/bilby/core/likelihood.py b/bilby/core/likelihood.py index 613b68806..34acf806a 100644 --- a/bilby/core/likelihood.py +++ b/bilby/core/likelihood.py @@ -3,10 +3,10 @@ import numpy as np from array_api_compat import is_array_api_obj from scipy.special import gammaln, xlogy -from scipy.stats import multivariate_normal from .utils import infer_parameters_from_function, infer_args_from_function_except_n_args -from ..compat.utils import array_module +from ..compat.patches import multivariate_logpdf +from ..compat.utils import BackendNotImplementedError, array_module class Likelihood: @@ -480,12 +480,13 @@ def __init__(self, mean, cov): self.cov = xp.atleast_2d(cov) self.mean = xp.atleast_1d(mean) self.sigma = xp.sqrt(xp.diag(self.cov)) - if xp == np: - self.logpdf = multivariate_normal(mean=self.mean, cov=self.cov).logpdf - else: - from functools import partial - from jax.scipy.stats.multivariate_normal import logpdf - self.logpdf = partial(logpdf, mean=self.mean, cov=self.cov) + try: + self.logpdf = multivariate_logpdf(xp, mean=self.mean, cov=self.cov) + except BackendNotImplementedError: + raise NotImplementedError( + f"Multivariate normal likelihood not implemented for {xp.__name__} backend" + ) + parameters = {"x{0}".format(i): 0 for i in range(self.dim)} super(AnalyticalMultidimensionalCovariantGaussian, self).__init__(parameters=parameters) @@ -519,14 +520,13 @@ def __init__(self, mean_1, mean_2, cov): self.sigma = xp.sqrt(xp.diag(self.cov)) self.mean_1 = xp.atleast_1d(mean_1) self.mean_2 = xp.atleast_1d(mean_2) - if xp == np: - self.logpdf_1 = multivariate_normal(mean=self.mean_1, cov=self.cov).logpdf - self.logpdf_2 = multivariate_normal(mean=self.mean_2, cov=self.cov).logpdf - else: - from functools import partial - from jax.scipy.stats.multivariate_normal import logpdf - self.logpdf_1 = partial(logpdf, mean=self.mean_1, cov=self.cov) - self.logpdf_2 = partial(logpdf, mean=self.mean_2, cov=self.cov) + try: + self.logpdf_1 = multivariate_logpdf(xp, mean=self.mean_1, cov=self.cov) + self.logpdf_2 = multivariate_logpdf(xp, mean=self.mean_2, cov=self.cov) + except BackendNotImplementedError: + raise NotImplementedError( + f"Multivariate normal likelihood not implemented for {xp.__name__} backend" + ) parameters = {"x{0}".format(i): 0 for i in range(self.dim)} super(AnalyticalMultidimensionalBimodalCovariantGaussian, self).__init__(parameters=parameters) diff --git a/bilby/core/prior/analytical.py b/bilby/core/prior/analytical.py index 755fd3f41..dc418ed4a 100644 --- a/bilby/core/prior/analytical.py +++ b/bilby/core/prior/analytical.py @@ -17,7 +17,8 @@ from .base import Prior from ..utils import logger -from ...compat.utils import array_module, xp_wrap +from ...compat.patches import erfinv_import +from ...compat.utils import BackendNotImplementedError, array_module, xp_wrap class DeltaFunction(Prior): @@ -526,10 +527,10 @@ def rescale(self, val, *, xp=np): This maps to the inverse CDF. This has been analytically solved for this case. """ - if "jax" in xp.__name__: - from jax.scipy.special import erfinv - else: - from scipy.special import erfinv + try: + erfinv = erfinv_import(xp) + except BackendNotImplementedError: + raise NotImplementedError(f"Gaussian prior rescale not implemented for this {xp.__name__}") return self.mu + erfinv(2 * val - 1) * 2 ** 0.5 * self.sigma @xp_wrap @@ -618,10 +619,12 @@ def rescale(self, val, *, xp=np): This maps to the inverse CDF. This has been analytically solved for this case. """ - if "jax" in xp.__name__: - from jax.scipy.special import erfinv - else: - from scipy.special import erfinv + try: + erfinv = erfinv_import(xp) + except BackendNotImplementedError: + raise NotImplementedError( + f"Truncated Gaussian prior rescale not implemented for this {xp.__name__}" + ) return erfinv(2 * val * self.normalisation + erf( (self.minimum - self.mu) / 2 ** 0.5 / self.sigma)) * 2 ** 0.5 * self.sigma + self.mu @@ -716,10 +719,10 @@ def rescale(self, val, *, xp=np): This maps to the inverse CDF. This has been analytically solved for this case. """ - if "jax" in xp.__name__: - from jax.scipy.special import erfinv - else: - from scipy.special import erfinv + try: + erfinv = erfinv_import(xp) + except BackendNotImplementedError: + raise NotImplementedError(f"LogNormal prior rescale not implemented for this {xp.__name__}") return xp.exp(self.mu + (2 * self.sigma ** 2)**0.5 * erfinv(2 * val - 1)) @xp_wrap diff --git a/bilby/core/prior/dict.py b/bilby/core/prior/dict.py index 7a9a8dcfd..29d0cae0e 100644 --- a/bilby/core/prior/dict.py +++ b/bilby/core/prior/dict.py @@ -6,7 +6,6 @@ from warnings import warn import numpy as np -from scipy._lib._array_api import array_namespace from .analytical import DeltaFunction from .base import Prior, Constraint @@ -17,6 +16,7 @@ BilbyJsonEncoder, decode_bilby_json, ) +from ...compat.utils import array_module class PriorDict(dict): @@ -542,7 +542,7 @@ def prob(self, sample, **kwargs): float: Joint probability of all individual sample probabilities """ - xp = array_namespace(*sample.values()) + xp = array_module(sample.values()) prob = xp.prod(xp.asarray([self[key].prob(sample[key]) for key in sample]), **kwargs) return prob @@ -643,7 +643,7 @@ def rescale(self, keys, theta): """ if isinstance(theta, {}.values().__class__): theta = list(theta) - xp = array_namespace(theta) + xp = array_module(theta) return xp.asarray([self[key].rescale(sample) for key, sample in zip(keys, theta)]) @@ -814,7 +814,7 @@ def prob(self, sample, **kwargs): """ self._prepare_evaluation(*zip(*sample.items())) - xp = array_namespace(*sample.values()) + xp = array_module(sample.values()) res = xp.asarray([ self[key].prob(sample[key], **self.get_required_variables(key)) for key in sample @@ -842,7 +842,7 @@ def ln_prob(self, sample, axis=None, normalized=True): """ self._prepare_evaluation(*zip(*sample.items())) - xp = array_namespace(*sample.values()) + xp = array_module(sample.values()) res = xp.array([ self[key].ln_prob(sample[key], **self.get_required_variables(key)) for key in sample @@ -876,7 +876,7 @@ def rescale(self, keys, theta): """ if isinstance(theta, {}.values().__class__): theta = list(theta) - xp = array_namespace(theta) + xp = array_module(theta) keys = list(keys) self._check_resolved() diff --git a/bilby/core/utils/calculus.py b/bilby/core/utils/calculus.py index f20973f4e..6dbce9bf5 100644 --- a/bilby/core/utils/calculus.py +++ b/bilby/core/utils/calculus.py @@ -1,5 +1,6 @@ import math +import array_api_compat as aac import numpy as np from scipy.interpolate import RectBivariateSpline, interp1d as _interp1d from scipy.special import logsumexp @@ -234,10 +235,17 @@ def __init__(self, x, y, z, bbox=[None] * 4, kx=3, ky=3, s=0, fill_value=None): 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): - from array_api_compat import is_jax_namespace xp = array_module(x) - if is_jax_namespace(xp): + 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) From 4b3d7013d8606d9de61809fec3f8bf0faca2d61a Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Mon, 22 Dec 2025 16:29:42 -0500 Subject: [PATCH 038/140] MAINT: removed extra ripple code --- bilby/gw/jaxstuff.py | 67 -------------------------------------------- 1 file changed, 67 deletions(-) delete mode 100644 bilby/gw/jaxstuff.py diff --git a/bilby/gw/jaxstuff.py b/bilby/gw/jaxstuff.py deleted file mode 100644 index 6046e51bd..000000000 --- a/bilby/gw/jaxstuff.py +++ /dev/null @@ -1,67 +0,0 @@ -""" -Generic dumping ground for jax-specific functions that we need. -This should find a home somewhere down the line, but gives an -idea of how much pain is being added. -""" - -import jax -import jax.numpy as jnp -from ripple.waveforms import IMRPhenomPv2 - - -def bilby_to_ripple_spins( - theta_jn, - phi_jl, - tilt_1, - tilt_2, - phi_12, - a_1, - a_2, -): - iota = theta_jn - spin_1x = a_1 * jnp.sin(tilt_1) * jnp.cos(phi_jl) - spin_1y = a_1 * jnp.sin(tilt_1) * jnp.sin(phi_jl) - spin_1z = a_1 * jnp.cos(tilt_1) - spin_2x = a_2 * jnp.sin(tilt_2) * jnp.cos(phi_jl + phi_12) - spin_2y = a_2 * jnp.sin(tilt_2) * jnp.sin(phi_jl + phi_12) - spin_2z = a_2 * jnp.cos(tilt_2) - return iota, spin_1x, spin_1y, spin_1z, spin_2x, spin_2y, spin_2z - - -wf_func = jax.jit(IMRPhenomPv2.gen_IMRPhenomPv2) - - -def ripple_bbh_relbin( - frequency, mass_1, mass_2, luminosity_distance, theta_jn, phase, - a_1, a_2, tilt_1, tilt_2, phi_12, phi_jl, fiducial, **kwargs, -): - if fiducial == 1: - kwargs["frequencies"] = frequency - else: - kwargs["frequencies"] = kwargs.pop("frequency_bin_edges") - return ripple_bbh( - frequency, mass_1, mass_2, luminosity_distance, theta_jn, phase, - a_1, a_2, tilt_1, tilt_2, phi_12, phi_jl, **kwargs - ) - - -def ripple_bbh( - frequency, mass_1, mass_2, luminosity_distance, theta_jn, phase, - a_1, a_2, tilt_1, tilt_2, phi_12, phi_jl, **kwargs, -): - iota, spin_1x, spin_1y, spin_1z, spin_2x, spin_2y, spin_2z = bilby_to_ripple_spins( - theta_jn, phi_jl, tilt_1, tilt_2, phi_12, a_1, a_2 - ) - if "frequencies" in kwargs: - frequencies = kwargs["frequencies"] - elif "minimum_frequency" in kwargs: - frequencies = jnp.maximum(frequency, kwargs["minimum_frequency"]) - else: - frequencies = frequency - theta = jnp.array([ - mass_1, mass_2, spin_1x, spin_1y, spin_1z, spin_2x, spin_2y, spin_2z, - luminosity_distance, jnp.array(0.0), phase, iota - ]) - hp, hc = wf_func(frequencies, theta, jax.numpy.array(20.0)) - return dict(plus=hp, cross=hc) - From bd36cc2d4974b9b2b62a7d24f8a16d64225b1ab8 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Mon, 22 Dec 2025 16:34:15 -0500 Subject: [PATCH 039/140] REFACTOR: make bilby_cython an optional dependency --- bilby/compat/types.py | 6 ++-- bilby/compat/utils.py | 1 - bilby/gw/compat/__init__.py | 6 ++++ bilby/gw/compat/cython.py | 66 +++++++++++++++++++++++++++++++++++++ bilby/gw/geometry.py | 61 ---------------------------------- bilby/gw/time.py | 33 ------------------- requirements.txt | 2 -- 7 files changed, 74 insertions(+), 101 deletions(-) create mode 100644 bilby/gw/compat/cython.py diff --git a/bilby/compat/types.py b/bilby/compat/types.py index 8a3391c44..48c74c29f 100644 --- a/bilby/compat/types.py +++ b/bilby/compat/types.py @@ -1,6 +1,4 @@ -from typing import Union import numpy as np -Real = Union[float, int] -ArrayLike = Union[np.ndarray, list, tuple] - +Real = float | int +ArrayLike = np.ndarray | list | tuple diff --git a/bilby/compat/utils.py b/bilby/compat/utils.py index 9f83ac9e5..98fae293d 100644 --- a/bilby/compat/utils.py +++ b/bilby/compat/utils.py @@ -21,7 +21,6 @@ def array_module(arr): return np - def promote_to_array(args, backend, skip=None): if skip is None: skip = len(args) diff --git a/bilby/gw/compat/__init__.py b/bilby/gw/compat/__init__.py index 8e2e63c62..16eea2d00 100644 --- a/bilby/gw/compat/__init__.py +++ b/bilby/gw/compat/__init__.py @@ -2,3 +2,9 @@ from .jax import n_leap_seconds except ModuleNotFoundError: pass + + +try: + from .cython import gps_time_to_utc +except ModuleNotFoundError: + pass \ No newline at end of file diff --git a/bilby/gw/compat/cython.py b/bilby/gw/compat/cython.py new file mode 100644 index 000000000..f42301875 --- /dev/null +++ b/bilby/gw/compat/cython.py @@ -0,0 +1,66 @@ +import numpy as np +from bilby_cython import time as _time, geometry as _geometry +from plum import dispatch + +from ...compat.types import Real, ArrayLike + + +@dispatch(precedence=1) +def gps_time_to_utc(gps_time: Real): + return _time.gps_time_to_utc(gps_time) + + +@dispatch(precedence=1) +def greenwich_mean_sidereal_time(gps_time: Real | ArrayLike): + return _time.greenwich_mean_sidereal_time(gps_time) + + +@dispatch(precedence=1) +def greenwich_sidereal_time(gps_time: Real, equation_of_equinoxes: Real): + return _time.greenwich_sidereal_time(gps_time, equation_of_equinoxes) + + +@dispatch(precedence=1) +def n_leap_seconds(gps_time: Real): + return _time.n_leap_seconds(gps_time) + + +@dispatch(precedence=1) +def utc_to_julian_day(utc_time: Real): + return _time.utc_to_julian_day(utc_time) + + +@dispatch(precedence=1) +def calculate_arm(arm_tilt: Real, arm_azimuth: Real, longitude: Real, latitude: Real): + return _geometry.calculate_arm(arm_tilt, arm_azimuth, longitude, latitude) + + +@dispatch(precedence=1) +def detector_tensor(x: ArrayLike, y: ArrayLike): + return _geometry.detector_tensor(x, y) + + +@dispatch(precedence=1) +def get_polarization_tensor(ra: Real, dec: Real, time: Real, psi: Real, mode: str): + return _geometry.get_polarization_tensor(ra, dec, time, psi, mode) + + +@dispatch(precedence=1) +def rotation_matrix_from_delta(delta: ArrayLike): + return _geometry.rotation_matrix_from_delta_x(delta) + + +@dispatch(precedence=1) +def time_delay_geocentric(detector1: ArrayLike, detector2: ArrayLike, ra, dec, time): + return _geometry.time_delay_geocentric(detector1, detector2, ra, dec, time) + + +@dispatch(precedence=1) +def time_delay_from_geocenter(detector1: ArrayLike, ra: Real, dec: Real, time: Real | ArrayLike): + return _geometry.time_delay_from_geocenter(detector1, ra, dec, time) + + +@dispatch(precedence=1) +def zenith_azimuth_to_theta_phi(zenith: Real, azimuth: Real, delta_x: np.ndarray): + theta, phi = _geometry.zenith_azimuth_to_theta_phi(zenith, azimuth, delta_x) + return theta, phi % (2 * np.pi) diff --git a/bilby/gw/geometry.py b/bilby/gw/geometry.py index 8b0cf2855..fe5ca027f 100644 --- a/bilby/gw/geometry.py +++ b/bilby/gw/geometry.py @@ -1,9 +1,6 @@ -import numpy as np from plum import dispatch -from bilby_cython import geometry as _geometry from .time import greenwich_mean_sidereal_time -from ..compat.types import Real, ArrayLike from ..compat.utils import array_module, promote_to_array @@ -198,61 +195,3 @@ def zenith_azimuth_to_theta_phi(zenith, azimuth, delta_x): theta = xp.arccos(omega[2]) phi = xp.arctan2(omega[1], omega[0]) % (2 * xp.pi) return theta, phi - - - -# @dispatch(precedence=1) -# def antenna_response(detector_tensor: np.ndarray, ra: FloatOrInt, dec: FloatOrInt, time: FloatOrInt, psi: FloatOrInt, mode: str): -# return _geometry.antenna_response(detector_tensor, ra, dec, time, psi, mode) - - -@dispatch(precedence=1) -def calculate_arm(arm_tilt: Real, arm_azimuth: Real, longitude: Real, latitude: Real): - return _geometry.calculate_arm(arm_tilt, arm_azimuth, longitude, latitude) - - -@dispatch(precedence=1) -def detector_tensor(x: ArrayLike, y: ArrayLike): - return _geometry.detector_tensor(x, y) - - -@dispatch(precedence=1) -def get_polarization_tensor(ra: Real, dec: Real, time: Real, psi: Real, mode: str): - return _geometry.get_polarization_tensor(ra, dec, time, psi, mode) - - -# @dispatch(precedence=1) -# def get_polarization_tensor_multiple_modes(ra: FloatOrInt, dec: FloatOrInt, time: FloatOrInt, psi: FloatOrInt, modes: list[str]): -# return [geometry.get_polarization_tensor(ra, dec, time, psi, mode) for mode in modes] - - -@dispatch(precedence=1) -def rotation_matrix_from_delta(delta: ArrayLike): - return _geometry.rotation_matrix_from_delta_x(delta) - - -# @dispatch(precedence=1) -# def three_by_three_matrix_contraction(a: ArrayLike, b: ArrayLike): -# return _geometry.three_by_three_matrix_contraction(a, b) - - -@dispatch(precedence=1) -def time_delay_geocentric(detector1: ArrayLike, detector2: ArrayLike, ra, dec, time): - return _geometry.time_delay_geocentric(detector1, detector2, ra, dec, time) - - -@dispatch(precedence=1) -def time_delay_from_geocenter(detector1: ArrayLike, ra: Real, dec: Real, time: Real): - return _geometry.time_delay_from_geocenter(detector1, ra, dec, time) - - -@dispatch(precedence=1) -def time_delay_from_geocenter(detector1: ArrayLike, ra: Real, dec: Real, time: ArrayLike): - return _geometry.time_delay_from_geocenter_vectorized(detector1, ra, dec, time) - - -@dispatch(precedence=1) -def zenith_azimuth_to_theta_phi(zenith: Real, azimuth: Real, delta_x: np.ndarray): - theta, phi = _geometry.zenith_azimuth_to_theta_phi(zenith, azimuth, delta_x) - return theta, phi % (2 * np.pi) - diff --git a/bilby/gw/time.py b/bilby/gw/time.py index 29a2b9c77..9b085edf3 100644 --- a/bilby/gw/time.py +++ b/bilby/gw/time.py @@ -2,9 +2,7 @@ import numpy as np from plum import dispatch -from bilby_cython import time as _time -from ..compat.types import Real, ArrayLike from ..compat.utils import array_module @@ -226,34 +224,3 @@ def utc_to_julian_day(utc_time): """ return utc_time.julian_day - - -@dispatch(precedence=1) -def gps_time_to_utc(gps_time: Real): - return _time.gps_time_to_utc(gps_time) - - -@dispatch(precedence=1) -def greenwich_mean_sidereal_time(gps_time: Real): - return _time.greenwich_mean_sidereal_time(gps_time) - - -@dispatch(precedence=1) -def greenwich_mean_sidereal_time(gps_time: ArrayLike): - return _time.greenwich_mean_sidereal_time_vectorized(gps_time) - - -@dispatch(precedence=1) -def greenwich_sidereal_time(gps_time: Real, equation_of_equinoxes: Real): - return _time.greenwich_sidereal_time(gps_time, equation_of_equinoxes) - - -@dispatch(precedence=1) -def n_leap_seconds(gps_time: Real): - return _time.n_leap_seconds(gps_time) - - -@dispatch(precedence=1) -def utc_to_julian_day(utc_time: Real): - return _time.utc_to_julian_day(utc_time) - diff --git a/requirements.txt b/requirements.txt index 2b3cc4405..3539f45b3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,3 @@ -bilby.cython>=0.3.0 dynesty>=2.0.1 emcee corner @@ -11,6 +10,5 @@ dill tqdm h5py attrs -importlib-metadata>=3.6; python_version < '3.10' plum-dispatch array_api_compat From cd6bccb51421d9929fe33058f3173523a9a9d6e8 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Mon, 22 Dec 2025 16:39:04 -0500 Subject: [PATCH 040/140] FMT: formatting fixes --- bilby/core/likelihood.py | 2 +- bilby/core/prior/analytical.py | 2 +- bilby/core/prior/dict.py | 1 - bilby/core/utils/calculus.py | 8 ++++---- bilby/gw/compat/jax.py | 8 +++++--- bilby/gw/geometry.py | 2 -- bilby/gw/likelihood/roq.py | 2 -- bilby/gw/time.py | 18 ++++++++---------- test/core/prior/prior_test.py | 2 +- 9 files changed, 20 insertions(+), 25 deletions(-) diff --git a/bilby/core/likelihood.py b/bilby/core/likelihood.py index 34acf806a..fa8965bad 100644 --- a/bilby/core/likelihood.py +++ b/bilby/core/likelihood.py @@ -486,7 +486,7 @@ def __init__(self, mean, cov): raise NotImplementedError( f"Multivariate normal likelihood not implemented for {xp.__name__} backend" ) - + parameters = {"x{0}".format(i): 0 for i in range(self.dim)} super(AnalyticalMultidimensionalCovariantGaussian, self).__init__(parameters=parameters) diff --git a/bilby/core/prior/analytical.py b/bilby/core/prior/analytical.py index dc418ed4a..76262dcf2 100644 --- a/bilby/core/prior/analytical.py +++ b/bilby/core/prior/analytical.py @@ -619,7 +619,7 @@ def rescale(self, val, *, xp=np): This maps to the inverse CDF. This has been analytically solved for this case. """ - try: + try: erfinv = erfinv_import(xp) except BackendNotImplementedError: raise NotImplementedError( diff --git a/bilby/core/prior/dict.py b/bilby/core/prior/dict.py index 29d0cae0e..b732c7082 100644 --- a/bilby/core/prior/dict.py +++ b/bilby/core/prior/dict.py @@ -882,7 +882,6 @@ def rescale(self, keys, theta): self._check_resolved() self._update_rescale_keys(keys) result = dict() - joint = dict() for key, index in zip( self.sorted_keys_without_fixed_parameters, self._rescale_indexes ): diff --git a/bilby/core/utils/calculus.py b/bilby/core/utils/calculus.py index 6dbce9bf5..7b9e9b017 100644 --- a/bilby/core/utils/calculus.py +++ b/bilby/core/utils/calculus.py @@ -192,7 +192,7 @@ def logtrapzexp(lnf, dx): class interp1d(_interp1d): - + def __call__(self, x): from array_api_compat import is_numpy_namespace @@ -201,7 +201,7 @@ def __call__(self, x): return super().__call__(x) else: return self._call_alt(x, xp=xp) - + def _call_alt(self, x, *, xp=np): if isinstance(self.fill_value, tuple): left, right = self.fill_value @@ -244,7 +244,7 @@ def __call__(self, x, y, dx=0, dy=0, grid=False): 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) @@ -258,7 +258,7 @@ def _call_scipy(self, x, y, dx=0, dy=0, grid=False): return result.item() else: return result - + def _call_jax(self, x, y): import jax.numpy as jnp from interpax import interp2d diff --git a/bilby/gw/compat/jax.py b/bilby/gw/compat/jax.py index 9b0732112..99277e30a 100644 --- a/bilby/gw/compat/jax.py +++ b/bilby/gw/compat/jax.py @@ -2,7 +2,10 @@ from jax import Array from plum import dispatch -from ..time import LEAP_SECONDS as _LEAP_SECONDS, n_leap_seconds +from ..time import ( + LEAP_SECONDS as _LEAP_SECONDS, + n_leap_seconds as _n_leap_seconds, +) __all__ = ["n_leap_seconds"] @@ -14,5 +17,4 @@ def n_leap_seconds(date: Array): """ Find the number of leap seconds required for the specified date. """ - return n_leap_seconds(date, LEAP_SECONDS) - + return _n_leap_seconds(date, LEAP_SECONDS) diff --git a/bilby/gw/geometry.py b/bilby/gw/geometry.py index fe5ca027f..c07ec5c0c 100644 --- a/bilby/gw/geometry.py +++ b/bilby/gw/geometry.py @@ -148,7 +148,6 @@ def rotation_matrix_from_delta(delta_x): return rotation_3 @ rotation_2 @ rotation_1 - @dispatch def three_by_three_matrix_contraction(a, b): """""" @@ -171,7 +170,6 @@ def time_delay_geocentric(detector1, detector2, ra, dec, time): return omega @ delta_d / speed_of_light - @dispatch def time_delay_from_geocenter(detector1, ra, dec, time): """""" diff --git a/bilby/gw/likelihood/roq.py b/bilby/gw/likelihood/roq.py index a74e194b1..a5c84b3a1 100644 --- a/bilby/gw/likelihood/roq.py +++ b/bilby/gw/likelihood/roq.py @@ -455,8 +455,6 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array frequency_nodes = self.waveform_generator.waveform_arguments['frequency_nodes'] linear_indices = self.waveform_generator.waveform_arguments['linear_indices'] quadratic_indices = self.waveform_generator.waveform_arguments['quadratic_indices'] - size_linear = len(linear_indices) - size_quadratic = len(quadratic_indices) h_linear = 0j h_quadratic = 0j for mode in waveform_polarizations['linear']: diff --git a/bilby/gw/time.py b/bilby/gw/time.py index 9b085edf3..51de99e3c 100644 --- a/bilby/gw/time.py +++ b/bilby/gw/time.py @@ -1,5 +1,3 @@ -from typing import Union - import numpy as np from plum import dispatch @@ -125,7 +123,7 @@ def greenwich_mean_sidereal_time(gps_time): ---------- gps_time : float GPS time in seconds. - + Returns ------- float @@ -145,7 +143,7 @@ def greenwich_sidereal_time(gps_time, equation_of_equinoxes): GPS time in seconds. equation_of_equinoxes : float Equation of the equinoxes in seconds. - + Returns ------- float @@ -178,26 +176,26 @@ def n_leap_seconds(gps_time, leap_seconds): GPS time in seconds. leap_seconds : array_like GPS time of leap seconds. - + Returns ------- float - Number of leap seconds + Number of leap seconds """ xp = array_module(gps_time) return xp.sum(gps_time > leap_seconds[:, None], axis=0).squeeze() @dispatch -def n_leap_seconds(gps_time: Union[np.ndarray, float, int]): +def n_leap_seconds(gps_time: np.ndarray | float | int): # noqa F811 """ Calculate the number of leap seconds that have occurred up to a given GPS time. Parameters ---------- - gps_time : float + gps_time : float | np.ndarray | int GPS time in seconds. - + Returns ------- float @@ -216,7 +214,7 @@ def utc_to_julian_day(utc_time): ---------- utc_time : datetime UTC time. - + Returns ------- float diff --git a/test/core/prior/prior_test.py b/test/core/prior/prior_test.py index 14f864e90..43ce08fe5 100644 --- a/test/core/prior/prior_test.py +++ b/test/core/prior/prior_test.py @@ -853,7 +853,7 @@ def test_set_minimum_setting(self): continue prior.minimum = (prior.maximum + prior.minimum) / 2 self.assertTrue(min(prior.sample(10000)) > prior.minimum) - + def test_jax_methods(self): import jax From 043b3c9afe7e3b43724ff60cea81ee3d0ee57fae Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Wed, 21 Jan 2026 16:59:14 -0500 Subject: [PATCH 041/140] BUG: fix array introspection for conversion --- bilby/gw/conversion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bilby/gw/conversion.py b/bilby/gw/conversion.py index 74608c2ea..55f81372c 100644 --- a/bilby/gw/conversion.py +++ b/bilby/gw/conversion.py @@ -241,7 +241,7 @@ def convert_to_lal_binary_black_hole_parameters(parameters): """ converted_parameters = parameters.copy() original_keys = list(converted_parameters.keys()) - xp = array_module(parameters[original_keys[5]]) + xp = array_module(parameters.values()) if 'luminosity_distance' not in original_keys: if 'redshift' in converted_parameters.keys(): converted_parameters['luminosity_distance'] = \ From bc070afa8bb1e906265cf249e5bb054f74cb1431 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Wed, 21 Jan 2026 16:59:48 -0500 Subject: [PATCH 042/140] REFACTOR: make parameters for waveform generator more strict --- bilby/gw/waveform_generator.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/bilby/gw/waveform_generator.py b/bilby/gw/waveform_generator.py index d66589da1..f1c5e6e2a 100644 --- a/bilby/gw/waveform_generator.py +++ b/bilby/gw/waveform_generator.py @@ -81,7 +81,8 @@ def __init__(self, duration=None, sampling_frequency=None, start_time=0, frequen self.waveform_arguments = dict() if parameters is not None: logger.warning( - "Non null parameters passed to waveform generator. These will be ignored." + "Setting initial parameters via the 'parameters' argument is " + "deprecated and will be removed in a future release." ) self._cache = dict(parameters=None, waveform=None, model=None) self.use_cache = use_cache @@ -169,13 +170,16 @@ def time_domain_strain(self, parameters=None): def _calculate_strain(self, model, model_data_points, transformation_function, transformed_model, transformed_model_data_points, parameters): - if parameters is None and self._cache["parameters"] is not None: - parameters = self._cache["parameters"] - elif parameters is None: - raise ValueError("No parameters passed to waveform generator.") - - if parameters == self._cache['parameters'] and self._cache['model'] == model and \ - self._cache['transformed_model'] == transformed_model: + if parameters is None: + parameters = self._cache.get('parameters', None) + if parameters is None: + raise ValueError("No parameters given to generate waveform.") + if ( + self.use_cache + and parameters == self._cache.get('parameters', None) + and self._cache['model'] == model + and self._cache['transformed_model'] == transformed_model + ): return self._cache['waveform'] else: self._cache['parameters'] = parameters.copy() From 2a537b19ab8ed204193a0b6599233a3d28897d14 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Wed, 21 Jan 2026 17:03:15 -0500 Subject: [PATCH 043/140] BUG: fix core likelihood tests --- bilby/core/likelihood.py | 4 ++-- test/core/likelihood_test.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bilby/core/likelihood.py b/bilby/core/likelihood.py index fa8965bad..68995e8e5 100644 --- a/bilby/core/likelihood.py +++ b/bilby/core/likelihood.py @@ -308,9 +308,9 @@ def __init__(self, x, y, func, **kwargs): def log_likelihood(self, parameters): mu = self.func(self.x, **self.model_parameters(parameters=parameters), **self.kwargs) - if any(mu < 0.): - return -np.inf xp = array_module(mu) + if xp.any(mu < 0.): + return -np.inf return -xp.sum(xp.log(mu) + (self.y / mu)) def __repr__(self): diff --git a/test/core/likelihood_test.py b/test/core/likelihood_test.py index 3c4c71c26..a8e570cd5 100644 --- a/test/core/likelihood_test.py +++ b/test/core/likelihood_test.py @@ -376,7 +376,7 @@ def test_log_likelihood_dummy(self): poisson_likelihood = PoissonLikelihood( x=self.x, y=self.y, func=lambda x: np.linspace(1, 100, self.N) ) - with mock.patch("numpy.sum") as m: + with mock.patch("array_api_compat.numpy.sum") as m: m.return_value = 1 self.assertEqual(1, poisson_likelihood.log_likelihood(dict())) @@ -468,7 +468,7 @@ def test_log_likelihood_default(self): exponential_likelihood = ExponentialLikelihood( x=self.x, y=self.y, func=lambda x: np.array([4.2]) ) - with mock.patch("numpy.sum") as m: + with mock.patch("array_api_compat.numpy.sum") as m: m.return_value = 3 self.assertEqual(-3, exponential_likelihood.log_likelihood(dict())) From b87327a992fdfd4144392594e23f868e5639aea6 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Thu, 22 Jan 2026 11:59:04 -0500 Subject: [PATCH 044/140] BUG: fix calibration calculations --- bilby/gw/detector/calibration.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/bilby/gw/detector/calibration.py b/bilby/gw/detector/calibration.py index 229bdba25..28e9c784e 100644 --- a/bilby/gw/detector/calibration.py +++ b/bilby/gw/detector/calibration.py @@ -44,6 +44,7 @@ import numpy as np import pandas as pd +from array_api_compat import is_jax_namespace from scipy.interpolate import interp1d from ...compat.utils import array_module @@ -375,8 +376,9 @@ def get_calibration_factor(self, frequency_array, **params): delta_amplitude = self._evaluate_spline("amplitude", a, b, c, d, previous_nodes) delta_phase = self._evaluate_spline("phase", a, b, c, d, previous_nodes) calibration_factor = (1 + delta_amplitude) * (2 + 1j * delta_phase) / (2 - 1j * delta_phase) + xp = calibration_factor.__array_namespace__() - return calibration_factor + return xp.nan_to_num(calibration_factor) class Precomputed(Recalibrate): @@ -408,8 +410,21 @@ def get_calibration_factor(self, frequency_array, **params): idx = int(params.get(self.prefix, None)) if idx is None: raise KeyError(f"Calibration index for {self.label} not found.") - if not np.array_equal(frequency_array, self.frequency_array): - raise ValueError("Frequency grid passed to calibrator doesn't match.") + + xp = frequency_array.__array_namespace__() + if not xp.array_equal(frequency_array, self.frequency_array): + intersection, mask, _ = xp.intersect1d( + frequency_array, self.frequency_array, return_indices=True + ) + if len(intersection) != len(self.frequency_array): + raise ValueError("Frequency grid passed to calibrator doesn't match.") + output = xp.ones_like(frequency_array, dtype=complex) + curve = xp.asarray(self.curves[idx]) + if is_jax_namespace(xp): + output = output.at[mask].set(curve) + else: + output[mask] = curve + return output return self.curves[idx] @classmethod From a605c05163bf73e63e76103b5d210d671989a6fa Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Thu, 22 Jan 2026 11:59:27 -0500 Subject: [PATCH 045/140] EXAMPLE: update jax fast tutorial --- .../injection_examples/jax_fast_tutorial.py | 243 +++++++++--------- 1 file changed, 118 insertions(+), 125 deletions(-) diff --git a/examples/gw_examples/injection_examples/jax_fast_tutorial.py b/examples/gw_examples/injection_examples/jax_fast_tutorial.py index 6bfd87ea8..b3eb23a55 100644 --- a/examples/gw_examples/injection_examples/jax_fast_tutorial.py +++ b/examples/gw_examples/injection_examples/jax_fast_tutorial.py @@ -10,96 +10,129 @@ We optionally use ripple waveforms and a JIT-compiled likelihood. """ import os -from itertools import product # Set OMP_NUM_THREADS to stop lalsimulation taking over my computer os.environ["OMP_NUM_THREADS"] = "1" import bilby -import bilby.gw.jaxstuff import numpy as np import jax import jax.numpy as jnp -from jax import random -from numpyro.infer import AIES, ESS # noqa -from numpyro.infer.ensemble_util import get_nondiagonal_indices +from bilby.compat.jax import JittedLikelihood +from ripple.waveforms import IMRPhenomPv2 jax.config.update("jax_enable_x64", True) -bilby.core.utils.setup_logger() # log_level="WARNING") - -def setup_prior(): - # Set up a PriorDict, which inherits from dict. - # By default we will sample all terms in the signal models. However, this will - # take a long time for the calculation, so for this example we will set almost - # all of the priors to be equall to their injected values. This implies the - # prior is a delta function at the true, injected value. In reality, the - # sampler implementation is smart enough to not sample any parameter that has - # a delta-function prior. - # The above list does *not* include mass_1, mass_2, theta_jn and luminosity - # distance, which means those are the parameters that will be included in the - # sampler. If we do nothing, then the default priors get used. - priors = bilby.gw.prior.BBHPriorDict() - del priors["mass_1"], priors["mass_2"] - priors["geocent_time"] = bilby.core.prior.Uniform(1126249642, 1126269642) - priors["luminosity_distance"].minimum = 1 - priors["luminosity_distance"].maximum = 500 - priors["chirp_mass"].minimum = 2.35 - priors["chirp_mass"].maximum = 2.45 - # priors["luminosity_distance"] = bilby.core.prior.PowerLaw(2.0, 10.0, 500.0) - # priors["sky_x"] = bilby.core.prior.Normal(mu=0, sigma=1) - # priors["sky_y"] = bilby.core.prior.Normal(mu=0, sigma=1) - # priors["sky_z"] = bilby.core.prior.Normal(mu=0, sigma=1) - # priors["delta_phase"] = priors.pop("phase") - # del priors["tilt_1"], priors["tilt_2"], priors["phi_12"], priors["phi_jl"] - # priors["spin_1_x"] = bilby.core.prior.Normal(mu=0, sigma=1) - # priors["spin_1_y"] = bilby.core.prior.Normal(mu=0, sigma=1) - # priors["spin_1_z"] = bilby.core.prior.Normal(mu=0, sigma=1) - # priors["spin_2_x"] = bilby.core.prior.Normal(mu=0, sigma=1) - # priors["spin_2_y"] = bilby.core.prior.Normal(mu=0, sigma=1) - # priors["spin_2_z"] = bilby.core.prior.Normal(mu=0, sigma=1) - # # del priors["a_1"], priors["a_2"] - # # priors["chi_1"] = bilby.core.prior.Uniform(-0.05, 0.05) - # # priors["chi_2"] = bilby.core.prior.Uniform(-0.05, 0.05) - # del priors["theta_jn"], priors["psi"], priors["delta_phase"] - # priors["orientation_w"] = bilby.core.prior.Normal(mu=0, sigma=1) - # priors["orientation_x"] = bilby.core.prior.Normal(mu=0, sigma=1) - # priors["orientation_y"] = bilby.core.prior.Normal(mu=0, sigma=1) - # priors["orientation_z"] = bilby.core.prior.Normal(mu=0, sigma=1) - return priors - - -def original_to_sampling_priors(priors, truth): - del priors["ra"], priors["dec"] - priors["zenith"] = bilby.core.prior.Cosine() - priors["azimuth"] = bilby.core.prior.Uniform(minimum=0, maximum=2 * np.pi) - priors["L1_time"] = bilby.core.prior.Uniform(truth["geocent_time"] - 0.1, truth["geocent_time"] + 0.1) +def bilby_to_ripple_spins( + theta_jn, + phi_jl, + tilt_1, + tilt_2, + phi_12, + a_1, + a_2, +): + """ + A simplified spherical to cartesian spin conversion function. + This is not equivalent to the method used in `bilby.gw.conversion` + which comes from `lalsimulation` and is not `JAX` compatible. + """ + iota = theta_jn + spin_1x = a_1 * jnp.sin(tilt_1) * jnp.cos(phi_jl) + spin_1y = a_1 * jnp.sin(tilt_1) * jnp.sin(phi_jl) + spin_1z = a_1 * jnp.cos(tilt_1) + spin_2x = a_2 * jnp.sin(tilt_2) * jnp.cos(phi_jl + phi_12) + spin_2y = a_2 * jnp.sin(tilt_2) * jnp.sin(phi_jl + phi_12) + spin_2z = a_2 * jnp.cos(tilt_2) + return iota, spin_1x, spin_1y, spin_1z, spin_2x, spin_2y, spin_2z + + +def ripple_bbh( + frequency, mass_1, mass_2, luminosity_distance, theta_jn, phase, + a_1, a_2, tilt_1, tilt_2, phi_12, phi_jl, **kwargs, +): + """ + Source function wrapper to ripple's IMRPhenomPv2 waveform generator. + This function cannot be jitted directly as the Bilby waveform generator + relies on inspecting the function signature. + + Parameters + ---------- + frequency: jnp.ndarray + Frequencies at which to compute the waveform. + mass_1: float | jnp.ndarray + Mass of the primary component in solar masses. + mass_2: float | jnp.ndarray + Mass of the secondary component in solar masses. + luminosity_distance: float | jnp.ndarray + Luminosity distance to the source in Mpc. + theta_jn: float | jnp.ndarray + Angle between total angular momentum and line of sight in radians. + phase: float | jnp.ndarray + Phase at coalescence in radians. + a_1: float | jnp.ndarray + Dimensionless spin magnitude of the primary component. + a_2: float | jnp.ndarray + Dimensionless spin magnitude of the secondary component. + tilt_1: float | jnp.ndarray + Tilt angle of the primary component spin in radians. + tilt_2: float | jnp.ndarray + Tilt angle of the secondary component spin in radians. + phi_12: float | jnp.ndarray + Azimuthal angle between the two spin vectors in radians. + phi_jl: float | jnp.ndarray + Azimuthal angle of the total angular momentum vector in radians. + **kwargs + Additional keyword arguments. Must include 'minimum_frequency'. + + Returns + ------- + dict + Dictionary containing the plus and cross polarizations of the waveform. + """ + iota, *cartesian_spins = bilby_to_ripple_spins( + # iota, spin_1x, spin_1y, spin_1z, spin_2x, spin_2y, spin_2z = bilby_to_ripple_spins( + theta_jn, phi_jl, tilt_1, tilt_2, phi_12, a_1, a_2 + ) + frequencies = jnp.maximum(frequency, kwargs["minimum_frequency"]) + theta = jnp.array([ + mass_1, mass_2, *cartesian_spins, + luminosity_distance, jnp.array(0.0), phase, iota + ]) + wf_func = jax.jit(IMRPhenomPv2.gen_IMRPhenomPv2) + hp, hc = wf_func(frequencies, theta, jnp.array(20.0)) + return dict(plus=hp, cross=hc) -def main(use_jax, model, idx): +def main(): # Set the duration and sampling frequency of the data segment that we're # going to inject the signal into duration = 64.0 sampling_frequency = 2048.0 minimum_frequency = 20.0 - if use_jax: - duration = jax.numpy.array(duration) - sampling_frequency = jax.numpy.array(sampling_frequency) - minimum_frequency = jax.numpy.array(minimum_frequency) + duration = jnp.array(duration) + sampling_frequency = jnp.array(sampling_frequency) + minimum_frequency = jnp.array(minimum_frequency) # Specify the output directory and the name of the simulation. - outdir = "pp-test-2" - label = f"{model}_{'jax' if use_jax else 'numpy'}_{idx}" + outdir = "outdir" + label = f"jax_fast_tutorial" # Set up a random seed for result reproducibility. This is optional! - bilby.core.utils.random.seed(88170235 + idx * 1000) + bilby.core.utils.random.seed(88170235) - priors = setup_prior() + priors = bilby.gw.prior.BBHPriorDict() injection_parameters = priors.sample() - if model == "relbin": - injection_parameters["fiducial"] = 1 - original_to_sampling_priors(priors, injection_parameters) + injection_parameters["geocent_time"] = 1000000000.0 + injection_parameters["luminosity_distance"] = 400.0 + del priors["ra"], priors["dec"] + priors["zenith"] = bilby.core.prior.Cosine() + priors["azimuth"] = bilby.core.prior.Uniform(minimum=0, maximum=2 * np.pi) + priors["L1_time"] = bilby.core.prior.Uniform( + injection_parameters["geocent_time"] - 0.1, + injection_parameters["geocent_time"] + 0.1, + ) # Fixed arguments passed into the source model waveform_arguments = dict( @@ -108,28 +141,13 @@ def main(use_jax, model, idx): minimum_frequency=minimum_frequency, ) - if use_jax: - match model: - case "relbin": - fdsm = bilby.gw.jaxstuff.ripple_bbh_relbin - case _: - fdsm = bilby.gw.jaxstuff.ripple_bbh - else: - match model: - case "relbin": - fdsm = bilby.gw.source.lal_binary_black_hole_relative_binning - case _: - fdsm = bilby.gw.source.lal_binary_black_hole - # fdsm = bilby.gw.source.sinegaussian - # Create the waveform_generator using a LAL BinaryBlackHole source function waveform_generator = bilby.gw.WaveformGenerator( duration=duration, sampling_frequency=sampling_frequency, - frequency_domain_source_model=fdsm, - # parameter_conversion=bilby.gw.conversion.convert_to_lal_binary_black_hole_parameters, + frequency_domain_source_model=ripple_bbh, waveform_arguments=waveform_arguments, - use_cache=not use_jax, + use_cache=False, ) # Set up interferometers. In this case we'll use two interferometers @@ -145,30 +163,11 @@ def main(use_jax, model, idx): waveform_generator=waveform_generator, parameters=injection_parameters, raise_error=False, ) - if use_jax: - ifos.set_array_backend(jax.numpy) - - if model == "mb": - if use_jax: - pass - else: - waveform_generator.frequency_domain_source_model = ( - bilby.gw.source.binary_black_hole_frequency_sequence - ) - del waveform_generator.waveform_arguments["minimum_frequency"] + ifos.set_array_backend(jnp) # Initialise the likelihood by passing in the interferometer data (ifos) and # the waveform generator - match model: - case "relbin": - likelihood_class = ( - bilby.gw.likelihood.RelativeBinningGravitationalWaveTransient - ) - case "mb": - likelihood_class = bilby.gw.likelihood.MBGravitationalWaveTransient - case _: - likelihood_class = bilby.gw.likelihood.GravitationalWaveTransient - likelihood = likelihood_class( + likelihood = bilby.gw.likelihood.GravitationalWaveTransient( interferometers=ifos, waveform_generator=waveform_generator, priors=priors, @@ -176,43 +175,37 @@ def main(use_jax, model, idx): distance_marginalization=True, reference_frame=ifos, time_reference="L1", - # epsilon=0.1, - # update_fiducial_parameters=True, ) + # Do an initial likelihood evaluation to trigger any internal setup + likelihood.log_likelihood_ratio(priors.sample()) + # Wrap the likelihood with the JittedLikelihood to JIT compile the likelihood + # evaluation + likelihood = JittedLikelihood(likelihood) + # Evaluate the likelihood once to trigger the JIT compilation, this will take + # a few seconds as compiling the waveform takes some time + likelihood.log_likelihood_ratio(priors.sample()) # use the log_compiles context so we can make sure there aren't recompilations # inside the sampling loop - if True: - # with jax.log_compiles(): + with jax.log_compiles(): result = bilby.run_sampler( likelihood=likelihood, priors=priors, - sampler="jaxted" if use_jax else "dynesty", - nlive=1000, + sampler="dynesty", + nlive=100, sample="acceptance-walk", - method="nest", - nsteps=100, - naccept=30, + naccept=5, injection_parameters=injection_parameters, outdir=outdir, label=label, - npool=None if use_jax else 16, - # save="hdf5", - save=False, + npool=None, + save="hdf5", rseed=np.random.randint(0, 100000), ) # Make a corner plot. - # result.plot_corner() - import IPython; IPython.embed() - return result.sampling_time + result.plot_corner() if __name__ == "__main__": - times = dict() - # for arg in product([True, False][:], ["relbin", "mb", "regular"][2:3]): - # times[arg] = main(*arg) - with jax.log_compiles(): - for idx in np.arange(100): - times[idx] = main(True, "mb", idx) - print(times) + main() From 76c61a56ade0723ccc9404952dc75acb1fe8119c Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Thu, 22 Jan 2026 11:59:45 -0500 Subject: [PATCH 046/140] TST: refactor marginalization tests to be less restrictive --- test/gw/likelihood/marginalization_test.py | 59 +++++++++++++--------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/test/gw/likelihood/marginalization_test.py b/test/gw/likelihood/marginalization_test.py index 351e516f8..b5da6ba16 100644 --- a/test/gw/likelihood/marginalization_test.py +++ b/test/gw/likelihood/marginalization_test.py @@ -3,6 +3,7 @@ import pytest import unittest from copy import deepcopy +from functools import cached_property from itertools import product from parameterized import parameterized @@ -230,54 +231,63 @@ def setUp(self): maximum=self.parameters["geocent_time"] + 0.1 ) - trial_roq_paths = [ - "/roq_basis", - os.path.join(os.path.expanduser("~"), "ROQ_data/IMRPhenomPv2/4s"), - "/home/cbc/ROQ_data/IMRPhenomPv2/4s", - ] - roq_dir = None - for path in trial_roq_paths: - if os.path.isdir(path): - roq_dir = path - break - if roq_dir is None: - raise Exception("Unable to load ROQ basis: cannot proceed with tests") - - self.roq_waveform_generator = bilby.gw.waveform_generator.WaveformGenerator( + self.relbin_waveform_generator = bilby.gw.waveform_generator.WaveformGenerator( duration=self.duration, sampling_frequency=self.sampling_frequency, - frequency_domain_source_model=bilby.gw.source.binary_black_hole_roq, + frequency_domain_source_model=bilby.gw.source.lal_binary_black_hole_relative_binning, start_time=1126259640, waveform_arguments=dict( reference_frequency=20.0, + minimum_frequency=20.0, waveform_approximant="IMRPhenomPv2", - frequency_nodes_linear=np.load(f"{roq_dir}/fnodes_linear.npy"), - frequency_nodes_quadratic=np.load(f"{roq_dir}/fnodes_quadratic.npy"), ) ) - self.roq_linear_matrix_file = f"{roq_dir}/B_linear.npy" - self.roq_quadratic_matrix_file = f"{roq_dir}/B_quadratic.npy" - self.relbin_waveform_generator = bilby.gw.waveform_generator.WaveformGenerator( + self.multiband_waveform_generator = bilby.gw.WaveformGenerator( duration=self.duration, sampling_frequency=self.sampling_frequency, - frequency_domain_source_model=bilby.gw.source.lal_binary_black_hole_relative_binning, + frequency_domain_source_model=bilby.gw.source.binary_black_hole_frequency_sequence, start_time=1126259640, waveform_arguments=dict( reference_frequency=20.0, - minimum_frequency=20.0, waveform_approximant="IMRPhenomPv2", ) ) - self.multiband_waveform_generator = bilby.gw.WaveformGenerator( + @property + def roq_dir(self): + trial_roq_paths = [ + "/roq_basis", + os.path.join(os.path.expanduser("~"), "ROQ_data/IMRPhenomPv2/4s"), + "/home/cbc/ROQ_data/IMRPhenomPv2/4s", + ] + if "BILBY_TESTING_ROQ_DIR" in os.environ: + trial_roq_paths.insert(0, os.environ["BILBY_TESTING_ROQ_DIR"]) + for path in trial_roq_paths: + if os.path.isdir(path): + return path + raise Exception("Unable to load ROQ basis: cannot proceed with tests") + + @property + def roq_linear_matrix_file(self): + return f"{self.roq_dir}/B_linear.npy" + + @property + def roq_quadratic_matrix_file(self): + return f"{self.roq_dir}/B_quadratic.npy" + + @cached_property + def roq_waveform_generator(self): + return bilby.gw.waveform_generator.WaveformGenerator( duration=self.duration, sampling_frequency=self.sampling_frequency, - frequency_domain_source_model=bilby.gw.source.binary_black_hole_frequency_sequence, + frequency_domain_source_model=bilby.gw.source.binary_black_hole_roq, start_time=1126259640, waveform_arguments=dict( reference_frequency=20.0, waveform_approximant="IMRPhenomPv2", + frequency_nodes_linear=np.load(f"{self.roq_dir}/fnodes_linear.npy"), + frequency_nodes_quadratic=np.load(f"{self.roq_dir}/fnodes_quadratic.npy"), ) ) @@ -287,7 +297,6 @@ def tearDown(self): del self.parameters del self.interferometers del self.waveform_generator - del self.roq_waveform_generator del self.priors @classmethod From 9c4bbb02851834c3a96910adfd5f236ac47a2a80 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Thu, 22 Jan 2026 13:31:28 -0500 Subject: [PATCH 047/140] DOC: update jittedlikelihood docstring --- bilby/compat/jax.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/bilby/compat/jax.py b/bilby/compat/jax.py index f94f64cd8..af0699147 100644 --- a/bilby/compat/jax.py +++ b/bilby/compat/jax.py @@ -7,11 +7,6 @@ class JittedLikelihood(Likelihood): """ A wrapper to just-in-time compile a :code:`Bilby` likelihood for use with :code:`jax`. - .. note:: - - This is currently hardcoded to return the log likelihood ratio, regardless of - the input. - Parameters ========== likelihood: bilby.core.likelihood.Likelihood From 18d486bb8a3f546db001d4dd207930d04182c476 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Thu, 22 Jan 2026 14:34:39 -0500 Subject: [PATCH 048/140] TEST: speed up initializing prior tests --- test/core/prior/prior_test.py | 44 +++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/test/core/prior/prior_test.py b/test/core/prior/prior_test.py index 43ce08fe5..2b0500db8 100644 --- a/test/core/prior/prior_test.py +++ b/test/core/prior/prior_test.py @@ -6,9 +6,28 @@ from scipy.integrate import trapezoid +aligned_prior_complex = bilby.gw.prior.AlignedSpin( + a_prior=bilby.core.prior.Beta(alpha=2.0, beta=2.0), + z_prior=bilby.core.prior.Beta(alpha=2.0, beta=2.0, minimum=-1), + name="test", + unit="unit", + num_interp=1000, +) + +hp_map_file = os.path.join( + os.path.dirname(os.path.realpath(__file__)), + "prior_files/GW150914_testing_skymap.fits", +) +hp_dist = bilby.gw.prior.HealPixMapPriorDist( + hp_map_file, names=["testra", "testdec"] +) +hp_3d_dist = bilby.gw.prior.HealPixMapPriorDist( + hp_map_file, names=["testra", "testdec", "testdistance"], distance=True +) + + class TestPriorClasses(unittest.TestCase): def setUp(self): - # set multivariate Gaussian mvg = bilby.core.prior.MultivariateGaussianDist( names=["testa", "testb"], @@ -22,16 +41,6 @@ def setUp(self): covs=np.array([[2.0, 0.5], [0.5, 2.0]]), weights=1.0, ) - hp_map_file = os.path.join( - os.path.dirname(os.path.realpath(__file__)), - "prior_files/GW150914_testing_skymap.fits", - ) - hp_dist = bilby.gw.prior.HealPixMapPriorDist( - hp_map_file, names=["testra", "testdec"] - ) - hp_3d_dist = bilby.gw.prior.HealPixMapPriorDist( - hp_map_file, names=["testra", "testdec", "testdistance"], distance=True - ) def condition_func(reference_params, test_param): return reference_params.copy() @@ -102,13 +111,7 @@ def condition_func(reference_params, test_param): name="test", unit="unit", minimum=1e-2, maximum=1e2 ), bilby.gw.prior.AlignedSpin(name="test", unit="unit"), - bilby.gw.prior.AlignedSpin( - a_prior=bilby.core.prior.Beta(alpha=2.0, beta=2.0), - z_prior=bilby.core.prior.Beta(alpha=2.0, beta=2.0, minimum=-1), - name="test", - unit="unit", - num_interp=1000, - ), + aligned_prior_complex, bilby.core.prior.MultivariateGaussian(dist=mvg, name="testa", unit="unit"), bilby.core.prior.MultivariateGaussian(dist=mvg, name="testb", unit="unit"), bilby.core.prior.MultivariateNormal(dist=mvn, name="testa", unit="unit"), @@ -861,15 +864,16 @@ def test_jax_methods(self): for prior in self.priors: if bilby.core.prior.JointPrior in prior.__class__.__mro__: continue - print(prior) scaled = prior.rescale(points) assert isinstance(scaled, jax.Array) if isinstance(prior, bilby.core.prior.DeltaFunction): continue - assert max(abs(prior.cdf(scaled) - points)) < 1e-6 probs = prior.prob(scaled) assert min(probs) > 0 assert max(abs(jax.numpy.log(probs) - prior.ln_prob(scaled))) < 1e-6 + if isinstance(prior, bilby.core.prior.WeightedDiscreteValues): + continue + assert max(abs(prior.cdf(scaled) - points)) < 1e-6 if __name__ == "__main__": From c258a62b79bf3474010afe661e9ccb12bc3ad9d6 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Thu, 22 Jan 2026 14:34:52 -0500 Subject: [PATCH 049/140] BUG: fix some test failures --- bilby/core/prior/analytical.py | 57 ++++++++++++++++---------------- bilby/core/prior/interpolated.py | 6 ++-- bilby/gw/prior.py | 2 +- 3 files changed, 33 insertions(+), 32 deletions(-) diff --git a/bilby/core/prior/analytical.py b/bilby/core/prior/analytical.py index 76262dcf2..ca48e3393 100644 --- a/bilby/core/prior/analytical.py +++ b/bilby/core/prior/analytical.py @@ -13,6 +13,7 @@ stdtr, stdtrit, xlogy, + xlog1py, ) from .base import Prior @@ -369,7 +370,7 @@ def ln_prob(self, val, *, xp=np): @xp_wrap def cdf(self, val, *, xp=np): asymmetric = xp.log(abs(val) / self.minimum) / xp.log(self.maximum / self.minimum) - return 0.5 * (1 + xp.sign(val) * asymmetric) + return xp.clip(0.5 * (1 + xp.sign(val) * asymmetric), 0, 1) class Cosine(Prior): @@ -989,7 +990,7 @@ def prob(self, val, *, xp=np): ======= Union[float, array_like]: Prior probability of val """ - return xp.exp(self.ln_prob(val)) + return xp.exp(self.ln_prob(val, xp=xp)) @xp_wrap def ln_prob(self, val, *, xp=np): @@ -1003,13 +1004,9 @@ def ln_prob(self, val, *, xp=np): ======= Union[float, array_like]: Prior probability of val """ - _ln_prob = ( - xlogy(xp.asarray(self.alpha - 1), val - self.minimum) - + xlogy(xp.asarray(self.beta - 1), self.maximum - val) - - betaln(self.alpha, self.beta) - - xlogy(self.alpha + self.beta - 1, self.maximum - self.minimum) - ) - return xp.nan_to_num(_ln_prob, nan=-xp.inf, neginf=-xp.inf, posinf=-xp.inf) + ln_prob = xlog1py(self.beta - 1.0, -val) + xlogy(self.alpha - 1.0, val) + ln_prob -= betaln(self.alpha, self.beta) + return xp.nan_to_num(ln_prob, nan=-xp.inf, neginf=-xp.inf, posinf=-xp.inf) @xp_wrap def cdf(self, val, *, xp=np): @@ -1442,20 +1439,21 @@ def __init__( The unit of the parameter. Used for plotting. """ + xp = array_module(values) nvalues = len(values) - values = np.array(values) + values = xp.array(values) if values.shape != (nvalues,): raise ValueError( f"Shape of argument 'values' must be 1d array-like but has shape {values.shape}" ) - minimum = np.min(values) + minimum = xp.min(values) # Small delta added to help with MCMC walking - maximum = np.max(values) * (1 + 1e-15) + maximum = xp.max(values) * (1 + 1e-15) super(WeightedDiscreteValues, self).__init__( name=name, latex_label=latex_label, minimum=minimum, maximum=maximum, unit=unit, boundary=boundary) self.nvalues = nvalues - sorter = np.argsort(values) + sorter = xp.argsort(values) self._values_array = values[sorter] # inititialization of priors from repr only supports @@ -1463,9 +1461,9 @@ def __init__( self.values = self._values_array.tolist() weights = ( - np.array(weights) / np.sum(weights) + xp.array(weights) / xp.sum(weights) if weights is not None - else np.ones(self.nvalues) / self.nvalues + else xp.ones(self.nvalues) / self.nvalues ) # check for consistent shape of input if weights.shape != (self.nvalues,): @@ -1476,14 +1474,15 @@ def __init__( ) self._weights_array = weights[sorter] self.weights = self._weights_array.tolist() - self._lnweights_array = np.log(self._weights_array) + self._lnweights_array = xp.log(self._weights_array) # save cdf for rescaling - _cumulative_weights_array = np.cumsum(self._weights_array) + _cumulative_weights_array = xp.cumsum(self._weights_array) # insert 0 for values smaller than minimum - self._cumulative_weights_array = np.insert(_cumulative_weights_array, 0, 0) + self._cumulative_weights_array = xp.insert(_cumulative_weights_array, 0, 0) - def rescale(self, val): + @xp_wrap + def rescale(self, val, *, xp=np): """ 'Rescale' a sample from the unit line element to the discrete-value prior. @@ -1498,10 +1497,11 @@ def rescale(self, val): ======= Union[float, array_like]: Rescaled probability """ - index = np.searchsorted(self._cumulative_weights_array[1:], val) - return self._values_array[index] + index = xp.searchsorted(self._cumulative_weights_array[1:], val) + return xp.asarray(self._values_array)[index] - def cdf(self, val): + @xp_wrap + def cdf(self, val, *, xp=np): """Return the cumulative prior probability of val. Parameters @@ -1512,10 +1512,11 @@ def cdf(self, val): ======= float: cumulative prior probability of val """ - index = np.searchsorted(self._values_array, val, side="right") - return self._cumulative_weights_array[index] + index = xp.searchsorted(self._values_array, val, side="right") + return xp.asarray(self._cumulative_weights_array)[index] - def prob(self, val): + @xp_wrap + def prob(self, val, *, xp=np): """Return the prior probability of val. Parameters @@ -1526,9 +1527,9 @@ def prob(self, val): ======= float: Prior probability of val """ - index = np.searchsorted(self._values_array, val) - index = np.clip(index, 0, self.nvalues - 1) - p = np.where(self._values_array[index] == val, self._weights_array[index], 0) + index = xp.searchsorted(self._values_array, val) + index = xp.clip(index, 0, self.nvalues - 1) + p = xp.where(self._values_array[index] == val, self._weights_array[index], 0) # turn 0d numpy array to scalar return p[()] diff --git a/bilby/core/prior/interpolated.py b/bilby/core/prior/interpolated.py index 57e04738d..ab03809d1 100644 --- a/bilby/core/prior/interpolated.py +++ b/bilby/core/prior/interpolated.py @@ -77,10 +77,10 @@ def prob(self, val): ======= Union[float, array_like]: Prior probability of val """ - return self.probability_density(val) + return self.probability_density(val)[()] def cdf(self, val): - return self.cumulative_distribution(val) + return self.cumulative_distribution(val)[()] @xp_wrap def rescale(self, val, *, xp=np): @@ -89,7 +89,7 @@ def rescale(self, val, *, xp=np): This maps to the inverse CDF. This is done using interpolation. """ - return self.inverse_cumulative_distribution(val) + return self.inverse_cumulative_distribution(val)[()] @property def minimum(self): diff --git a/bilby/gw/prior.py b/bilby/gw/prior.py index 04c1d0db8..3258d37d1 100644 --- a/bilby/gw/prior.py +++ b/bilby/gw/prior.py @@ -512,7 +512,7 @@ def integrand(aa, chi): after performing the integral over spin orientation using a delta function identity. """ - return a_prior.prob(aa) * z_prior.prob(chi / aa) / aa + return a_prior.prob(aa, xp=np) * z_prior.prob(chi / aa, xp=np) / aa self.num_interp = 10_000 if num_interp is None else num_interp xx = np.linspace(chi_min, chi_max, self.num_interp) From a0caae91d3fbdf766ec08f05ba6b6892ab7c703c Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Thu, 22 Jan 2026 14:44:43 -0500 Subject: [PATCH 050/140] BUG: fix conditional+joint prior rescaling --- bilby/core/prior/dict.py | 8 +++++++- test/core/prior/conditional_test.py | 8 +++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/bilby/core/prior/dict.py b/bilby/core/prior/dict.py index b732c7082..eb02ae841 100644 --- a/bilby/core/prior/dict.py +++ b/bilby/core/prior/dict.py @@ -888,7 +888,13 @@ def rescale(self, keys, theta): result[key] = self[key].rescale( theta[index], **self.get_required_variables(key) ) - self[key].least_recently_sampled = result[key] + if isinstance(self[key], JointPrior) and result[key] is not None: + for key, val in zip(self[key].dist.names, result[key]): + self[key].least_recently_sampled = val + result[key] = val + else: + self[key].least_recently_sampled = result[key] + return xp.array([result[key] for key in keys]) def _update_rescale_keys(self, keys): diff --git a/test/core/prior/conditional_test.py b/test/core/prior/conditional_test.py index 68db12ed7..e7e5ec670 100644 --- a/test/core/prior/conditional_test.py +++ b/test/core/prior/conditional_test.py @@ -349,8 +349,10 @@ def test_rescale_with_joint_prior(self): ) ) - ref_variables = list(self.test_sample.values()) + [0.4, 0.1] - keys = list(self.test_sample.keys()) + names + ref_variables = list(self.test_sample.values()) + ref_variables = ref_variables[:2] + [0.1] + ref_variables[2:] + [0.4] + keys = list(self.test_sample.keys()) + keys = keys[:2] + ["mvgvar_0"] + keys[2:] + ["mvgvar_1"] res = priordict.rescale(keys=keys, theta=ref_variables) self.assertEqual(np.shape(res), (6,)) @@ -360,7 +362,7 @@ def test_rescale_with_joint_prior(self): expected = [self.test_sample["var_0"]] for ii in range(1, 4): expected.append(expected[-1] * self.test_sample[f"var_{ii}"]) - np.testing.assert_array_equal(expected, res[:4]) + np.testing.assert_array_equal(expected, list(res)[:2] + list(res)[3:5]) def test_cdf(self): """ From c0e7dc0de11660dc46796f1bcca87826a4afd460 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Thu, 22 Jan 2026 14:57:03 -0500 Subject: [PATCH 051/140] BUG: fix some gnarly conversion corner cases --- bilby/compat/utils.py | 11 ++++++++++- bilby/gw/conversion.py | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/bilby/compat/utils.py b/bilby/compat/utils.py index 98fae293d..057f6eb1c 100644 --- a/bilby/compat/utils.py +++ b/bilby/compat/utils.py @@ -1,3 +1,5 @@ +from collections.abc import Iterable + import numpy as np from array_api_compat import array_namespace @@ -10,7 +12,14 @@ def array_module(arr): try: return array_namespace(arr) except TypeError: - if arr.__class__.__module__ == "builtins": + if isinstance(arr, dict): + try: + return array_namespace(*[val for val in arr.values() if not isinstance(val, str)]) + except TypeError: + return np + elif arr.__class__.__module__ == "builtins" and isinstance(arr, Iterable): + return array_namespace(arr) + elif arr.__class__.__module__ == "builtins": return np elif arr.__module__.startswith("pandas"): return np diff --git a/bilby/gw/conversion.py b/bilby/gw/conversion.py index 55f81372c..9ce14458b 100644 --- a/bilby/gw/conversion.py +++ b/bilby/gw/conversion.py @@ -241,7 +241,7 @@ def convert_to_lal_binary_black_hole_parameters(parameters): """ converted_parameters = parameters.copy() original_keys = list(converted_parameters.keys()) - xp = array_module(parameters.values()) + xp = array_module(parameters) if 'luminosity_distance' not in original_keys: if 'redshift' in converted_parameters.keys(): converted_parameters['luminosity_distance'] = \ @@ -2114,7 +2114,7 @@ def generate_spin_parameters(sample): output_sample = sample.copy() output_sample = generate_component_spins(output_sample) - xp = array_module(sample["spin_1z"]) + xp = array_module(sample) output_sample['chi_eff'] = (output_sample['spin_1z'] + output_sample['spin_2z'] * From 6949e7f6f0f8934da416fd6186b57120469b27f2 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Thu, 22 Jan 2026 15:28:47 -0500 Subject: [PATCH 052/140] BUG: fix multiband likelihood --- bilby/gw/detector/calibration.py | 5 +++-- bilby/gw/likelihood/multiband.py | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/bilby/gw/detector/calibration.py b/bilby/gw/detector/calibration.py index 28e9c784e..6f7390bde 100644 --- a/bilby/gw/detector/calibration.py +++ b/bilby/gw/detector/calibration.py @@ -362,8 +362,9 @@ def get_calibration_factor(self, frequency_array, **params): calibration_factor : array-like The factor to multiply the strain by. """ - log10f_per_deltalog10f = ( - np.log10(frequency_array) - self.log_spline_points[0] + log10f_per_deltalog10f = np.nan_to_num( + np.log10(frequency_array) - self.log_spline_points[0], + neginf=0.0, ) / self.delta_log_spline_points previous_nodes = np.clip(np.floor(log10f_per_deltalog10f).astype(int), a_min=0, a_max=self.n_points - 2) b = log10f_per_deltalog10f - previous_nodes diff --git a/bilby/gw/likelihood/multiband.py b/bilby/gw/likelihood/multiband.py index a11ae7ab3..09f9faeb1 100644 --- a/bilby/gw/likelihood/multiband.py +++ b/bilby/gw/likelihood/multiband.py @@ -768,7 +768,7 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array if self.linear_interpolation: optimal_snr_squared = xp.vdot( - (strain * strain.conjugate()).real, + xp.abs(strain)**2, self.quadratic_coeffs[interferometer.name] ) else: @@ -779,7 +779,7 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array Mb = self.Mbs[b] if b == 0: optimal_snr_squared += (4. / self.interferometers.duration) * xp.vdot( - (strain[start_idx:end_idx + 1] * strain[start_idx:end_idx + 1].conjugate()).real, + xp.abs(strain[start_idx:end_idx + 1])**2, interferometer.frequency_mask[Ks:Ke + 1] * self.windows[start_idx:end_idx + 1] / interferometer.power_spectral_density_array[Ks:Ke + 1]) else: @@ -789,7 +789,7 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array self.hbcs[interferometer.name][b][-Mb:] = xp.fft.irfft(self.wths[interferometer.name][b]) thbc = xp.fft.rfft(self.hbcs[interferometer.name][b]) optimal_snr_squared += (4. / self.Tbhats[b]) * xp.vdot( - thbc * np.conjugate(thbc).real, self.Ibcs[interferometer.name][b]) + xp.abs(thbc)**2, self.Ibcs[interferometer.name][b]) complex_matched_filter_snr = d_inner_h / (optimal_snr_squared**0.5) From 97c87e361fc803a1d47e513fc6210a94f867eb01 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Thu, 22 Jan 2026 15:48:43 -0500 Subject: [PATCH 053/140] BUG: fix bug in array_namespace check --- bilby/compat/utils.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bilby/compat/utils.py b/bilby/compat/utils.py index 057f6eb1c..4b099969b 100644 --- a/bilby/compat/utils.py +++ b/bilby/compat/utils.py @@ -18,7 +18,10 @@ def array_module(arr): except TypeError: return np elif arr.__class__.__module__ == "builtins" and isinstance(arr, Iterable): - return array_namespace(arr) + try: + return array_namespace(*arr) + except TypeError: + return np elif arr.__class__.__module__ == "builtins": return np elif arr.__module__.startswith("pandas"): From c3b6830ebf298bbf35761db09edbf601de75a466 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Thu, 22 Jan 2026 15:57:44 -0500 Subject: [PATCH 054/140] TEST: make sure healpix prior doesn't store state between calls --- test/core/prior/prior_test.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/core/prior/prior_test.py b/test/core/prior/prior_test.py index 2b0500db8..bed42cf19 100644 --- a/test/core/prior/prior_test.py +++ b/test/core/prior/prior_test.py @@ -42,6 +42,10 @@ def setUp(self): weights=1.0, ) + # need to reset this for the repr test to get equality correct + hp_dist.requested_parameters = {"testra": None, "testdec": None} + hp_3d_dist.requested_parameters = {"testra": None, "testdec": None, "testdistance": None} + def condition_func(reference_params, test_param): return reference_params.copy() From dca5bd3abcf17d6f007056b1198851dc2dccbf1b Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Thu, 22 Jan 2026 16:03:15 -0500 Subject: [PATCH 055/140] FMT: example formatting fixes --- .../injection_examples/jax_fast_tutorial.py | 38 ++++++++++++++----- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/examples/gw_examples/injection_examples/jax_fast_tutorial.py b/examples/gw_examples/injection_examples/jax_fast_tutorial.py index b3eb23a55..6f6654536 100644 --- a/examples/gw_examples/injection_examples/jax_fast_tutorial.py +++ b/examples/gw_examples/injection_examples/jax_fast_tutorial.py @@ -15,9 +15,9 @@ os.environ["OMP_NUM_THREADS"] = "1" import bilby -import numpy as np import jax import jax.numpy as jnp +import numpy as np from bilby.compat.jax import JittedLikelihood from ripple.waveforms import IMRPhenomPv2 @@ -49,8 +49,19 @@ def bilby_to_ripple_spins( def ripple_bbh( - frequency, mass_1, mass_2, luminosity_distance, theta_jn, phase, - a_1, a_2, tilt_1, tilt_2, phi_12, phi_jl, **kwargs, + frequency, + mass_1, + mass_2, + luminosity_distance, + theta_jn, + phase, + a_1, + a_2, + tilt_1, + tilt_2, + phi_12, + phi_jl, + **kwargs, ): """ Source function wrapper to ripple's IMRPhenomPv2 waveform generator. @@ -85,21 +96,27 @@ def ripple_bbh( Azimuthal angle of the total angular momentum vector in radians. **kwargs Additional keyword arguments. Must include 'minimum_frequency'. - + Returns ------- dict Dictionary containing the plus and cross polarizations of the waveform. """ iota, *cartesian_spins = bilby_to_ripple_spins( - # iota, spin_1x, spin_1y, spin_1z, spin_2x, spin_2y, spin_2z = bilby_to_ripple_spins( theta_jn, phi_jl, tilt_1, tilt_2, phi_12, a_1, a_2 ) frequencies = jnp.maximum(frequency, kwargs["minimum_frequency"]) - theta = jnp.array([ - mass_1, mass_2, *cartesian_spins, - luminosity_distance, jnp.array(0.0), phase, iota - ]) + theta = jnp.array( + [ + mass_1, + mass_2, + *cartesian_spins, + luminosity_distance, + jnp.array(0.0), + phase, + iota, + ] + ) wf_func = jax.jit(IMRPhenomPv2.gen_IMRPhenomPv2) hp, hc = wf_func(frequencies, theta, jnp.array(20.0)) return dict(plus=hp, cross=hc) @@ -160,7 +177,8 @@ def main(): start_time=injection_parameters["geocent_time"] - duration + 2, ) ifos.inject_signal( - waveform_generator=waveform_generator, parameters=injection_parameters, + waveform_generator=waveform_generator, + parameters=injection_parameters, raise_error=False, ) ifos.set_array_backend(jnp) From 22f376ed556d37253fc5cedd4358f2eeb17c72b8 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Thu, 22 Jan 2026 16:33:32 -0500 Subject: [PATCH 056/140] BUG: make sure indices don't overflow in roq --- bilby/gw/likelihood/roq.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bilby/gw/likelihood/roq.py b/bilby/gw/likelihood/roq.py index a5c84b3a1..d8e894d53 100644 --- a/bilby/gw/likelihood/roq.py +++ b/bilby/gw/likelihood/roq.py @@ -485,6 +485,7 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array indices, in_bounds = self._closest_time_indices( ifo_time, self.weights['time_samples']) + indices = xp.clip(indices, 0, len(self.weights['time_samples']) - 1) d_inner_h_tc_array = xp.einsum( 'i,ji->j', xp.conjugate(h_linear), From e9910807c08f5f40d3826c3912c54de6f8b6a2bf Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Fri, 23 Jan 2026 08:48:10 -0500 Subject: [PATCH 057/140] BUG: fix multiband time marginalization setup --- bilby/gw/likelihood/multiband.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bilby/gw/likelihood/multiband.py b/bilby/gw/likelihood/multiband.py index 09f9faeb1..c805627ce 100644 --- a/bilby/gw/likelihood/multiband.py +++ b/bilby/gw/likelihood/multiband.py @@ -710,20 +710,20 @@ def setup_multibanding_from_weights(self, weights): setattr(self, key, value) def _setup_time_marginalization_multiband(self): - """This overwrites attributes set by _setup_time_marginalization of the base likelihood class""" + self._beam_pattern_reference_time = ( + self.priors['geocent_time'].minimum + self.priors['geocent_time'].maximum + ) / 2 N = self.Nbs[-1] // 2 self._delta_tc = self.durations[0] / N - self._times = \ - self.interferometers.start_time + np.arange(N) * self._delta_tc + self._times = ( + np.arange(N) * self._delta_tc + (self._beam_pattern_reference_time - self.interferometers.start_time) + ) % self.interferometers.duration + self.interferometers.start_time self.time_prior_array = \ self.priors['geocent_time'].prob(self._times) * self._delta_tc # allocate array which is FFTed at each likelihood evaluation self._full_d_h = np.zeros(N, dtype=complex) # idxs to convert full frequency points to banded frequency points, used for filling _full_d_h. self._full_to_multiband = [int(f * self.durations[0]) for f in self.banded_frequency_points] - self._beam_pattern_reference_time = ( - self.priors['geocent_time'].minimum + self.priors['geocent_time'].maximum - ) / 2 self.interferometers.reference_time = self._beam_pattern_reference_time def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array=True, parameters): From 4024d7047852f5773d1aea00004b3449177dfd5a Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Fri, 23 Jan 2026 08:48:32 -0500 Subject: [PATCH 058/140] BUG: fix roq interpolation for out of bounds sample --- bilby/gw/likelihood/roq.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bilby/gw/likelihood/roq.py b/bilby/gw/likelihood/roq.py index d8e894d53..f85b72668 100644 --- a/bilby/gw/likelihood/roq.py +++ b/bilby/gw/likelihood/roq.py @@ -565,9 +565,10 @@ def _interp_five_samples(time_samples, values, time): value: float The value of the function at the input time """ + xp = time_samples.__array_namespace__() r1 = (-values[0] + 8. * values[1] - 14. * values[2] + 8. * values[3] - values[4]) / 4. r2 = values[2] - 2. * values[3] + values[4] - a = (time_samples[3] - time) / (time_samples[1] - time_samples[0]) + a = (time_samples[3] - time) / xp.maximum(time_samples[1] - time_samples[0], 1e-12) b = 1. - a c = (a**3. - a) / 6. d = (b**3. - b) / 6. From 95497987a4d490985bda88627d348b94f2fa6021 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Fri, 23 Jan 2026 08:48:47 -0500 Subject: [PATCH 059/140] TYPO: fix typo in jax example --- examples/gw_examples/injection_examples/jax_fast_tutorial.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gw_examples/injection_examples/jax_fast_tutorial.py b/examples/gw_examples/injection_examples/jax_fast_tutorial.py index 6f6654536..56b1b4d3a 100644 --- a/examples/gw_examples/injection_examples/jax_fast_tutorial.py +++ b/examples/gw_examples/injection_examples/jax_fast_tutorial.py @@ -134,7 +134,7 @@ def main(): # Specify the output directory and the name of the simulation. outdir = "outdir" - label = f"jax_fast_tutorial" + label = "jax_fast_tutorial" # Set up a random seed for result reproducibility. This is optional! bilby.core.utils.random.seed(88170235) From 0397ba6d3616e8911e5e81e270a96b4467843f22 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Fri, 23 Jan 2026 08:49:22 -0500 Subject: [PATCH 060/140] REFACTOR: refactor more roq likelihood tests --- test/gw/likelihood_test.py | 126 ++++++++++++++++++++----------------- 1 file changed, 68 insertions(+), 58 deletions(-) diff --git a/test/gw/likelihood_test.py b/test/gw/likelihood_test.py index 9d7a7e36f..38310d6df 100644 --- a/test/gw/likelihood_test.py +++ b/test/gw/likelihood_test.py @@ -1,6 +1,7 @@ import os import unittest import tempfile +from functools import cached_property from itertools import product from parameterized import parameterized import pytest @@ -276,31 +277,6 @@ def setUp(self): self.duration = 4 self.sampling_frequency = 2048 - # Possible locations for the ROQ: in the docker image, local, or on CIT - trial_roq_paths = [ - "/roq_basis", - os.path.join(os.path.expanduser("~"), "ROQ_data/IMRPhenomPv2/4s"), - "/home/cbc/ROQ_data/IMRPhenomPv2/4s", - ] - roq_dir = None - for path in trial_roq_paths: - if os.path.isdir(path): - roq_dir = path - break - if roq_dir is None: - raise Exception("Unable to load ROQ basis: cannot proceed with tests") - - linear_matrix_file = "{}/B_linear.npy".format(roq_dir) - quadratic_matrix_file = "{}/B_quadratic.npy".format(roq_dir) - - fnodes_linear_file = "{}/fnodes_linear.npy".format(roq_dir) - fnodes_linear = np.load(fnodes_linear_file).T - fnodes_quadratic_file = "{}/fnodes_quadratic.npy".format(roq_dir) - fnodes_quadratic = np.load(fnodes_quadratic_file).T - self.linear_matrix_file = "{}/B_linear.npy".format(roq_dir) - self.quadratic_matrix_file = "{}/B_quadratic.npy".format(roq_dir) - self.params_file = "{}/params.dat".format(roq_dir) - self.test_parameters = dict( mass_1=36.0, mass_2=36.0, @@ -349,20 +325,6 @@ def setUp(self): self.ifos = ifos - roq_wfg = bilby.gw.waveform_generator.WaveformGenerator( - duration=self.duration, - sampling_frequency=self.sampling_frequency, - frequency_domain_source_model=bilby.gw.source.binary_black_hole_roq, - waveform_arguments=dict( - frequency_nodes_linear=fnodes_linear, - frequency_nodes_quadratic=fnodes_quadratic, - reference_frequency=20.0, - waveform_approximant="IMRPhenomPv2", - ), - ) - - self.roq_wfg = roq_wfg - self.non_roq = bilby.gw.likelihood.GravitationalWaveTransient( interferometers=ifos, waveform_generator=non_roq_wfg ) @@ -374,33 +336,81 @@ def setUp(self): priors=self.priors.copy(), ) - self.roq = bilby.gw.likelihood.ROQGravitationalWaveTransient( - interferometers=ifos, - waveform_generator=roq_wfg, - linear_matrix=linear_matrix_file, - quadratic_matrix=quadratic_matrix_file, - priors=self.priors, - ) - - self.roq_phase = bilby.gw.likelihood.ROQGravitationalWaveTransient( - interferometers=ifos, - waveform_generator=roq_wfg, - linear_matrix=linear_matrix_file, - quadratic_matrix=quadratic_matrix_file, - phase_marginalization=True, - priors=self.priors.copy(), - ) - def tearDown(self): del ( - self.roq, self.non_roq, self.non_roq_phase, - self.roq_phase, self.ifos, self.priors, ) + @property + def roq_dir(self): + trial_roq_paths = [ + "/roq_basis", + os.path.join(os.path.expanduser("~"), "ROQ_data/IMRPhenomPv2/4s"), + "/home/cbc/ROQ_data/IMRPhenomPv2/4s", + ] + if "BILBY_TESTING_ROQ_DIR" in os.environ: + trial_roq_paths.insert(0, os.environ["BILBY_TESTING_ROQ_DIR"]) + print(trial_roq_paths) + for path in trial_roq_paths: + print(path, os.path.isdir(path)) + if os.path.isdir(path): + return path + raise Exception("Unable to load ROQ basis: cannot proceed with tests") + + @property + def linear_matrix_file(self): + return f"{self.roq_dir}/B_linear.npy" + + @property + def quadratic_matrix_file(self): + return f"{self.roq_dir}/B_quadratic.npy" + + @property + def params_file(self): + return f"{self.roq_dir}/params.dat" + + @cached_property + def roq_wfg(self): + fnodes_linear_file = f"{self.roq_dir}/fnodes_linear.npy" + fnodes_quadratic_file = f"{self.roq_dir}/fnodes_quadratic.npy" + fnodes_linear = np.load(fnodes_linear_file).T + fnodes_quadratic = np.load(fnodes_quadratic_file).T + return bilby.gw.waveform_generator.WaveformGenerator( + duration=self.duration, + sampling_frequency=self.sampling_frequency, + frequency_domain_source_model=bilby.gw.source.binary_black_hole_roq, + waveform_arguments=dict( + frequency_nodes_linear=fnodes_linear, + frequency_nodes_quadratic=fnodes_quadratic, + reference_frequency=20.0, + waveform_approximant="IMRPhenomPv2", + ), + ) + + @cached_property + def roq(self): + return bilby.gw.likelihood.ROQGravitationalWaveTransient( + interferometers=self.ifos, + waveform_generator=self.roq_wfg, + linear_matrix=self.linear_matrix_file, + quadratic_matrix=self.quadratic_matrix_file, + priors=self.priors, + ) + + @cached_property + def roq_phase(self): + return bilby.gw.likelihood.ROQGravitationalWaveTransient( + interferometers=self.ifos, + waveform_generator=self.roq_wfg, + linear_matrix=self.linear_matrix_file, + quadratic_matrix=self.quadratic_matrix_file, + phase_marginalization=True, + priors=self.priors.copy(), + ) + def test_matches_non_roq(self): self.assertLess( abs( From 0ee5f14b96830018871950e493c1e0e0fc797559 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Fri, 23 Jan 2026 10:33:20 -0500 Subject: [PATCH 061/140] MAINT: revert new conversions --- bilby/gw/conversion.py | 45 ------------------------------------- bilby/gw/likelihood/base.py | 32 +++++++++++--------------- 2 files changed, 13 insertions(+), 64 deletions(-) diff --git a/bilby/gw/conversion.py b/bilby/gw/conversion.py index 9ce14458b..3dcb6962c 100644 --- a/bilby/gw/conversion.py +++ b/bilby/gw/conversion.py @@ -179,40 +179,6 @@ def transform_precessing_spins(*args): return lalsim_SimInspiralTransformPrecessingNewInitialConditions(*args) -def convert_orientation_quaternion(parameters): - xp = array_module(parameters["orientation_w"]) - norm = ( - parameters["orientation_w"]**2 - + parameters["orientation_x"]**2 - + parameters["orientation_y"]**2 - + parameters["orientation_z"]**2 - )**0.5 - parameters["theta_jn"] = 2 * xp.arccos( - parameters["orientation_z"] / norm - ) - parameters["psi"] = xp.arctan2( - parameters["orientation_w"], - parameters["orientation_y"] - + parameters["orientation_x"], - ) / 2 - parameters["delta_phase"] = xp.arctan2( - parameters["orientation_y"], - parameters["orientation_x"], - ) / 2 - - -def convert_cartesian(parameters, label): - spin_norm = ( - parameters[f"{label}_x"]**2 - + parameters[f"{label}_y"]**2 - + parameters[f"{label}_z"]**2 - )**0.5 - xp = array_module(spin_norm) - zenith = xp.arccos(parameters[f"{label}_z"] / spin_norm) - azimuth = xp.arctan2(parameters[f"{label}_y"], parameters[f"{label}_x"]) - return zenith, azimuth - - def convert_to_lal_binary_black_hole_parameters(parameters): """ Convert parameters we have into parameters we need. @@ -264,14 +230,6 @@ def convert_to_lal_binary_black_hole_parameters(parameters): converted_parameters = generate_component_masses(converted_parameters, require_add=False) for idx in ['1', '2']: - if f"spin_{idx}_x" in original_keys: - converted_parameters["tilt_1"], converted_parameters["phi_jl"] = ( - convert_cartesian(converted_parameters, "spin_1") - ) - converted_parameters["tilt_2"], converted_parameters["phi_12"] = ( - convert_cartesian(converted_parameters, "spin_2") - ) - converted_parameters["phi_12"] -= converted_parameters["phi_jl"] key = 'chi_{}'.format(idx) if key in original_keys: if "chi_{}_in_plane".format(idx) in original_keys: @@ -302,9 +260,6 @@ def convert_to_lal_binary_black_hole_parameters(parameters): ) converted_parameters[f"cos_tilt_{idx}"] = 1.0 - if "orientation_w" in original_keys: - convert_orientation_quaternion(converted_parameters) - for key in ["phi_jl", "phi_12"]: if key not in converted_parameters: converted_parameters[key] = 0.0 diff --git a/bilby/gw/likelihood/base.py b/bilby/gw/likelihood/base.py index 7a8a39f75..269b28496 100644 --- a/bilby/gw/likelihood/base.py +++ b/bilby/gw/likelihood/base.py @@ -1104,26 +1104,20 @@ def get_sky_frame_parameters(self, parameters): "Falling back to geocent time" ) if not self.reference_frame == "sky": - if "sky_x" in parameters: - zenith, azimuth = convert_cartesian(parameters, "sky") - elif "zenith" in parameters: - zenith = parameters["zenith"] - azimuth = parameters["azimuth"] - elif "ra" in parameters and "dec" in parameters: - ra = parameters["ra"] - dec = parameters["dec"] - logger.warning( - "Cannot convert from zenith/azimuth to ra/dec falling " - "back to provided ra/dec" - ) - zenith = None - else: - raise KeyError("No sky location parameters recognised") - if zenith is not None: + try: ra, dec = zenith_azimuth_to_ra_dec( - zenith, azimuth, time, self.reference_frame - ) - else: + parameters['zenith'], parameters['azimuth'], + time, self.reference_frame) + except KeyError: + if "ra" in parameters and "dec" in parameters: + ra = parameters["ra"] + dec = parameters["dec"] + logger.warning( + "Cannot convert from zenith/azimuth to ra/dec falling " + "back to provided ra/dec" + ) + else: + raise ra = parameters["ra"] dec = parameters["dec"] if "geocent" not in self.time_reference and f"{self.time_reference}_time" in parameters: From 78e6faa2a1982f18fad31b2354f2d92dcb1d1f84 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Fri, 23 Jan 2026 10:38:29 -0500 Subject: [PATCH 062/140] CI: fix selecting only non-windows os --- uv.lock | 3616 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3616 insertions(+) create mode 100644 uv.lock diff --git a/uv.lock b/uv.lock new file mode 100644 index 000000000..ce9c68897 --- /dev/null +++ b/uv.lock @@ -0,0 +1,3616 @@ +version = 1 +revision = 3 +requires-python = ">=3.10" +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.11'", +] + +[[package]] +name = "array-api-compat" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/89/e5/9a12dd1c2b0ad61f3c3ad0fc14b888c65fd735dd9d26805f77317303cbe5/array_api_compat-1.14.0.tar.gz", hash = "sha256:c819ba707f5c507800cb545f7e6348ff1ecc46538381d9ad9b371ffc9cd6d784", size = 106369, upload-time = "2026-02-26T12:02:42.452Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/d3/54cd560804a8c2b898824778e86c13c2a14600bc83532a9c4f69f2f469c3/array_api_compat-1.14.0-py3-none-any.whl", hash = "sha256:ed5af1f9b6595a199c942505f281ec994892556b6efc24679a0501e87a7d6279", size = 60124, upload-time = "2026-02-26T12:02:41.127Z" }, +] + +[[package]] +name = "array-api-extra" +version = "0.9.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "array-api-compat", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/a0/571d22e229aad80c4d200914db5218e7dae399aff097b34e8671c2fd6ff3/array_api_extra-0.9.2.tar.gz", hash = "sha256:630013895e6d1cb411ddb9a4a7aa9d3861281a448caaf4ce7a60f4284708d236", size = 87176, upload-time = "2026-01-03T13:37:45.703Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/21/2b/bfa1cfe370dd4ed51f834f2c6ad93b7f6263b83615ab96ad91094cc98ec6/array_api_extra-0.9.2-py3-none-any.whl", hash = "sha256:d0643a9a4e981746057649accad068ca0fe4066d890f6a95d8b4cd5131b3b661", size = 51056, upload-time = "2026-01-03T13:37:44.252Z" }, +] + +[[package]] +name = "array-api-extra" +version = "0.10.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "array-api-compat", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/25/77/2ef35219b7cf612366da9b26c95307d8b3b0bb0c0e9767314554a15f4117/array_api_extra-0.10.1.tar.gz", hash = "sha256:712ea45bd122d51c05085fc5fa30cdf66a10cc5d1af81d3e7beaff620ef1e1f2", size = 89510, upload-time = "2026-02-27T17:14:47.06Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/f7/9e14be985fd77ae26fee9136c9735e8987772e0ecf5f1f4e6e2b84cadc46/array_api_extra-0.10.1-py3-none-any.whl", hash = "sha256:9c2003079ccd2a0c92b1cf797b5867b9d7ea9428e75f70c7f78c1c0842d54368", size = 88377, upload-time = "2026-02-27T17:14:45.422Z" }, +] + +[[package]] +name = "astropy" +version = "6.1.7" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "astropy-iers-data", marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "packaging", marker = "python_full_version < '3.11'" }, + { name = "pyerfa", marker = "python_full_version < '3.11'" }, + { name = "pyyaml", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8a/f8/9c6675ab4c646b95aae2762d108f6be4504033d91bd50da21daa62cab5ce/astropy-6.1.7.tar.gz", hash = "sha256:a405ac186306b6cb152e6df2f7444ab8bd764e4127d7519da1b3ae4dd65357ef", size = 7063411, upload-time = "2024-11-22T21:22:34.373Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/4f/27f91eb9cdaa37835e52496dcad00fd89969ef5154795697987d031d0605/astropy-6.1.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:be954c5f7707a089609053665aeb76493b79e5c4753c39486761bc6d137bf040", size = 6531137, upload-time = "2024-11-22T21:21:30.502Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f2/fb2c6c1d31c21df0d4409ecd5e9788795be6f8f80b67008c8191488d55cf/astropy-6.1.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b5e48df5ab2e3e521e82a7233a4b1159d071e64e6cbb76c45415dc68d3b97af1", size = 6410376, upload-time = "2024-11-22T21:21:32.986Z" }, + { url = "https://files.pythonhosted.org/packages/fd/68/65ad3ea77440df2e8625d8fee585d5fc6049f33a61e49221f91d8de0e3df/astropy-6.1.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55c78252633c644361e2f7092d71f80ef9c2e6649f08d97711d9f19af514aedc", size = 9892774, upload-time = "2024-11-22T21:21:34.823Z" }, + { url = "https://files.pythonhosted.org/packages/b4/41/e366fc5baff41f7b433f07a46c053a24459e93d2912690d099f0eefabfc3/astropy-6.1.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:985e5e74489d23f1a11953b6b283fccde3f46cb6c68fee4f7228e5f6d8350ba9", size = 9962419, upload-time = "2024-11-22T21:21:36.863Z" }, + { url = "https://files.pythonhosted.org/packages/1e/a0/e6c1ef80f7e20fb600b3af742d227e6356704dbda3763ff1d76a53a0fd7b/astropy-6.1.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:dc2ea28ed41a3d92c39b1481d9c5be016ae58d68f144f3fd8cecffe503525bab", size = 9987760, upload-time = "2024-11-22T21:21:39.736Z" }, + { url = "https://files.pythonhosted.org/packages/49/93/6b23e75d690763a9d702038c74ea9a74181a278fe362fbeecea35b691e4a/astropy-6.1.7-cp310-cp310-win32.whl", hash = "sha256:4e4badadd8dfa5dca08fd86e9a50a3a91af321975859f5941579e6b7ce9ba199", size = 6274137, upload-time = "2024-11-22T21:21:42.178Z" }, + { url = "https://files.pythonhosted.org/packages/6e/e1/af92dc2132547e3998476a4b0ab19d15c50d8ec1d85e658fe6503e125fd1/astropy-6.1.7-cp310-cp310-win_amd64.whl", hash = "sha256:8d7f6727689288ee08fc0a4a297fc7e8089d01718321646bd00fea0906ad63dc", size = 6394810, upload-time = "2024-11-22T21:21:43.843Z" }, + { url = "https://files.pythonhosted.org/packages/4f/5e/d31204823764f6e5fa4820c1b4f49f8eef7cf691b796ec389f41b4f5a699/astropy-6.1.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:09edca01276ee63f7b2ff511da9bfb432068ba3242e27ef27d76e5a171087b7e", size = 6531221, upload-time = "2024-11-22T21:21:46.2Z" }, + { url = "https://files.pythonhosted.org/packages/22/e2/ae5dd6d9272e41619d85df4e4a03cf06acea8bcb44c42fe67e5cd04ae131/astropy-6.1.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:072f62a67992393beb016dc80bee8fb994fda9aa69e945f536ed8ac0e51291e6", size = 6409477, upload-time = "2024-11-22T21:21:47.862Z" }, + { url = "https://files.pythonhosted.org/packages/01/ed/9bc17beb457943ee04b8c85614ddb4a64a4a91597340dca28332e112209d/astropy-6.1.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2706156d3646f9c9a7fc810475d8ab0df4c717beefa8326552576a0f8ddca20", size = 10150734, upload-time = "2024-11-22T21:21:49.847Z" }, + { url = "https://files.pythonhosted.org/packages/39/38/1c5263f0d775def518707ccd1cf9d4df1d99d523fc148df9e38aa5ba9d54/astropy-6.1.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fcd99e627692f8e58bb3097d330bfbd109a22e00dab162a67f203b0a0601ad2c", size = 10210679, upload-time = "2024-11-22T21:21:52.011Z" }, + { url = "https://files.pythonhosted.org/packages/32/d1/7365e16b0158f755977a5bdbd329df40a9772b0423a1d5075aba9246673f/astropy-6.1.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b0ebbcb637b2e9bcb73011f2b7890d7a3f5a41b66ccaad7c28f065e81e28f0b2", size = 10245960, upload-time = "2024-11-22T21:21:54.785Z" }, + { url = "https://files.pythonhosted.org/packages/e9/b6/4dc6f9ef1c17738b8ebd8922bc1c6fec48542ccfe5124b6719737b012b8c/astropy-6.1.7-cp311-cp311-win32.whl", hash = "sha256:192b12ede49cd828362ab1a6ede2367fe203f4d851804ec22fa92e009a524281", size = 6272124, upload-time = "2024-11-22T21:21:57.504Z" }, + { url = "https://files.pythonhosted.org/packages/ba/c6/b5f33597bfbc1afad0640b20000633127dfa0a4295b607a0439f45546d9a/astropy-6.1.7-cp311-cp311-win_amd64.whl", hash = "sha256:3cac64bcdf570c947019bd2bc96711eeb2c7763afe192f18c9551e52a6c296b2", size = 6396627, upload-time = "2024-11-22T21:21:59.913Z" }, + { url = "https://files.pythonhosted.org/packages/46/2b/007c888fead170c714ecdcf56bc59e8d3252776bd3f16e1797158a46f65d/astropy-6.1.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f2a8bcbb1306052cc38c9eed2c9331bfafe2582b499a7321946abf74b26eb256", size = 6535604, upload-time = "2024-11-22T21:22:02.515Z" }, + { url = "https://files.pythonhosted.org/packages/8e/4c/cc30c9b1440f4a2f1f52845873ae3f8f7c4343261e516603a35546574ed7/astropy-6.1.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:eaf88878684f9d31aff36475c90d101f4cff22fdd4fd50098d9950fd56994df7", size = 6415117, upload-time = "2024-11-22T21:22:04.484Z" }, + { url = "https://files.pythonhosted.org/packages/12/2d/9985b8b4225c2495c4e64713d1630937c83af863db606d12676b72b4f651/astropy-6.1.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb8cd231e53556e4eebe0393ea95a8cea6b2ff4187c95ac4ff8b17e7a8da823", size = 10177861, upload-time = "2024-11-22T21:22:06.043Z" }, + { url = "https://files.pythonhosted.org/packages/b7/b6/63ccb085757638d15f0f9d6f2dffaccce7785236fe8bf23e4b380a333ce0/astropy-6.1.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ad36334d138a4f71d6fdcf225a98ad1dad6c343da4362d5a47a71f5c9da3ca9", size = 10258014, upload-time = "2024-11-22T21:22:08.164Z" }, + { url = "https://files.pythonhosted.org/packages/c8/ee/a6af891802de463f70e3fddf09f3aeb1d46dde87885e2245d25a2ac46948/astropy-6.1.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dd731c526869d0c68507be7b31dd10871b7c44d310bb5495476505560c83cd33", size = 10277363, upload-time = "2024-11-22T21:22:10.331Z" }, + { url = "https://files.pythonhosted.org/packages/dd/98/b253583f9de7033f03a7c5f5314b9e93177725a2020e0f36d338d242bf0e/astropy-6.1.7-cp312-cp312-win32.whl", hash = "sha256:662bacd7ae42561e038cbd85eea3b749308cf3575611a745b60f034d3350c97a", size = 6271741, upload-time = "2024-11-22T21:22:12.696Z" }, + { url = "https://files.pythonhosted.org/packages/7a/63/e1b5f01e6735ed8f9d62d3eed5f226bc0ab516ab8558ffaccf6d4185f91d/astropy-6.1.7-cp312-cp312-win_amd64.whl", hash = "sha256:5b4d02a98a0bf91ff7fd4ef0bd0ecca83c9497338cb88b61ec9f971350688222", size = 6396352, upload-time = "2024-11-22T21:22:14.525Z" }, + { url = "https://files.pythonhosted.org/packages/73/9d/21d2e61080a81e7e1f5e5006204a76e70588aa1a88aa9044c2d203578d07/astropy-6.1.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fbeaf04427987c0c6fa2e579eb40011802b06fba6b3a7870e082d5c693564e1b", size = 6528360, upload-time = "2024-11-22T21:22:16.275Z" }, + { url = "https://files.pythonhosted.org/packages/d5/3e/b999ec6cd607c512e66d8a138443361eb88899760c7cb8517a66155732ee/astropy-6.1.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ab6e88241a14185b9404b02246329185b70292984aa0616b20a0628dfe4f4ebb", size = 6407905, upload-time = "2024-11-22T21:22:18.035Z" }, + { url = "https://files.pythonhosted.org/packages/db/2d/44557c63688c2ed03d0d72b4f27fc30fc1ea250aeb5ebd939796c5f98bee/astropy-6.1.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0529c75565feaabb629946806b4763ae7b02069aeff4c3b56a69e8a9e638500", size = 10106849, upload-time = "2024-11-22T21:22:20.393Z" }, + { url = "https://files.pythonhosted.org/packages/66/bc/993552eb932dec528fe6b95f511e918473ea4406dee4b17c223f3fd8a919/astropy-6.1.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c5ec347631da77573fc729ba04e5d89a3bc94500bf6037152a2d0f9965ae1ce", size = 10194766, upload-time = "2024-11-22T21:22:23.116Z" }, + { url = "https://files.pythonhosted.org/packages/9f/f3/3c5282762c8a5746e7752e46a1e328c79a5d0186d96cfd0995bdf976e1f9/astropy-6.1.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dc496f87aaccaa5c6624acc985b8770f039c5bbe74b120c8ed7bad3698e24e1b", size = 10219291, upload-time = "2024-11-22T21:22:26.079Z" }, + { url = "https://files.pythonhosted.org/packages/d8/52/949bb79df9c03f56d0ae93ac62f2616fe3e67db51677bf412473bf6d077e/astropy-6.1.7-cp313-cp313-win32.whl", hash = "sha256:b1e01d534383c038dbf8664b964fa4ea818c7419318830d3c732c750c64115c6", size = 6269501, upload-time = "2024-11-22T21:22:28.468Z" }, + { url = "https://files.pythonhosted.org/packages/a1/da/f369561a67061dd42e13c7f758b393ae90319dbbcf7e301a18ce3fa43ec6/astropy-6.1.7-cp313-cp313-win_amd64.whl", hash = "sha256:af08cf2b0368f1ea585eb26a55d99a2de9e9b0bd30aba84b5329059c3ec33590", size = 6393207, upload-time = "2024-11-22T21:22:31.797Z" }, +] + +[[package]] +name = "astropy" +version = "7.2.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "astropy-iers-data", marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "packaging", marker = "python_full_version >= '3.11'" }, + { name = "pyerfa", marker = "python_full_version >= '3.11'" }, + { name = "pyyaml", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7b/92/2dce2d48347efc3346d08ca7995b152d242ebd170c571f7c9346468d8427/astropy-7.2.0.tar.gz", hash = "sha256:ae48bc26b1feaeb603cd94bd1fa1aa39137a115fe931b7f13787ab420e8c3070", size = 7057774, upload-time = "2025-11-25T22:36:41.916Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b4/6d/6330a844bad8dfc4875e0f2fa1db1fee87837ba9805aa8a8d048c071363a/astropy-7.2.0-cp311-abi3-macosx_10_9_x86_64.whl", hash = "sha256:efac04df4cc488efe630c2fff1992d6516dfb16a06e197fb68bc9e8e3b85def1", size = 6442332, upload-time = "2025-11-25T22:36:23.6Z" }, + { url = "https://files.pythonhosted.org/packages/a6/ba/3418133ba144dfcd1530bca5a6b695f4cdd21a8abaaa2ac4e5450d11b028/astropy-7.2.0-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:52e9a7d9c86b21f1af911a2930cd0c4a275fb302d455c89e11eedaffef6f2ad0", size = 6413656, upload-time = "2025-11-25T22:36:26.548Z" }, + { url = "https://files.pythonhosted.org/packages/be/ba/05e43b5a7d738316a097fa78524d3eaaff5986294b4a052d4adb3c45e7c0/astropy-7.2.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97c370421b9bb13d4c762c7af06d172bad7c01bd5bcf88314f6913c3c235b770", size = 9758867, upload-time = "2025-11-25T22:36:28.661Z" }, + { url = "https://files.pythonhosted.org/packages/c3/1c/f06ad85180e7dd9855aa5ede901bfc2be858d7bee17d4e978a14c0ecec14/astropy-7.2.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2f39ce2c80211fbceb005d377a5478cd0d66c42aa1498d252f2239fe5a025c24", size = 9789007, upload-time = "2025-11-25T22:36:31.063Z" }, + { url = "https://files.pythonhosted.org/packages/f8/fb/e4d35194a5009d7a73333079481a4ef1380a255d67b9c1db578151a5fb50/astropy-7.2.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ad4d71db994d45f046a1a5449000cf0f88ab6367cb67658500654a0586d6ab19", size = 9748547, upload-time = "2025-11-25T22:36:33.154Z" }, + { url = "https://files.pythonhosted.org/packages/36/ea/f990730978ae0a7a34705f885d2f3806928c5f0bc22eefd6a1a23539cc32/astropy-7.2.0-cp311-abi3-win32.whl", hash = "sha256:95161f26602433176483e8bde8ab1a8ca09148f5b4bf5190569a26d381091598", size = 6237228, upload-time = "2025-11-25T22:36:35.236Z" }, + { url = "https://files.pythonhosted.org/packages/ec/bc/f4378f586dd63902c37d16f68f35f7d555b3b32e08ac6b1d633eb0a48805/astropy-7.2.0-cp311-abi3-win_amd64.whl", hash = "sha256:dc7c340ba1713e55c93071b32033f3153470a0f663a4d539c03a7c9b44020790", size = 6362868, upload-time = "2025-11-25T22:36:37.784Z" }, + { url = "https://files.pythonhosted.org/packages/77/79/b6d4bf01913cfd4ce0cd4c1be5916beccdb92b2970bab8c827984231eae6/astropy-7.2.0-cp311-abi3-win_arm64.whl", hash = "sha256:0c428735a3f15b05c2095bc6ccb5f98a64bc99fb7015866af19ff8492420ddaf", size = 6221756, upload-time = "2025-11-25T22:36:39.852Z" }, +] + +[[package]] +name = "astropy-iers-data" +version = "0.2026.5.11.1.8.52" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1e/a1/186ae52e05fe90cb9a02f6fa27201e066b91d951fe6d32cd5220def2f1dd/astropy_iers_data-0.2026.5.11.1.8.52.tar.gz", hash = "sha256:11d7d6caa141d1ab14196a645672989f25014cbf97d97940c407f278e26b400d", size = 1933045, upload-time = "2026-05-11T01:09:38.884Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/77/22a4fd8774e04aa2599fe02637b3cea107ac5234745ae91dbd3e7a8349dd/astropy_iers_data-0.2026.5.11.1.8.52-py3-none-any.whl", hash = "sha256:40c449c35bd8deabc20053f024f3d4c0cbea2947adf620fcbb6f4242b55e2090", size = 1989748, upload-time = "2026-05-11T01:09:36.91Z" }, +] + +[[package]] +name = "attrs" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, +] + +[[package]] +name = "beartype" +version = "0.22.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/94/1009e248bbfbab11397abca7193bea6626806be9a327d399810d523a07cb/beartype-0.22.9.tar.gz", hash = "sha256:8f82b54aa723a2848a56008d18875f91c1db02c32ef6a62319a002e3e25a975f", size = 1608866, upload-time = "2025-12-13T06:50:30.72Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/cc/18245721fa7747065ab478316c7fea7c74777d07f37ae60db2e84f8172e8/beartype-0.22.9-py3-none-any.whl", hash = "sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2", size = 1333658, upload-time = "2025-12-13T06:50:28.266Z" }, +] + +[[package]] +name = "bilby" +source = { editable = "." } +dependencies = [ + { name = "array-api-compat" }, + { name = "array-api-extra", version = "0.9.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "array-api-extra", version = "0.10.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "attrs" }, + { name = "corner" }, + { name = "dill" }, + { name = "dynesty" }, + { name = "emcee" }, + { name = "h5py" }, + { name = "matplotlib" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "pandas", version = "3.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "plum-dispatch" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "tqdm" }, +] + +[package.optional-dependencies] +all = [ + { name = "astropy", version = "6.1.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "astropy", version = "7.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "celerite" }, + { name = "george" }, + { name = "glasflow" }, + { name = "gwpy", version = "3.0.14", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "gwpy", version = "4.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "interpax" }, + { name = "jax", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "jax", version = "0.10.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "lalsuite" }, + { name = "parameterized" }, + { name = "plotly" }, + { name = "pyfftw", version = "0.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "pyfftw", version = "0.15.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "pytest-requires" }, + { name = "pytest-rerunfailures" }, + { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scikit-learn", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +gw = [ + { name = "astropy", version = "6.1.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "astropy", version = "7.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "gwpy", version = "3.0.14", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "gwpy", version = "4.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "lalsuite" }, + { name = "pyfftw", version = "0.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "pyfftw", version = "0.15.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scikit-learn", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +jax = [ + { name = "interpax" }, + { name = "jax", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "jax", version = "0.10.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +mcmc = [ + { name = "glasflow" }, + { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scikit-learn", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] + +[package.metadata] +requires-dist = [ + { name = "array-api-compat", specifier = ">=1.13" }, + { name = "array-api-extra" }, + { name = "astropy", marker = "extra == 'all'", specifier = ">=5" }, + { name = "astropy", marker = "extra == 'gw'", specifier = ">=5" }, + { name = "attrs" }, + { name = "celerite", marker = "extra == 'all'" }, + { name = "corner" }, + { name = "dill" }, + { name = "dynesty", specifier = ">=2.0.1" }, + { name = "emcee" }, + { name = "george", marker = "extra == 'all'" }, + { name = "glasflow", marker = "extra == 'all'" }, + { name = "glasflow", marker = "extra == 'mcmc'" }, + { name = "gwpy", marker = "extra == 'all'" }, + { name = "gwpy", marker = "extra == 'gw'" }, + { name = "h5py" }, + { name = "interpax", marker = "extra == 'all'" }, + { name = "interpax", marker = "extra == 'jax'" }, + { name = "jax", marker = "extra == 'all'" }, + { name = "jax", marker = "extra == 'jax'" }, + { name = "lalsuite", marker = "extra == 'all'" }, + { name = "lalsuite", marker = "extra == 'gw'" }, + { name = "matplotlib" }, + { name = "numpy" }, + { name = "pandas" }, + { name = "parameterized", marker = "extra == 'all'" }, + { name = "plotly", marker = "extra == 'all'" }, + { name = "plum-dispatch" }, + { name = "pyfftw", marker = "extra == 'all'" }, + { name = "pyfftw", marker = "extra == 'gw'" }, + { name = "pytest-requires", marker = "extra == 'all'" }, + { name = "pytest-rerunfailures", marker = "extra == 'all'" }, + { name = "scikit-learn", marker = "extra == 'all'" }, + { name = "scikit-learn", marker = "extra == 'gw'" }, + { name = "scikit-learn", marker = "extra == 'mcmc'" }, + { name = "scipy", specifier = ">=1.5" }, + { name = "tqdm" }, +] +provides-extras = ["all", "gw", "jax", "mcmc"] + +[[package]] +name = "celerite" +version = "0.4.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/7a/37e380a05055e730b38698bf5ec456bee6494936edb0dc96bb8e4dd5a0d9/celerite-0.4.3.tar.gz", hash = "sha256:aa8b487f57a7cc9793c4652b19b2df090beaa849730b50805f030694e94d0f6d", size = 14186766, upload-time = "2024-06-22T15:01:16.3Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/43/9bb3557b653113711341f7cee7deaf9c826f056dc9a54c8b77fb77de5629/celerite-0.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c433697026c144b3968342de380d30e5871e3dcb8f0c7db17e59786d67d943fb", size = 335537, upload-time = "2024-06-22T15:00:40.571Z" }, + { url = "https://files.pythonhosted.org/packages/bb/d2/b0005aa039765cb7aa0e07fa0bd29d2188a6fe54122d454a36db242fddc7/celerite-0.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3be153dc1a06948dcc35b394455af68b03e39b7bbfa5835d4cf62d040c090b4a", size = 302843, upload-time = "2024-06-22T15:00:42.884Z" }, + { url = "https://files.pythonhosted.org/packages/fb/e5/04b52438da6af7f6c0a4019ac800e0bccff180b291db4e4fe8cbbbf2a9cb/celerite-0.4.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2f1377789600a9fd66962d38fd4b35df255205512a2d5d6171d8569aa104fb5", size = 307963, upload-time = "2024-06-22T15:00:44.98Z" }, + { url = "https://files.pythonhosted.org/packages/54/f8/a02650ea97e072a9796b6fd7dcf362271366cf198b1b61f910000c87b893/celerite-0.4.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:845db1bd018a5c03399385aa5a8b1f5913aaaa613d51e4d8ba1092a84c4b73c5", size = 1302582, upload-time = "2024-06-22T15:00:47.111Z" }, + { url = "https://files.pythonhosted.org/packages/13/40/c2bf5ffa5d792c770e8b90d2254c1fdfa5f554b185f00ad8bc21bd893fdc/celerite-0.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:c06643359d5f2d02429c151d2fc567a8bea1e1f2f7fe2a1050729c9de843231a", size = 348900, upload-time = "2024-06-22T15:00:48.917Z" }, + { url = "https://files.pythonhosted.org/packages/3d/ee/381bc81913ee9eda49d5f894b2a677af8a8f918806a8be1b05fe320c86b6/celerite-0.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:702646a2fd7664648dcd126f0ecbefdacec16d2ff06b3b98b14f5321c1184af0", size = 337120, upload-time = "2024-06-22T15:00:50.949Z" }, + { url = "https://files.pythonhosted.org/packages/13/53/948a764200e8ccbda5f8253d969c49d0d0a7ea84047ae8d1cf564d918e41/celerite-0.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:383532f043a11a25c5d3889169273db3f8ef33026c86397bafebbedd7e013dfd", size = 304082, upload-time = "2024-06-22T15:00:52.297Z" }, + { url = "https://files.pythonhosted.org/packages/84/a1/e9d6558194f95f49c95bc0e1e9f1d2a08c835db73c64709b163ca0bd67b3/celerite-0.4.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a8003db2e725f93844ca40acb2919290b9b6b20395abeb3d252ccd275e18ab8", size = 309193, upload-time = "2024-06-22T15:00:54.534Z" }, + { url = "https://files.pythonhosted.org/packages/ac/80/5c73134c9dd72ad7ffd0e6e5d82605f7c9a40654164afa232ffd8b92efce/celerite-0.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:433c3a3ccc52afbcb360b66ad4b501bfe06d9b81e75cfd7495680640ddbbc45d", size = 1304175, upload-time = "2024-06-22T15:00:56.55Z" }, + { url = "https://files.pythonhosted.org/packages/3b/5c/cc1a5de3bf7340cf352d3ef4eae0a214eb3663525410bd0ecc44ae040e4d/celerite-0.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:760c9624d65a524da3ed04dedcab6a964310306faefcce20e94da0034964df5a", size = 349808, upload-time = "2024-06-22T15:00:58.082Z" }, + { url = "https://files.pythonhosted.org/packages/41/ab/a9522f03d8a92d989119e10266c77707ee6a61a24cc84ebccb26317b2dfe/celerite-0.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1e9a68739f1e051401a0bf40e18779563965f22ca36a2f10dfe58e7d31f71bbf", size = 335309, upload-time = "2024-06-22T15:00:59.381Z" }, + { url = "https://files.pythonhosted.org/packages/79/68/3f48c2b7232dcd752eb89dedb6b3d666fecd496b033f040691f73836ab6f/celerite-0.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68ac010dc3990d3339e1c7c35536b92ecc3d5a6bad2ee9add8646b241a290ce8", size = 302572, upload-time = "2024-06-22T15:01:00.71Z" }, + { url = "https://files.pythonhosted.org/packages/af/cf/06d4fc481b9678768c5f49fc89e8aff41700152fab5a8c6d9e029902dfb6/celerite-0.4.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ab8b71513fba2bf54b8a152d21071efc1282c9ae69a1fc193fa077a7997befd", size = 309164, upload-time = "2024-06-22T15:01:02.282Z" }, + { url = "https://files.pythonhosted.org/packages/14/1b/7c62ea4f0912026c6c7791dadcbc999eb94281a22db44bcc248958dda479/celerite-0.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a1ab89af0f99aaafc1f7f26d103c679410eb0698986f92309d67cadbb7063a9e", size = 1304095, upload-time = "2024-06-22T15:01:04.347Z" }, + { url = "https://files.pythonhosted.org/packages/a0/e0/2333cbc6c8a34da9fea8d471dae63447d38d31b06d1db3150482a1934779/celerite-0.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:1fa4e44a6a12941e1ff0cc6a8d0ce6a036ebfd253337705cb81c6bff529b29a0", size = 350091, upload-time = "2024-06-22T15:01:06.457Z" }, +] + +[[package]] +name = "certifi" +version = "2026.4.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/25/ee/6caf7a40c36a1220410afe15a1cc64993a1f864871f698c0f93acb72842a/certifi-2026.4.22.tar.gz", hash = "sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580", size = 137077, upload-time = "2026-04-22T11:26:11.191Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/30/7cd8fdcdfbc5b869528b079bfb76dcdf6056b1a2097a662e5e8c04f42965/certifi-2026.4.22-py3-none-any.whl", hash = "sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a", size = 135707, upload-time = "2026-04-22T11:26:09.372Z" }, +] + +[[package]] +name = "cffi" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283, upload-time = "2025-09-08T23:22:08.01Z" }, + { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504, upload-time = "2025-09-08T23:22:10.637Z" }, + { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811, upload-time = "2025-09-08T23:22:12.267Z" }, + { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402, upload-time = "2025-09-08T23:22:13.455Z" }, + { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217, upload-time = "2025-09-08T23:22:14.596Z" }, + { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079, upload-time = "2025-09-08T23:22:15.769Z" }, + { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475, upload-time = "2025-09-08T23:22:17.427Z" }, + { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829, upload-time = "2025-09-08T23:22:19.069Z" }, + { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211, upload-time = "2025-09-08T23:22:20.588Z" }, + { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036, upload-time = "2025-09-08T23:22:22.143Z" }, + { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184, upload-time = "2025-09-08T23:22:23.328Z" }, + { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790, upload-time = "2025-09-08T23:22:24.752Z" }, + { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" }, + { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" }, + { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" }, + { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" }, + { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" }, + { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" }, + { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" }, + { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" }, + { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" }, + { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" }, + { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" }, + { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" }, + { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, + { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, + { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, + { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, + { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, + { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, + { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, + { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, + { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, + { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, + { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, + { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, + { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, + { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, + { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, + { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, + { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, + { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, + { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, + { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, + { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, + { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, + { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, + { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, + { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, + { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, + { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, + { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, + { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, + { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/08/0f303cb0b529e456bb116f2d50565a482694fbb94340bf56d44677e7ed03/charset_normalizer-3.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d", size = 315182, upload-time = "2026-04-02T09:25:40.673Z" }, + { url = "https://files.pythonhosted.org/packages/24/47/b192933e94b546f1b1fe4df9cc1f84fcdbf2359f8d1081d46dd029b50207/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8", size = 209329, upload-time = "2026-04-02T09:25:42.354Z" }, + { url = "https://files.pythonhosted.org/packages/c2/b4/01fa81c5ca6141024d89a8fc15968002b71da7f825dd14113207113fabbd/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790", size = 231230, upload-time = "2026-04-02T09:25:44.281Z" }, + { url = "https://files.pythonhosted.org/packages/20/f7/7b991776844dfa058017e600e6e55ff01984a063290ca5622c0b63162f68/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc", size = 225890, upload-time = "2026-04-02T09:25:45.475Z" }, + { url = "https://files.pythonhosted.org/packages/20/e7/bed0024a0f4ab0c8a9c64d4445f39b30c99bd1acd228291959e3de664247/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393", size = 216930, upload-time = "2026-04-02T09:25:46.58Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ab/b18f0ab31cdd7b3ddb8bb76c4a414aeb8160c9810fdf1bc62f269a539d87/charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153", size = 202109, upload-time = "2026-04-02T09:25:48.031Z" }, + { url = "https://files.pythonhosted.org/packages/82/e5/7e9440768a06dfb3075936490cb82dbf0ee20a133bf0dd8551fa096914ec/charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af", size = 214684, upload-time = "2026-04-02T09:25:49.245Z" }, + { url = "https://files.pythonhosted.org/packages/71/94/8c61d8da9f062fdf457c80acfa25060ec22bf1d34bbeaca4350f13bcfd07/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34", size = 212785, upload-time = "2026-04-02T09:25:50.671Z" }, + { url = "https://files.pythonhosted.org/packages/66/cd/6e9889c648e72c0ab2e5967528bb83508f354d706637bc7097190c874e13/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1", size = 203055, upload-time = "2026-04-02T09:25:51.802Z" }, + { url = "https://files.pythonhosted.org/packages/92/2e/7a951d6a08aefb7eb8e1b54cdfb580b1365afdd9dd484dc4bee9e5d8f258/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752", size = 232502, upload-time = "2026-04-02T09:25:53.388Z" }, + { url = "https://files.pythonhosted.org/packages/58/d5/abcf2d83bf8e0a1286df55cd0dc1d49af0da4282aa77e986df343e7de124/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53", size = 214295, upload-time = "2026-04-02T09:25:54.765Z" }, + { url = "https://files.pythonhosted.org/packages/47/3a/7d4cd7ed54be99973a0dc176032cba5cb1f258082c31fa6df35cff46acfc/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616", size = 227145, upload-time = "2026-04-02T09:25:55.904Z" }, + { url = "https://files.pythonhosted.org/packages/1d/98/3a45bf8247889cf28262ebd3d0872edff11565b2a1e3064ccb132db3fbb0/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a", size = 218884, upload-time = "2026-04-02T09:25:57.074Z" }, + { url = "https://files.pythonhosted.org/packages/ad/80/2e8b7f8915ed5c9ef13aa828d82738e33888c485b65ebf744d615040c7ea/charset_normalizer-3.4.7-cp310-cp310-win32.whl", hash = "sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374", size = 148343, upload-time = "2026-04-02T09:25:58.199Z" }, + { url = "https://files.pythonhosted.org/packages/35/1b/3b8c8c77184af465ee9ad88b5aea46ea6b2e1f7b9dc9502891e37af21e30/charset_normalizer-3.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943", size = 159174, upload-time = "2026-04-02T09:25:59.322Z" }, + { url = "https://files.pythonhosted.org/packages/be/c1/feb40dca40dbb21e0a908801782d9288c64fc8d8e562c2098e9994c8c21b/charset_normalizer-3.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008", size = 147805, upload-time = "2026-04-02T09:26:00.756Z" }, + { url = "https://files.pythonhosted.org/packages/c2/d7/b5b7020a0565c2e9fa8c09f4b5fa6232feb326b8c20081ccded47ea368fd/charset_normalizer-3.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7", size = 309705, upload-time = "2026-04-02T09:26:02.191Z" }, + { url = "https://files.pythonhosted.org/packages/5a/53/58c29116c340e5456724ecd2fff4196d236b98f3da97b404bc5e51ac3493/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7", size = 206419, upload-time = "2026-04-02T09:26:03.583Z" }, + { url = "https://files.pythonhosted.org/packages/b2/02/e8146dc6591a37a00e5144c63f29fb7c97a734ea8a111190783c0e60ab63/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e", size = 227901, upload-time = "2026-04-02T09:26:04.738Z" }, + { url = "https://files.pythonhosted.org/packages/fb/73/77486c4cd58f1267bf17db420e930c9afa1b3be3fe8c8b8ebbebc9624359/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c", size = 222742, upload-time = "2026-04-02T09:26:06.36Z" }, + { url = "https://files.pythonhosted.org/packages/a1/fa/f74eb381a7d94ded44739e9d94de18dc5edc9c17fb8c11f0a6890696c0a9/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df", size = 214061, upload-time = "2026-04-02T09:26:08.347Z" }, + { url = "https://files.pythonhosted.org/packages/dc/92/42bd3cefcf7687253fb86694b45f37b733c97f59af3724f356fa92b8c344/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265", size = 199239, upload-time = "2026-04-02T09:26:09.823Z" }, + { url = "https://files.pythonhosted.org/packages/4c/3d/069e7184e2aa3b3cddc700e3dd267413dc259854adc3380421c805c6a17d/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4", size = 210173, upload-time = "2026-04-02T09:26:10.953Z" }, + { url = "https://files.pythonhosted.org/packages/62/51/9d56feb5f2e7074c46f93e0ebdbe61f0848ee246e2f0d89f8e20b89ebb8f/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e", size = 209841, upload-time = "2026-04-02T09:26:12.142Z" }, + { url = "https://files.pythonhosted.org/packages/d2/59/893d8f99cc4c837dda1fe2f1139079703deb9f321aabcb032355de13b6c7/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38", size = 200304, upload-time = "2026-04-02T09:26:13.711Z" }, + { url = "https://files.pythonhosted.org/packages/7d/1d/ee6f3be3464247578d1ed5c46de545ccc3d3ff933695395c402c21fa6b77/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c", size = 229455, upload-time = "2026-04-02T09:26:14.941Z" }, + { url = "https://files.pythonhosted.org/packages/54/bb/8fb0a946296ea96a488928bdce8ef99023998c48e4713af533e9bb98ef07/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b", size = 210036, upload-time = "2026-04-02T09:26:16.478Z" }, + { url = "https://files.pythonhosted.org/packages/9a/bc/015b2387f913749f82afd4fcba07846d05b6d784dd16123cb66860e0237d/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c", size = 224739, upload-time = "2026-04-02T09:26:17.751Z" }, + { url = "https://files.pythonhosted.org/packages/17/ab/63133691f56baae417493cba6b7c641571a2130eb7bceba6773367ab9ec5/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d", size = 216277, upload-time = "2026-04-02T09:26:18.981Z" }, + { url = "https://files.pythonhosted.org/packages/06/6d/3be70e827977f20db77c12a97e6a9f973631a45b8d186c084527e53e77a4/charset_normalizer-3.4.7-cp311-cp311-win32.whl", hash = "sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad", size = 147819, upload-time = "2026-04-02T09:26:20.295Z" }, + { url = "https://files.pythonhosted.org/packages/20/d9/5f67790f06b735d7c7637171bbfd89882ad67201891b7275e51116ed8207/charset_normalizer-3.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00", size = 159281, upload-time = "2026-04-02T09:26:21.74Z" }, + { url = "https://files.pythonhosted.org/packages/ca/83/6413f36c5a34afead88ce6f66684d943d91f233d76dd083798f9602b75ae/charset_normalizer-3.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1", size = 147843, upload-time = "2026-04-02T09:26:22.901Z" }, + { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" }, + { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" }, + { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" }, + { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" }, + { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" }, + { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" }, + { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" }, + { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" }, + { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" }, + { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" }, + { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" }, + { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" }, + { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" }, + { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" }, + { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" }, + { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" }, + { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" }, + { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" }, + { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" }, + { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" }, + { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" }, + { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" }, + { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" }, + { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" }, + { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" }, + { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" }, + { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" }, + { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" }, + { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" }, + { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" }, + { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" }, + { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" }, + { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" }, + { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" }, + { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" }, + { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" }, + { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" }, + { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" }, + { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" }, + { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" }, + { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" }, + { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" }, + { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" }, + { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" }, + { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" }, + { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" }, + { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" }, + { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" }, + { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" }, + { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" }, + { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" }, + { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, +] + +[[package]] +name = "click" +version = "8.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bb/63/f9e1ea081ce35720d8b92acde70daaedace594dc93b693c869e0d5910718/click-8.3.3.tar.gz", hash = "sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2", size = 328061, upload-time = "2026-04-22T15:11:27.506Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ae/44/c1221527f6a71a01ec6fbad7fa78f1d50dfa02217385cf0fa3eec7087d59/click-8.3.3-py3-none-any.whl", hash = "sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613", size = 110502, upload-time = "2026-04-22T15:11:25.044Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "contourpy" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130, upload-time = "2025-04-15T17:47:53.79Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/a3/da4153ec8fe25d263aa48c1a4cbde7f49b59af86f0b6f7862788c60da737/contourpy-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba38e3f9f330af820c4b27ceb4b9c7feee5fe0493ea53a8720f4792667465934", size = 268551, upload-time = "2025-04-15T17:34:46.581Z" }, + { url = "https://files.pythonhosted.org/packages/2f/6c/330de89ae1087eb622bfca0177d32a7ece50c3ef07b28002de4757d9d875/contourpy-1.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc41ba0714aa2968d1f8674ec97504a8f7e334f48eeacebcaa6256213acb0989", size = 253399, upload-time = "2025-04-15T17:34:51.427Z" }, + { url = "https://files.pythonhosted.org/packages/c1/bd/20c6726b1b7f81a8bee5271bed5c165f0a8e1f572578a9d27e2ccb763cb2/contourpy-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9be002b31c558d1ddf1b9b415b162c603405414bacd6932d031c5b5a8b757f0d", size = 312061, upload-time = "2025-04-15T17:34:55.961Z" }, + { url = "https://files.pythonhosted.org/packages/22/fc/a9665c88f8a2473f823cf1ec601de9e5375050f1958cbb356cdf06ef1ab6/contourpy-1.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8d2e74acbcba3bfdb6d9d8384cdc4f9260cae86ed9beee8bd5f54fee49a430b9", size = 351956, upload-time = "2025-04-15T17:35:00.992Z" }, + { url = "https://files.pythonhosted.org/packages/25/eb/9f0a0238f305ad8fb7ef42481020d6e20cf15e46be99a1fcf939546a177e/contourpy-1.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e259bced5549ac64410162adc973c5e2fb77f04df4a439d00b478e57a0e65512", size = 320872, upload-time = "2025-04-15T17:35:06.177Z" }, + { url = "https://files.pythonhosted.org/packages/32/5c/1ee32d1c7956923202f00cf8d2a14a62ed7517bdc0ee1e55301227fc273c/contourpy-1.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad687a04bc802cbe8b9c399c07162a3c35e227e2daccf1668eb1f278cb698631", size = 325027, upload-time = "2025-04-15T17:35:11.244Z" }, + { url = "https://files.pythonhosted.org/packages/83/bf/9baed89785ba743ef329c2b07fd0611d12bfecbedbdd3eeecf929d8d3b52/contourpy-1.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cdd22595308f53ef2f891040ab2b93d79192513ffccbd7fe19be7aa773a5e09f", size = 1306641, upload-time = "2025-04-15T17:35:26.701Z" }, + { url = "https://files.pythonhosted.org/packages/d4/cc/74e5e83d1e35de2d28bd97033426b450bc4fd96e092a1f7a63dc7369b55d/contourpy-1.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b4f54d6a2defe9f257327b0f243612dd051cc43825587520b1bf74a31e2f6ef2", size = 1374075, upload-time = "2025-04-15T17:35:43.204Z" }, + { url = "https://files.pythonhosted.org/packages/0c/42/17f3b798fd5e033b46a16f8d9fcb39f1aba051307f5ebf441bad1ecf78f8/contourpy-1.3.2-cp310-cp310-win32.whl", hash = "sha256:f939a054192ddc596e031e50bb13b657ce318cf13d264f095ce9db7dc6ae81c0", size = 177534, upload-time = "2025-04-15T17:35:46.554Z" }, + { url = "https://files.pythonhosted.org/packages/54/ec/5162b8582f2c994721018d0c9ece9dc6ff769d298a8ac6b6a652c307e7df/contourpy-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c440093bbc8fc21c637c03bafcbef95ccd963bc6e0514ad887932c18ca2a759a", size = 221188, upload-time = "2025-04-15T17:35:50.064Z" }, + { url = "https://files.pythonhosted.org/packages/b3/b9/ede788a0b56fc5b071639d06c33cb893f68b1178938f3425debebe2dab78/contourpy-1.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6a37a2fb93d4df3fc4c0e363ea4d16f83195fc09c891bc8ce072b9d084853445", size = 269636, upload-time = "2025-04-15T17:35:54.473Z" }, + { url = "https://files.pythonhosted.org/packages/e6/75/3469f011d64b8bbfa04f709bfc23e1dd71be54d05b1b083be9f5b22750d1/contourpy-1.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b7cd50c38f500bbcc9b6a46643a40e0913673f869315d8e70de0438817cb7773", size = 254636, upload-time = "2025-04-15T17:35:58.283Z" }, + { url = "https://files.pythonhosted.org/packages/8d/2f/95adb8dae08ce0ebca4fd8e7ad653159565d9739128b2d5977806656fcd2/contourpy-1.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6658ccc7251a4433eebd89ed2672c2ed96fba367fd25ca9512aa92a4b46c4f1", size = 313053, upload-time = "2025-04-15T17:36:03.235Z" }, + { url = "https://files.pythonhosted.org/packages/c3/a6/8ccf97a50f31adfa36917707fe39c9a0cbc24b3bbb58185577f119736cc9/contourpy-1.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:70771a461aaeb335df14deb6c97439973d253ae70660ca085eec25241137ef43", size = 352985, upload-time = "2025-04-15T17:36:08.275Z" }, + { url = "https://files.pythonhosted.org/packages/1d/b6/7925ab9b77386143f39d9c3243fdd101621b4532eb126743201160ffa7e6/contourpy-1.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65a887a6e8c4cd0897507d814b14c54a8c2e2aa4ac9f7686292f9769fcf9a6ab", size = 323750, upload-time = "2025-04-15T17:36:13.29Z" }, + { url = "https://files.pythonhosted.org/packages/c2/f3/20c5d1ef4f4748e52d60771b8560cf00b69d5c6368b5c2e9311bcfa2a08b/contourpy-1.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3859783aefa2b8355697f16642695a5b9792e7a46ab86da1118a4a23a51a33d7", size = 326246, upload-time = "2025-04-15T17:36:18.329Z" }, + { url = "https://files.pythonhosted.org/packages/8c/e5/9dae809e7e0b2d9d70c52b3d24cba134dd3dad979eb3e5e71f5df22ed1f5/contourpy-1.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:eab0f6db315fa4d70f1d8ab514e527f0366ec021ff853d7ed6a2d33605cf4b83", size = 1308728, upload-time = "2025-04-15T17:36:33.878Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4a/0058ba34aeea35c0b442ae61a4f4d4ca84d6df8f91309bc2d43bb8dd248f/contourpy-1.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d91a3ccc7fea94ca0acab82ceb77f396d50a1f67412efe4c526f5d20264e6ecd", size = 1375762, upload-time = "2025-04-15T17:36:51.295Z" }, + { url = "https://files.pythonhosted.org/packages/09/33/7174bdfc8b7767ef2c08ed81244762d93d5c579336fc0b51ca57b33d1b80/contourpy-1.3.2-cp311-cp311-win32.whl", hash = "sha256:1c48188778d4d2f3d48e4643fb15d8608b1d01e4b4d6b0548d9b336c28fc9b6f", size = 178196, upload-time = "2025-04-15T17:36:55.002Z" }, + { url = "https://files.pythonhosted.org/packages/5e/fe/4029038b4e1c4485cef18e480b0e2cd2d755448bb071eb9977caac80b77b/contourpy-1.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:5ebac872ba09cb8f2131c46b8739a7ff71de28a24c869bcad554477eb089a878", size = 222017, upload-time = "2025-04-15T17:36:58.576Z" }, + { url = "https://files.pythonhosted.org/packages/34/f7/44785876384eff370c251d58fd65f6ad7f39adce4a093c934d4a67a7c6b6/contourpy-1.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4caf2bcd2969402bf77edc4cb6034c7dd7c0803213b3523f111eb7460a51b8d2", size = 271580, upload-time = "2025-04-15T17:37:03.105Z" }, + { url = "https://files.pythonhosted.org/packages/93/3b/0004767622a9826ea3d95f0e9d98cd8729015768075d61f9fea8eeca42a8/contourpy-1.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:82199cb78276249796419fe36b7386bd8d2cc3f28b3bc19fe2454fe2e26c4c15", size = 255530, upload-time = "2025-04-15T17:37:07.026Z" }, + { url = "https://files.pythonhosted.org/packages/e7/bb/7bd49e1f4fa805772d9fd130e0d375554ebc771ed7172f48dfcd4ca61549/contourpy-1.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:106fab697af11456fcba3e352ad50effe493a90f893fca6c2ca5c033820cea92", size = 307688, upload-time = "2025-04-15T17:37:11.481Z" }, + { url = "https://files.pythonhosted.org/packages/fc/97/e1d5dbbfa170725ef78357a9a0edc996b09ae4af170927ba8ce977e60a5f/contourpy-1.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d14f12932a8d620e307f715857107b1d1845cc44fdb5da2bc8e850f5ceba9f87", size = 347331, upload-time = "2025-04-15T17:37:18.212Z" }, + { url = "https://files.pythonhosted.org/packages/6f/66/e69e6e904f5ecf6901be3dd16e7e54d41b6ec6ae3405a535286d4418ffb4/contourpy-1.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:532fd26e715560721bb0d5fc7610fce279b3699b018600ab999d1be895b09415", size = 318963, upload-time = "2025-04-15T17:37:22.76Z" }, + { url = "https://files.pythonhosted.org/packages/a8/32/b8a1c8965e4f72482ff2d1ac2cd670ce0b542f203c8e1d34e7c3e6925da7/contourpy-1.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b383144cf2d2c29f01a1e8170f50dacf0eac02d64139dcd709a8ac4eb3cfe", size = 323681, upload-time = "2025-04-15T17:37:33.001Z" }, + { url = "https://files.pythonhosted.org/packages/30/c6/12a7e6811d08757c7162a541ca4c5c6a34c0f4e98ef2b338791093518e40/contourpy-1.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c49f73e61f1f774650a55d221803b101d966ca0c5a2d6d5e4320ec3997489441", size = 1308674, upload-time = "2025-04-15T17:37:48.64Z" }, + { url = "https://files.pythonhosted.org/packages/2a/8a/bebe5a3f68b484d3a2b8ffaf84704b3e343ef1addea528132ef148e22b3b/contourpy-1.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3d80b2c0300583228ac98d0a927a1ba6a2ba6b8a742463c564f1d419ee5b211e", size = 1380480, upload-time = "2025-04-15T17:38:06.7Z" }, + { url = "https://files.pythonhosted.org/packages/34/db/fcd325f19b5978fb509a7d55e06d99f5f856294c1991097534360b307cf1/contourpy-1.3.2-cp312-cp312-win32.whl", hash = "sha256:90df94c89a91b7362e1142cbee7568f86514412ab8a2c0d0fca72d7e91b62912", size = 178489, upload-time = "2025-04-15T17:38:10.338Z" }, + { url = "https://files.pythonhosted.org/packages/01/c8/fadd0b92ffa7b5eb5949bf340a63a4a496a6930a6c37a7ba0f12acb076d6/contourpy-1.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:8c942a01d9163e2e5cfb05cb66110121b8d07ad438a17f9e766317bcb62abf73", size = 223042, upload-time = "2025-04-15T17:38:14.239Z" }, + { url = "https://files.pythonhosted.org/packages/2e/61/5673f7e364b31e4e7ef6f61a4b5121c5f170f941895912f773d95270f3a2/contourpy-1.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:de39db2604ae755316cb5967728f4bea92685884b1e767b7c24e983ef5f771cb", size = 271630, upload-time = "2025-04-15T17:38:19.142Z" }, + { url = "https://files.pythonhosted.org/packages/ff/66/a40badddd1223822c95798c55292844b7e871e50f6bfd9f158cb25e0bd39/contourpy-1.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3f9e896f447c5c8618f1edb2bafa9a4030f22a575ec418ad70611450720b5b08", size = 255670, upload-time = "2025-04-15T17:38:23.688Z" }, + { url = "https://files.pythonhosted.org/packages/1e/c7/cf9fdee8200805c9bc3b148f49cb9482a4e3ea2719e772602a425c9b09f8/contourpy-1.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71e2bd4a1c4188f5c2b8d274da78faab884b59df20df63c34f74aa1813c4427c", size = 306694, upload-time = "2025-04-15T17:38:28.238Z" }, + { url = "https://files.pythonhosted.org/packages/dd/e7/ccb9bec80e1ba121efbffad7f38021021cda5be87532ec16fd96533bb2e0/contourpy-1.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de425af81b6cea33101ae95ece1f696af39446db9682a0b56daaa48cfc29f38f", size = 345986, upload-time = "2025-04-15T17:38:33.502Z" }, + { url = "https://files.pythonhosted.org/packages/dc/49/ca13bb2da90391fa4219fdb23b078d6065ada886658ac7818e5441448b78/contourpy-1.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:977e98a0e0480d3fe292246417239d2d45435904afd6d7332d8455981c408b85", size = 318060, upload-time = "2025-04-15T17:38:38.672Z" }, + { url = "https://files.pythonhosted.org/packages/c8/65/5245ce8c548a8422236c13ffcdcdada6a2a812c361e9e0c70548bb40b661/contourpy-1.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:434f0adf84911c924519d2b08fc10491dd282b20bdd3fa8f60fd816ea0b48841", size = 322747, upload-time = "2025-04-15T17:38:43.712Z" }, + { url = "https://files.pythonhosted.org/packages/72/30/669b8eb48e0a01c660ead3752a25b44fdb2e5ebc13a55782f639170772f9/contourpy-1.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c66c4906cdbc50e9cba65978823e6e00b45682eb09adbb78c9775b74eb222422", size = 1308895, upload-time = "2025-04-15T17:39:00.224Z" }, + { url = "https://files.pythonhosted.org/packages/05/5a/b569f4250decee6e8d54498be7bdf29021a4c256e77fe8138c8319ef8eb3/contourpy-1.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8b7fc0cd78ba2f4695fd0a6ad81a19e7e3ab825c31b577f384aa9d7817dc3bef", size = 1379098, upload-time = "2025-04-15T17:43:29.649Z" }, + { url = "https://files.pythonhosted.org/packages/19/ba/b227c3886d120e60e41b28740ac3617b2f2b971b9f601c835661194579f1/contourpy-1.3.2-cp313-cp313-win32.whl", hash = "sha256:15ce6ab60957ca74cff444fe66d9045c1fd3e92c8936894ebd1f3eef2fff075f", size = 178535, upload-time = "2025-04-15T17:44:44.532Z" }, + { url = "https://files.pythonhosted.org/packages/12/6e/2fed56cd47ca739b43e892707ae9a13790a486a3173be063681ca67d2262/contourpy-1.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e1578f7eafce927b168752ed7e22646dad6cd9bca673c60bff55889fa236ebf9", size = 223096, upload-time = "2025-04-15T17:44:48.194Z" }, + { url = "https://files.pythonhosted.org/packages/54/4c/e76fe2a03014a7c767d79ea35c86a747e9325537a8b7627e0e5b3ba266b4/contourpy-1.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0475b1f6604896bc7c53bb070e355e9321e1bc0d381735421a2d2068ec56531f", size = 285090, upload-time = "2025-04-15T17:43:34.084Z" }, + { url = "https://files.pythonhosted.org/packages/7b/e2/5aba47debd55d668e00baf9651b721e7733975dc9fc27264a62b0dd26eb8/contourpy-1.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c85bb486e9be652314bb5b9e2e3b0d1b2e643d5eec4992c0fbe8ac71775da739", size = 268643, upload-time = "2025-04-15T17:43:38.626Z" }, + { url = "https://files.pythonhosted.org/packages/a1/37/cd45f1f051fe6230f751cc5cdd2728bb3a203f5619510ef11e732109593c/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:745b57db7758f3ffc05a10254edd3182a2a83402a89c00957a8e8a22f5582823", size = 310443, upload-time = "2025-04-15T17:43:44.522Z" }, + { url = "https://files.pythonhosted.org/packages/8b/a2/36ea6140c306c9ff6dd38e3bcec80b3b018474ef4d17eb68ceecd26675f4/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:970e9173dbd7eba9b4e01aab19215a48ee5dd3f43cef736eebde064a171f89a5", size = 349865, upload-time = "2025-04-15T17:43:49.545Z" }, + { url = "https://files.pythonhosted.org/packages/95/b7/2fc76bc539693180488f7b6cc518da7acbbb9e3b931fd9280504128bf956/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6c4639a9c22230276b7bffb6a850dfc8258a2521305e1faefe804d006b2e532", size = 321162, upload-time = "2025-04-15T17:43:54.203Z" }, + { url = "https://files.pythonhosted.org/packages/f4/10/76d4f778458b0aa83f96e59d65ece72a060bacb20cfbee46cf6cd5ceba41/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc829960f34ba36aad4302e78eabf3ef16a3a100863f0d4eeddf30e8a485a03b", size = 327355, upload-time = "2025-04-15T17:44:01.025Z" }, + { url = "https://files.pythonhosted.org/packages/43/a3/10cf483ea683f9f8ab096c24bad3cce20e0d1dd9a4baa0e2093c1c962d9d/contourpy-1.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d32530b534e986374fc19eaa77fcb87e8a99e5431499949b828312bdcd20ac52", size = 1307935, upload-time = "2025-04-15T17:44:17.322Z" }, + { url = "https://files.pythonhosted.org/packages/78/73/69dd9a024444489e22d86108e7b913f3528f56cfc312b5c5727a44188471/contourpy-1.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e298e7e70cf4eb179cc1077be1c725b5fd131ebc81181bf0c03525c8abc297fd", size = 1372168, upload-time = "2025-04-15T17:44:33.43Z" }, + { url = "https://files.pythonhosted.org/packages/0f/1b/96d586ccf1b1a9d2004dd519b25fbf104a11589abfd05484ff12199cca21/contourpy-1.3.2-cp313-cp313t-win32.whl", hash = "sha256:d0e589ae0d55204991450bb5c23f571c64fe43adaa53f93fc902a84c96f52fe1", size = 189550, upload-time = "2025-04-15T17:44:37.092Z" }, + { url = "https://files.pythonhosted.org/packages/b0/e6/6000d0094e8a5e32ad62591c8609e269febb6e4db83a1c75ff8868b42731/contourpy-1.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:78e9253c3de756b3f6a5174d024c4835acd59eb3f8e2ca13e775dbffe1558f69", size = 238214, upload-time = "2025-04-15T17:44:40.827Z" }, + { url = "https://files.pythonhosted.org/packages/33/05/b26e3c6ecc05f349ee0013f0bb850a761016d89cec528a98193a48c34033/contourpy-1.3.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fd93cc7f3139b6dd7aab2f26a90dde0aa9fc264dbf70f6740d498a70b860b82c", size = 265681, upload-time = "2025-04-15T17:44:59.314Z" }, + { url = "https://files.pythonhosted.org/packages/2b/25/ac07d6ad12affa7d1ffed11b77417d0a6308170f44ff20fa1d5aa6333f03/contourpy-1.3.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:107ba8a6a7eec58bb475329e6d3b95deba9440667c4d62b9b6063942b61d7f16", size = 315101, upload-time = "2025-04-15T17:45:04.165Z" }, + { url = "https://files.pythonhosted.org/packages/8f/4d/5bb3192bbe9d3f27e3061a6a8e7733c9120e203cb8515767d30973f71030/contourpy-1.3.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ded1706ed0c1049224531b81128efbd5084598f18d8a2d9efae833edbd2b40ad", size = 220599, upload-time = "2025-04-15T17:45:08.456Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c0/91f1215d0d9f9f343e4773ba6c9b89e8c0cc7a64a6263f21139da639d848/contourpy-1.3.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5f5964cdad279256c084b69c3f412b7801e15356b16efa9d78aa974041903da0", size = 266807, upload-time = "2025-04-15T17:45:15.535Z" }, + { url = "https://files.pythonhosted.org/packages/d4/79/6be7e90c955c0487e7712660d6cead01fa17bff98e0ea275737cc2bc8e71/contourpy-1.3.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49b65a95d642d4efa8f64ba12558fcb83407e58a2dfba9d796d77b63ccfcaff5", size = 318729, upload-time = "2025-04-15T17:45:20.166Z" }, + { url = "https://files.pythonhosted.org/packages/87/68/7f46fb537958e87427d98a4074bcde4b67a70b04900cfc5ce29bc2f556c1/contourpy-1.3.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8c5acb8dddb0752bf252e01a3035b21443158910ac16a3b0d20e7fed7d534ce5", size = 221791, upload-time = "2025-04-15T17:45:24.794Z" }, +] + +[[package]] +name = "contourpy" +version = "1.3.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/2e/c4390a31919d8a78b90e8ecf87cd4b4c4f05a5b48d05ec17db8e5404c6f4/contourpy-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:709a48ef9a690e1343202916450bc48b9e51c049b089c7f79a267b46cffcdaa1", size = 288773, upload-time = "2025-07-26T12:01:02.277Z" }, + { url = "https://files.pythonhosted.org/packages/0d/44/c4b0b6095fef4dc9c420e041799591e3b63e9619e3044f7f4f6c21c0ab24/contourpy-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:23416f38bfd74d5d28ab8429cc4d63fa67d5068bd711a85edb1c3fb0c3e2f381", size = 270149, upload-time = "2025-07-26T12:01:04.072Z" }, + { url = "https://files.pythonhosted.org/packages/30/2e/dd4ced42fefac8470661d7cb7e264808425e6c5d56d175291e93890cce09/contourpy-1.3.3-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:929ddf8c4c7f348e4c0a5a3a714b5c8542ffaa8c22954862a46ca1813b667ee7", size = 329222, upload-time = "2025-07-26T12:01:05.688Z" }, + { url = "https://files.pythonhosted.org/packages/f2/74/cc6ec2548e3d276c71389ea4802a774b7aa3558223b7bade3f25787fafc2/contourpy-1.3.3-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9e999574eddae35f1312c2b4b717b7885d4edd6cb46700e04f7f02db454e67c1", size = 377234, upload-time = "2025-07-26T12:01:07.054Z" }, + { url = "https://files.pythonhosted.org/packages/03/b3/64ef723029f917410f75c09da54254c5f9ea90ef89b143ccadb09df14c15/contourpy-1.3.3-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf67e0e3f482cb69779dd3061b534eb35ac9b17f163d851e2a547d56dba0a3a", size = 380555, upload-time = "2025-07-26T12:01:08.801Z" }, + { url = "https://files.pythonhosted.org/packages/5f/4b/6157f24ca425b89fe2eb7e7be642375711ab671135be21e6faa100f7448c/contourpy-1.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51e79c1f7470158e838808d4a996fa9bac72c498e93d8ebe5119bc1e6becb0db", size = 355238, upload-time = "2025-07-26T12:01:10.319Z" }, + { url = "https://files.pythonhosted.org/packages/98/56/f914f0dd678480708a04cfd2206e7c382533249bc5001eb9f58aa693e200/contourpy-1.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:598c3aaece21c503615fd59c92a3598b428b2f01bfb4b8ca9c4edeecc2438620", size = 1326218, upload-time = "2025-07-26T12:01:12.659Z" }, + { url = "https://files.pythonhosted.org/packages/fb/d7/4a972334a0c971acd5172389671113ae82aa7527073980c38d5868ff1161/contourpy-1.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:322ab1c99b008dad206d406bb61d014cf0174df491ae9d9d0fac6a6fda4f977f", size = 1392867, upload-time = "2025-07-26T12:01:15.533Z" }, + { url = "https://files.pythonhosted.org/packages/75/3e/f2cc6cd56dc8cff46b1a56232eabc6feea52720083ea71ab15523daab796/contourpy-1.3.3-cp311-cp311-win32.whl", hash = "sha256:fd907ae12cd483cd83e414b12941c632a969171bf90fc937d0c9f268a31cafff", size = 183677, upload-time = "2025-07-26T12:01:17.088Z" }, + { url = "https://files.pythonhosted.org/packages/98/4b/9bd370b004b5c9d8045c6c33cf65bae018b27aca550a3f657cdc99acdbd8/contourpy-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:3519428f6be58431c56581f1694ba8e50626f2dd550af225f82fb5f5814d2a42", size = 225234, upload-time = "2025-07-26T12:01:18.256Z" }, + { url = "https://files.pythonhosted.org/packages/d9/b6/71771e02c2e004450c12b1120a5f488cad2e4d5b590b1af8bad060360fe4/contourpy-1.3.3-cp311-cp311-win_arm64.whl", hash = "sha256:15ff10bfada4bf92ec8b31c62bf7c1834c244019b4a33095a68000d7075df470", size = 193123, upload-time = "2025-07-26T12:01:19.848Z" }, + { url = "https://files.pythonhosted.org/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb", size = 293419, upload-time = "2025-07-26T12:01:21.16Z" }, + { url = "https://files.pythonhosted.org/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6", size = 273979, upload-time = "2025-07-26T12:01:22.448Z" }, + { url = "https://files.pythonhosted.org/packages/d4/1c/a12359b9b2ca3a845e8f7f9ac08bdf776114eb931392fcad91743e2ea17b/contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7", size = 332653, upload-time = "2025-07-26T12:01:24.155Z" }, + { url = "https://files.pythonhosted.org/packages/63/12/897aeebfb475b7748ea67b61e045accdfcf0d971f8a588b67108ed7f5512/contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8", size = 379536, upload-time = "2025-07-26T12:01:25.91Z" }, + { url = "https://files.pythonhosted.org/packages/43/8a/a8c584b82deb248930ce069e71576fc09bd7174bbd35183b7943fb1064fd/contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea", size = 384397, upload-time = "2025-07-26T12:01:27.152Z" }, + { url = "https://files.pythonhosted.org/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1", size = 362601, upload-time = "2025-07-26T12:01:28.808Z" }, + { url = "https://files.pythonhosted.org/packages/05/0a/a3fe3be3ee2dceb3e615ebb4df97ae6f3828aa915d3e10549ce016302bd1/contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7", size = 1331288, upload-time = "2025-07-26T12:01:31.198Z" }, + { url = "https://files.pythonhosted.org/packages/33/1d/acad9bd4e97f13f3e2b18a3977fe1b4a37ecf3d38d815333980c6c72e963/contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411", size = 1403386, upload-time = "2025-07-26T12:01:33.947Z" }, + { url = "https://files.pythonhosted.org/packages/cf/8f/5847f44a7fddf859704217a99a23a4f6417b10e5ab1256a179264561540e/contourpy-1.3.3-cp312-cp312-win32.whl", hash = "sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69", size = 185018, upload-time = "2025-07-26T12:01:35.64Z" }, + { url = "https://files.pythonhosted.org/packages/19/e8/6026ed58a64563186a9ee3f29f41261fd1828f527dd93d33b60feca63352/contourpy-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b", size = 226567, upload-time = "2025-07-26T12:01:36.804Z" }, + { url = "https://files.pythonhosted.org/packages/d1/e2/f05240d2c39a1ed228d8328a78b6f44cd695f7ef47beb3e684cf93604f86/contourpy-1.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc", size = 193655, upload-time = "2025-07-26T12:01:37.999Z" }, + { url = "https://files.pythonhosted.org/packages/68/35/0167aad910bbdb9599272bd96d01a9ec6852f36b9455cf2ca67bd4cc2d23/contourpy-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:177fb367556747a686509d6fef71d221a4b198a3905fe824430e5ea0fda54eb5", size = 293257, upload-time = "2025-07-26T12:01:39.367Z" }, + { url = "https://files.pythonhosted.org/packages/96/e4/7adcd9c8362745b2210728f209bfbcf7d91ba868a2c5f40d8b58f54c509b/contourpy-1.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d002b6f00d73d69333dac9d0b8d5e84d9724ff9ef044fd63c5986e62b7c9e1b1", size = 274034, upload-time = "2025-07-26T12:01:40.645Z" }, + { url = "https://files.pythonhosted.org/packages/73/23/90e31ceeed1de63058a02cb04b12f2de4b40e3bef5e082a7c18d9c8ae281/contourpy-1.3.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:348ac1f5d4f1d66d3322420f01d42e43122f43616e0f194fc1c9f5d830c5b286", size = 334672, upload-time = "2025-07-26T12:01:41.942Z" }, + { url = "https://files.pythonhosted.org/packages/ed/93/b43d8acbe67392e659e1d984700e79eb67e2acb2bd7f62012b583a7f1b55/contourpy-1.3.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:655456777ff65c2c548b7c454af9c6f33f16c8884f11083244b5819cc214f1b5", size = 381234, upload-time = "2025-07-26T12:01:43.499Z" }, + { url = "https://files.pythonhosted.org/packages/46/3b/bec82a3ea06f66711520f75a40c8fc0b113b2a75edb36aa633eb11c4f50f/contourpy-1.3.3-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:644a6853d15b2512d67881586bd03f462c7ab755db95f16f14d7e238f2852c67", size = 385169, upload-time = "2025-07-26T12:01:45.219Z" }, + { url = "https://files.pythonhosted.org/packages/4b/32/e0f13a1c5b0f8572d0ec6ae2f6c677b7991fafd95da523159c19eff0696a/contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4debd64f124ca62069f313a9cb86656ff087786016d76927ae2cf37846b006c9", size = 362859, upload-time = "2025-07-26T12:01:46.519Z" }, + { url = "https://files.pythonhosted.org/packages/33/71/e2a7945b7de4e58af42d708a219f3b2f4cff7386e6b6ab0a0fa0033c49a9/contourpy-1.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a15459b0f4615b00bbd1e91f1b9e19b7e63aea7483d03d804186f278c0af2659", size = 1332062, upload-time = "2025-07-26T12:01:48.964Z" }, + { url = "https://files.pythonhosted.org/packages/12/fc/4e87ac754220ccc0e807284f88e943d6d43b43843614f0a8afa469801db0/contourpy-1.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca0fdcd73925568ca027e0b17ab07aad764be4706d0a925b89227e447d9737b7", size = 1403932, upload-time = "2025-07-26T12:01:51.979Z" }, + { url = "https://files.pythonhosted.org/packages/a6/2e/adc197a37443f934594112222ac1aa7dc9a98faf9c3842884df9a9d8751d/contourpy-1.3.3-cp313-cp313-win32.whl", hash = "sha256:b20c7c9a3bf701366556e1b1984ed2d0cedf999903c51311417cf5f591d8c78d", size = 185024, upload-time = "2025-07-26T12:01:53.245Z" }, + { url = "https://files.pythonhosted.org/packages/18/0b/0098c214843213759692cc638fce7de5c289200a830e5035d1791d7a2338/contourpy-1.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:1cadd8b8969f060ba45ed7c1b714fe69185812ab43bd6b86a9123fe8f99c3263", size = 226578, upload-time = "2025-07-26T12:01:54.422Z" }, + { url = "https://files.pythonhosted.org/packages/8a/9a/2f6024a0c5995243cd63afdeb3651c984f0d2bc727fd98066d40e141ad73/contourpy-1.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9", size = 193524, upload-time = "2025-07-26T12:01:55.73Z" }, + { url = "https://files.pythonhosted.org/packages/c0/b3/f8a1a86bd3298513f500e5b1f5fd92b69896449f6cab6a146a5d52715479/contourpy-1.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:88df9880d507169449d434c293467418b9f6cbe82edd19284aa0409e7fdb933d", size = 306730, upload-time = "2025-07-26T12:01:57.051Z" }, + { url = "https://files.pythonhosted.org/packages/3f/11/4780db94ae62fc0c2053909b65dc3246bd7cecfc4f8a20d957ad43aa4ad8/contourpy-1.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d06bb1f751ba5d417047db62bca3c8fde202b8c11fb50742ab3ab962c81e8216", size = 287897, upload-time = "2025-07-26T12:01:58.663Z" }, + { url = "https://files.pythonhosted.org/packages/ae/15/e59f5f3ffdd6f3d4daa3e47114c53daabcb18574a26c21f03dc9e4e42ff0/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4e6b05a45525357e382909a4c1600444e2a45b4795163d3b22669285591c1ae", size = 326751, upload-time = "2025-07-26T12:02:00.343Z" }, + { url = "https://files.pythonhosted.org/packages/0f/81/03b45cfad088e4770b1dcf72ea78d3802d04200009fb364d18a493857210/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab3074b48c4e2cf1a960e6bbeb7f04566bf36b1861d5c9d4d8ac04b82e38ba20", size = 375486, upload-time = "2025-07-26T12:02:02.128Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ba/49923366492ffbdd4486e970d421b289a670ae8cf539c1ea9a09822b371a/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c3d53c796f8647d6deb1abe867daeb66dcc8a97e8455efa729516b997b8ed99", size = 388106, upload-time = "2025-07-26T12:02:03.615Z" }, + { url = "https://files.pythonhosted.org/packages/9f/52/5b00ea89525f8f143651f9f03a0df371d3cbd2fccd21ca9b768c7a6500c2/contourpy-1.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50ed930df7289ff2a8d7afeb9603f8289e5704755c7e5c3bbd929c90c817164b", size = 352548, upload-time = "2025-07-26T12:02:05.165Z" }, + { url = "https://files.pythonhosted.org/packages/32/1d/a209ec1a3a3452d490f6b14dd92e72280c99ae3d1e73da74f8277d4ee08f/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4feffb6537d64b84877da813a5c30f1422ea5739566abf0bd18065ac040e120a", size = 1322297, upload-time = "2025-07-26T12:02:07.379Z" }, + { url = "https://files.pythonhosted.org/packages/bc/9e/46f0e8ebdd884ca0e8877e46a3f4e633f6c9c8c4f3f6e72be3fe075994aa/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2b7e9480ffe2b0cd2e787e4df64270e3a0440d9db8dc823312e2c940c167df7e", size = 1391023, upload-time = "2025-07-26T12:02:10.171Z" }, + { url = "https://files.pythonhosted.org/packages/b9/70/f308384a3ae9cd2209e0849f33c913f658d3326900d0ff5d378d6a1422d2/contourpy-1.3.3-cp313-cp313t-win32.whl", hash = "sha256:283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3", size = 196157, upload-time = "2025-07-26T12:02:11.488Z" }, + { url = "https://files.pythonhosted.org/packages/b2/dd/880f890a6663b84d9e34a6f88cded89d78f0091e0045a284427cb6b18521/contourpy-1.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8", size = 240570, upload-time = "2025-07-26T12:02:12.754Z" }, + { url = "https://files.pythonhosted.org/packages/80/99/2adc7d8ffead633234817ef8e9a87115c8a11927a94478f6bb3d3f4d4f7d/contourpy-1.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301", size = 199713, upload-time = "2025-07-26T12:02:14.4Z" }, + { url = "https://files.pythonhosted.org/packages/72/8b/4546f3ab60f78c514ffb7d01a0bd743f90de36f0019d1be84d0a708a580a/contourpy-1.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fde6c716d51c04b1c25d0b90364d0be954624a0ee9d60e23e850e8d48353d07a", size = 292189, upload-time = "2025-07-26T12:02:16.095Z" }, + { url = "https://files.pythonhosted.org/packages/fd/e1/3542a9cb596cadd76fcef413f19c79216e002623158befe6daa03dbfa88c/contourpy-1.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:cbedb772ed74ff5be440fa8eee9bd49f64f6e3fc09436d9c7d8f1c287b121d77", size = 273251, upload-time = "2025-07-26T12:02:17.524Z" }, + { url = "https://files.pythonhosted.org/packages/b1/71/f93e1e9471d189f79d0ce2497007731c1e6bf9ef6d1d61b911430c3db4e5/contourpy-1.3.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22e9b1bd7a9b1d652cd77388465dc358dafcd2e217d35552424aa4f996f524f5", size = 335810, upload-time = "2025-07-26T12:02:18.9Z" }, + { url = "https://files.pythonhosted.org/packages/91/f9/e35f4c1c93f9275d4e38681a80506b5510e9327350c51f8d4a5a724d178c/contourpy-1.3.3-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a22738912262aa3e254e4f3cb079a95a67132fc5a063890e224393596902f5a4", size = 382871, upload-time = "2025-07-26T12:02:20.418Z" }, + { url = "https://files.pythonhosted.org/packages/b5/71/47b512f936f66a0a900d81c396a7e60d73419868fba959c61efed7a8ab46/contourpy-1.3.3-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:afe5a512f31ee6bd7d0dda52ec9864c984ca3d66664444f2d72e0dc4eb832e36", size = 386264, upload-time = "2025-07-26T12:02:21.916Z" }, + { url = "https://files.pythonhosted.org/packages/04/5f/9ff93450ba96b09c7c2b3f81c94de31c89f92292f1380261bd7195bea4ea/contourpy-1.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f64836de09927cba6f79dcd00fdd7d5329f3fccc633468507079c829ca4db4e3", size = 363819, upload-time = "2025-07-26T12:02:23.759Z" }, + { url = "https://files.pythonhosted.org/packages/3e/a6/0b185d4cc480ee494945cde102cb0149ae830b5fa17bf855b95f2e70ad13/contourpy-1.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1fd43c3be4c8e5fd6e4f2baeae35ae18176cf2e5cced681cca908addf1cdd53b", size = 1333650, upload-time = "2025-07-26T12:02:26.181Z" }, + { url = "https://files.pythonhosted.org/packages/43/d7/afdc95580ca56f30fbcd3060250f66cedbde69b4547028863abd8aa3b47e/contourpy-1.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6afc576f7b33cf00996e5c1102dc2a8f7cc89e39c0b55df93a0b78c1bd992b36", size = 1404833, upload-time = "2025-07-26T12:02:28.782Z" }, + { url = "https://files.pythonhosted.org/packages/e2/e2/366af18a6d386f41132a48f033cbd2102e9b0cf6345d35ff0826cd984566/contourpy-1.3.3-cp314-cp314-win32.whl", hash = "sha256:66c8a43a4f7b8df8b71ee1840e4211a3c8d93b214b213f590e18a1beca458f7d", size = 189692, upload-time = "2025-07-26T12:02:30.128Z" }, + { url = "https://files.pythonhosted.org/packages/7d/c2/57f54b03d0f22d4044b8afb9ca0e184f8b1afd57b4f735c2fa70883dc601/contourpy-1.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:cf9022ef053f2694e31d630feaacb21ea24224be1c3ad0520b13d844274614fd", size = 232424, upload-time = "2025-07-26T12:02:31.395Z" }, + { url = "https://files.pythonhosted.org/packages/18/79/a9416650df9b525737ab521aa181ccc42d56016d2123ddcb7b58e926a42c/contourpy-1.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:95b181891b4c71de4bb404c6621e7e2390745f887f2a026b2d99e92c17892339", size = 198300, upload-time = "2025-07-26T12:02:32.956Z" }, + { url = "https://files.pythonhosted.org/packages/1f/42/38c159a7d0f2b7b9c04c64ab317042bb6952b713ba875c1681529a2932fe/contourpy-1.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:33c82d0138c0a062380332c861387650c82e4cf1747aaa6938b9b6516762e772", size = 306769, upload-time = "2025-07-26T12:02:34.2Z" }, + { url = "https://files.pythonhosted.org/packages/c3/6c/26a8205f24bca10974e77460de68d3d7c63e282e23782f1239f226fcae6f/contourpy-1.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ea37e7b45949df430fe649e5de8351c423430046a2af20b1c1961cae3afcda77", size = 287892, upload-time = "2025-07-26T12:02:35.807Z" }, + { url = "https://files.pythonhosted.org/packages/66/06/8a475c8ab718ebfd7925661747dbb3c3ee9c82ac834ccb3570be49d129f4/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d304906ecc71672e9c89e87c4675dc5c2645e1f4269a5063b99b0bb29f232d13", size = 326748, upload-time = "2025-07-26T12:02:37.193Z" }, + { url = "https://files.pythonhosted.org/packages/b4/a3/c5ca9f010a44c223f098fccd8b158bb1cb287378a31ac141f04730dc49be/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca658cd1a680a5c9ea96dc61cdbae1e85c8f25849843aa799dfd3cb370ad4fbe", size = 375554, upload-time = "2025-07-26T12:02:38.894Z" }, + { url = "https://files.pythonhosted.org/packages/80/5b/68bd33ae63fac658a4145088c1e894405e07584a316738710b636c6d0333/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ab2fd90904c503739a75b7c8c5c01160130ba67944a7b77bbf36ef8054576e7f", size = 388118, upload-time = "2025-07-26T12:02:40.642Z" }, + { url = "https://files.pythonhosted.org/packages/40/52/4c285a6435940ae25d7410a6c36bda5145839bc3f0beb20c707cda18b9d2/contourpy-1.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7301b89040075c30e5768810bc96a8e8d78085b47d8be6e4c3f5a0b4ed478a0", size = 352555, upload-time = "2025-07-26T12:02:42.25Z" }, + { url = "https://files.pythonhosted.org/packages/24/ee/3e81e1dd174f5c7fefe50e85d0892de05ca4e26ef1c9a59c2a57e43b865a/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2a2a8b627d5cc6b7c41a4beff6c5ad5eb848c88255fda4a8745f7e901b32d8e4", size = 1322295, upload-time = "2025-07-26T12:02:44.668Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b2/6d913d4d04e14379de429057cd169e5e00f6c2af3bb13e1710bcbdb5da12/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fd6ec6be509c787f1caf6b247f0b1ca598bef13f4ddeaa126b7658215529ba0f", size = 1391027, upload-time = "2025-07-26T12:02:47.09Z" }, + { url = "https://files.pythonhosted.org/packages/93/8a/68a4ec5c55a2971213d29a9374913f7e9f18581945a7a31d1a39b5d2dfe5/contourpy-1.3.3-cp314-cp314t-win32.whl", hash = "sha256:e74a9a0f5e3fff48fb5a7f2fd2b9b70a3fe014a67522f79b7cca4c0c7e43c9ae", size = 202428, upload-time = "2025-07-26T12:02:48.691Z" }, + { url = "https://files.pythonhosted.org/packages/fa/96/fd9f641ffedc4fa3ace923af73b9d07e869496c9cc7a459103e6e978992f/contourpy-1.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:13b68d6a62db8eafaebb8039218921399baf6e47bf85006fd8529f2a08ef33fc", size = 250331, upload-time = "2025-07-26T12:02:50.137Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8c/469afb6465b853afff216f9528ffda78a915ff880ed58813ba4faf4ba0b6/contourpy-1.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b7448cb5a725bb1e35ce88771b86fba35ef418952474492cf7c764059933ff8b", size = 203831, upload-time = "2025-07-26T12:02:51.449Z" }, + { url = "https://files.pythonhosted.org/packages/a5/29/8dcfe16f0107943fa92388c23f6e05cff0ba58058c4c95b00280d4c75a14/contourpy-1.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cd5dfcaeb10f7b7f9dc8941717c6c2ade08f587be2226222c12b25f0483ed497", size = 278809, upload-time = "2025-07-26T12:02:52.74Z" }, + { url = "https://files.pythonhosted.org/packages/85/a9/8b37ef4f7dafeb335daee3c8254645ef5725be4d9c6aa70b50ec46ef2f7e/contourpy-1.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:0c1fc238306b35f246d61a1d416a627348b5cf0648648a031e14bb8705fcdfe8", size = 261593, upload-time = "2025-07-26T12:02:54.037Z" }, + { url = "https://files.pythonhosted.org/packages/0a/59/ebfb8c677c75605cc27f7122c90313fd2f375ff3c8d19a1694bda74aaa63/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70f9aad7de812d6541d29d2bbf8feb22ff7e1c299523db288004e3157ff4674e", size = 302202, upload-time = "2025-07-26T12:02:55.947Z" }, + { url = "https://files.pythonhosted.org/packages/3c/37/21972a15834d90bfbfb009b9d004779bd5a07a0ec0234e5ba8f64d5736f4/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ed3657edf08512fc3fe81b510e35c2012fbd3081d2e26160f27ca28affec989", size = 329207, upload-time = "2025-07-26T12:02:57.468Z" }, + { url = "https://files.pythonhosted.org/packages/0c/58/bd257695f39d05594ca4ad60df5bcb7e32247f9951fd09a9b8edb82d1daa/contourpy-1.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:3d1a3799d62d45c18bafd41c5fa05120b96a28079f2393af559b843d1a966a77", size = 225315, upload-time = "2025-07-26T12:02:58.801Z" }, +] + +[[package]] +name = "corner" +version = "2.2.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "matplotlib" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5c/61/2d728798e9ae3bf899d962f77486cb29888d57f6fbf9561bc1435a6b1a74/corner-2.2.3.tar.gz", hash = "sha256:471b7b63395d8f1dee176bb779348ade38d56abd23404a48802a593607745e1c", size = 5932840, upload-time = "2024-11-23T12:50:56.955Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/4a/5bd0a8b981c5a93153d9eb7c63143b407cc7f8dfc9f91eedc9b6f5289eca/corner-2.2.3-py3-none-any.whl", hash = "sha256:39674b223482456c3a78234dc7bdefd21188a2d47bb8cd468104a0501f6659ec", size = 15946, upload-time = "2024-11-23T12:50:53.639Z" }, +] + +[[package]] +name = "cryptography" +version = "48.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9f/a9/db8f313fdcd85d767d4973515e1db101f9c71f95fced83233de224673757/cryptography-48.0.0.tar.gz", hash = "sha256:5c3932f4436d1cccb036cb0eaef46e6e2db91035166f1ad6505c3c9d5a635920", size = 832984, upload-time = "2026-05-04T22:59:38.133Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/3d/01f6dd9190170a5a241e0e98c2d04be3664a9e6f5b9b872cde63aff1c3dd/cryptography-48.0.0-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:0c558d2cdffd8f4bbb30fc7134c74d2ca9a476f830bb053074498fbc86f41ed6", size = 8001587, upload-time = "2026-05-04T22:57:36.803Z" }, + { url = "https://files.pythonhosted.org/packages/b2/6e/e90527eef33f309beb811cf7c982c3aeffcce8e3edb178baa4ca3ae4a6fa/cryptography-48.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f5333311663ea94f75dd408665686aaf426563556bb5283554a3539177e03b8c", size = 4690433, upload-time = "2026-05-04T22:57:40.373Z" }, + { url = "https://files.pythonhosted.org/packages/90/04/673510ed51ddff56575f306cf1617d80411ee76831ccd3097599140efdfe/cryptography-48.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7995ef305d7165c3f11ae07f2517e5a4f1d5c18da1376a0a9ed496336b69e5f3", size = 4710620, upload-time = "2026-05-04T22:57:42.935Z" }, + { url = "https://files.pythonhosted.org/packages/14/d5/e9c4ef932c8d800490c34d8bd589d64a31d5890e27ec9e9ad532be893294/cryptography-48.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:40ba1f85eaa6959837b1d51c9767e230e14612eea4ef110ee8854ada22da1bf5", size = 4696283, upload-time = "2026-05-04T22:57:45.294Z" }, + { url = "https://files.pythonhosted.org/packages/0c/29/174b9dfb60b12d59ecfc6cfa04bc88c21b42a54f01b8aae09bb6e51e4c7f/cryptography-48.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:369a6348999f94bbd53435c894377b20ab95f25a9065c283570e70150d8abc3c", size = 5296573, upload-time = "2026-05-04T22:57:47.933Z" }, + { url = "https://files.pythonhosted.org/packages/95/38/0d29a6fd7d0d1373f0c0c88a04ba20e359b257753ac497564cd660fc1d55/cryptography-48.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a0e692c683f4df67815a2d258b324e66f4738bd7a96a218c826dce4f4bd05d8f", size = 4743677, upload-time = "2026-05-04T22:57:50.067Z" }, + { url = "https://files.pythonhosted.org/packages/30/be/eef653013d5c63b6a490529e0316f9ac14a37602965d4903efed1399f32b/cryptography-48.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:18349bbc56f4743c8b12dc32e2bccb2cf83ee8b69a3bba74ef8ae857e26b3d25", size = 4330808, upload-time = "2026-05-04T22:57:52.301Z" }, + { url = "https://files.pythonhosted.org/packages/84/9e/500463e87abb7a0a0f9f256ec21123ecde0a7b5541a15e840ea54551fd81/cryptography-48.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e8eac43dfca5c4cccc6dad9a80504436fca53bb9bc3100a2386d730fbe6b602", size = 4695941, upload-time = "2026-05-04T22:57:54.603Z" }, + { url = "https://files.pythonhosted.org/packages/e3/dc/7303087450c2ec9e7fbb750e17c2abfbc658f23cbd0e54009509b7cc4091/cryptography-48.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9ccdac7d40688ecb5a3b4a604b8a88c8002e3442d6c60aead1db2a89a041560c", size = 5252579, upload-time = "2026-05-04T22:57:57.207Z" }, + { url = "https://files.pythonhosted.org/packages/d0/c0/7101d3b7215edcdc90c45da544961fd8ed2d6448f77577460fa75a8443f7/cryptography-48.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:bd72e68b06bb1e96913f97dd4901119bc17f39d4586a5adf2d3e47bc2b9d58b5", size = 4743326, upload-time = "2026-05-04T22:57:59.535Z" }, + { url = "https://files.pythonhosted.org/packages/ac/d8/5b833bad13016f562ab9d063d68199a4bd121d18458e439515601d3357ec/cryptography-48.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:59baa2cb386c4f0b9905bd6eb4c2a79a69a128408fd31d32ca4d7102d4156321", size = 4826672, upload-time = "2026-05-04T22:58:01.996Z" }, + { url = "https://files.pythonhosted.org/packages/98/e1/7074eb8bf3c135558c73fc2bcf0f5633f912e6fb87e868a55c454080ef09/cryptography-48.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9249e3cd978541d665967ac2cb2787fd6a62bddf1e75b3e347a594d7dacf4f74", size = 4972574, upload-time = "2026-05-04T22:58:03.968Z" }, + { url = "https://files.pythonhosted.org/packages/04/70/e5a1b41d325f797f39427aa44ef8baf0be500065ab6d8e10369d850d4a4f/cryptography-48.0.0-cp311-abi3-win32.whl", hash = "sha256:9c459db21422be75e2809370b829a87eb37f74cd785fc4aa9ea1e5f43b47cda4", size = 3294868, upload-time = "2026-05-04T22:58:06.467Z" }, + { url = "https://files.pythonhosted.org/packages/f4/ac/8ac51b4a5fc5932eb7ee5c517ba7dc8cd834f0048962b6b352f00f41ebf9/cryptography-48.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:5b012212e08b8dd5edc78ef54da83dd9892fd9105323b3993eff6bea65dc21d7", size = 3817107, upload-time = "2026-05-04T22:58:08.845Z" }, + { url = "https://files.pythonhosted.org/packages/6b/84/70e3feea9feea87fd7cbe77efb2712ae1e3e6edf10749dc6e95f4e60e455/cryptography-48.0.0-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:3cb07a3ed6431663cd321ea8a000a1314c74211f823e4177fefa2255e057d1ec", size = 7986556, upload-time = "2026-05-04T22:58:11.172Z" }, + { url = "https://files.pythonhosted.org/packages/89/6e/18e07a618bb5442ba10cf4df16e99c071365528aa570dfcb8c02e25a303b/cryptography-48.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8c7378637d7d88016fa6791c159f698b3d3eed28ebf844ac36b9dc04a14dae18", size = 4684776, upload-time = "2026-05-04T22:58:13.712Z" }, + { url = "https://files.pythonhosted.org/packages/be/6a/4ea3b4c6c6759794d5ee2103c304a5076dc4b19ae1f9fe47dba439e159e9/cryptography-48.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc90c0b39b2e3c65ef52c804b72e3c58f8a04ab2a1871272798e5f9572c17d20", size = 4698121, upload-time = "2026-05-04T22:58:16.448Z" }, + { url = "https://files.pythonhosted.org/packages/2f/59/6ff6ad6cae03bb887da2a5860b2c9805f8dac969ef01ce563336c49bd1d1/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:76341972e1eff8b4bea859f09c0d3e64b96ce931b084f9b9b7db8ef364c30eff", size = 4690042, upload-time = "2026-05-04T22:58:18.544Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b4/fc334ed8cfd705aca282fe4d8f5ae64a8e0f74932e9feecb344610cf6e4d/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:55b7718303bf06a5753dcdccf2f3945cf18ad7bffde41b61226e4db31ab89a9c", size = 5282526, upload-time = "2026-05-04T22:58:20.75Z" }, + { url = "https://files.pythonhosted.org/packages/11/08/9f8c5386cc4cd90d8255c7cdd0f5baf459a08502a09de30dc51f553d38dc/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:a64697c641c7b1b2178e573cbc31c7c6684cd56883a478d75143dbb7118036db", size = 4733116, upload-time = "2026-05-04T22:58:23.627Z" }, + { url = "https://files.pythonhosted.org/packages/b8/77/99307d7574045699f8805aa500fa0fb83422d115b5400a064ddd306d7750/cryptography-48.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:561215ea3879cb1cbbf272867e2efda62476f240fb58c64de6b393ae19246741", size = 4316030, upload-time = "2026-05-04T22:58:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/fd/36/a608b98337af3cb2aff4818e406649d30572b7031918b04c87d979495348/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ad64688338ed4bc1a6618076ba75fd7194a5f1797ac60b47afe926285adb3166", size = 4689640, upload-time = "2026-05-04T22:58:27.747Z" }, + { url = "https://files.pythonhosted.org/packages/dd/a6/825010a291b4438aecc1f568bc428189fc1175515223632477c07dc0a6df/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:906cbf0670286c6e0044156bc7d4af9cbb0ef6db9f73e52c3ec56ba6bdde5336", size = 5237657, upload-time = "2026-05-04T22:58:29.848Z" }, + { url = "https://files.pythonhosted.org/packages/b9/09/4e76a09b4caa29aad535ddc806f5d4c5d01885bd978bd984fbc6ca032cae/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:ea8990436d914540a40ab24b6a77c0969695ed52f4a4874c5137ccf7045a7057", size = 4732362, upload-time = "2026-05-04T22:58:32.009Z" }, + { url = "https://files.pythonhosted.org/packages/18/78/444fa04a77d0cb95f417dda20d450e13c56ba8e5220fc892a1658f44f882/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c18684a7f0cc9a3cb60328f496b8e3372def7c5d2df39ac267878b05565aaaae", size = 4819580, upload-time = "2026-05-04T22:58:34.254Z" }, + { url = "https://files.pythonhosted.org/packages/38/85/ea67067c70a1fd4be2c63d35eeed82658023021affccc7b17705f8527dd2/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9be5aafa5736574f8f15f262adc81b2a9869e2cfe9014d52a44633905b40d52c", size = 4963283, upload-time = "2026-05-04T22:58:36.376Z" }, + { url = "https://files.pythonhosted.org/packages/75/54/cc6d0f3deac3e81c7f847e8a189a12b6cdd65059b43dad25d4316abd849a/cryptography-48.0.0-cp314-cp314t-win32.whl", hash = "sha256:c17dfe85494deaeddc5ce251aebd1d60bbe6afc8b62071bb0b469431a000124f", size = 3270954, upload-time = "2026-05-04T22:58:38.791Z" }, + { url = "https://files.pythonhosted.org/packages/49/67/cc947e288c0758a4e5473d1dcb743037ab7785541265a969240b8885441a/cryptography-48.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27241b1dc9962e056062a8eef1991d02c3a24569c95975bd2322a8a52c6e5e12", size = 3797313, upload-time = "2026-05-04T22:58:40.746Z" }, + { url = "https://files.pythonhosted.org/packages/f2/63/61d4a4e1c6b6bab6ce1e213cd36a24c415d90e76d78c5eb8577c5541d2e8/cryptography-48.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:58d00498e8933e4a194f3076aee1b4a97dfec1a6da444535755822fe5d8b0b86", size = 7983482, upload-time = "2026-05-04T22:58:43.769Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ac/f5b5995b87770c693e2596559ffafe195b4033a57f14a82268a2842953f3/cryptography-48.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:614d0949f4790582d2cc25553abd09dd723025f0c0e7c67376a1d77196743d6e", size = 4683266, upload-time = "2026-05-04T22:58:46.064Z" }, + { url = "https://files.pythonhosted.org/packages/ec/c6/8b14f67e18338fbc4adb76f66c001f5c3610b3e2d1837f268f47a347dbbb/cryptography-48.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ce4bfae76319a532a2dc68f82cc32f5676ee792a983187dac07183690e5c66f", size = 4696228, upload-time = "2026-05-04T22:58:48.22Z" }, + { url = "https://files.pythonhosted.org/packages/ea/73/f808fbae9514bd91b47875b003f13e284c8c6bdfd904b7944e803937eec1/cryptography-48.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:2eb992bbd4661238c5a397594c83f5b4dc2bc5b848c365c8f991b6780efcc5c7", size = 4689097, upload-time = "2026-05-04T22:58:50.9Z" }, + { url = "https://files.pythonhosted.org/packages/93/01/d86632d7d28db8ae83221995752eeb6639ffb374c2d22955648cf8d52797/cryptography-48.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:22a5cb272895dce158b2cacdfdc3debd299019659f42947dbdac6f32d68fe832", size = 5283582, upload-time = "2026-05-04T22:58:53.017Z" }, + { url = "https://files.pythonhosted.org/packages/02/e1/50edc7a50334807cc4791fc4a0ce7468b4a1416d9138eab358bfc9a3d70b/cryptography-48.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2b4d59804e8408e2fea7d1fbaf218e5ec984325221db76e6a241a9abd6cdd95c", size = 4730479, upload-time = "2026-05-04T22:58:55.611Z" }, + { url = "https://files.pythonhosted.org/packages/6f/af/99a582b1b1641ff5911ac559beb45097cf79efd4ead4657f578ef1af2d47/cryptography-48.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:984a20b0f62a26f48a3396c72e4bc34c66e356d356bf370053066b3b6d54634a", size = 4326481, upload-time = "2026-05-04T22:58:57.607Z" }, + { url = "https://files.pythonhosted.org/packages/90/ee/89aa26a06ef0a7d7611788ffd571a7c50e368cc6a4d5eef8b4884e866edb/cryptography-48.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5a5ed8fde7a1d09376ca0b40e68cd59c69fe23b1f9768bd5824f54681626032a", size = 4688713, upload-time = "2026-05-04T22:59:00.077Z" }, + { url = "https://files.pythonhosted.org/packages/70/ba/bcb1b0bb7a33d4c7c0c4d4c7874b4a62ae4f56113a5f4baefa362dfb1f0f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:8cd666227ef7af430aa5914a9910e0ddd703e75f039cef0825cd0da71b6b711a", size = 5238165, upload-time = "2026-05-04T22:59:02.317Z" }, + { url = "https://files.pythonhosted.org/packages/c9/70/ca4003b1ce5ca3dc3186ada51908c8a9b9ff7d5cab83cc0d43ee14ec144f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9071196d81abc88b3516ac8cdfad32e2b66dd4a5393a8e68a961e9161ddc6239", size = 4729947, upload-time = "2026-05-04T22:59:05.255Z" }, + { url = "https://files.pythonhosted.org/packages/44/a0/4ec7cf774207905aef1a8d11c3750d5a1db805eb380ee4e16df317870128/cryptography-48.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1e2d54c8be6152856a36f0882ab231e70f8ec7f14e93cf87db8a2ed056bf160c", size = 4822059, upload-time = "2026-05-04T22:59:07.802Z" }, + { url = "https://files.pythonhosted.org/packages/1e/75/a2e55f99c16fcac7b5d6c1eb19ad8e00799854d6be5ca845f9259eae1681/cryptography-48.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a5da777e32ffed6f85a7b2b3f7c5cbc88c146bfcd0a1d7baf5fcc6c52ee35dd4", size = 4960575, upload-time = "2026-05-04T22:59:09.851Z" }, + { url = "https://files.pythonhosted.org/packages/b8/23/6e6f32143ab5d8b36ca848a502c4bcd477ae75b9e1677e3530d669062578/cryptography-48.0.0-cp39-abi3-win32.whl", hash = "sha256:77a2ccbbe917f6710e05ba9adaa25fb5075620bf3ea6fb751997875aff4ae4bd", size = 3279117, upload-time = "2026-05-04T22:59:12.019Z" }, + { url = "https://files.pythonhosted.org/packages/9d/9a/0fea98a70cf1749d41d738836f6349d97945f7c89433a259a6c2642eefeb/cryptography-48.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:16cd65b9330583e4619939b3a3843eec1e6e789744bb01e7c7e2e62e33c239c8", size = 3792100, upload-time = "2026-05-04T22:59:14.884Z" }, + { url = "https://files.pythonhosted.org/packages/be/d2/024b5e06be9d44cb021fb0e1a03d34d63989cf56a0fe62f3dfbab695b9b4/cryptography-48.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:84cf79f0dc8b36ac5da873481716e87aef31fcfa0444f9e1d8b4b2cece142855", size = 3950391, upload-time = "2026-05-04T22:59:17.415Z" }, + { url = "https://files.pythonhosted.org/packages/bc/17/3861e17c56fa0fd37491a14a8673fdb77c57fc5693cafe745ea8b06dba75/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:fdfef35d751d510fcef5252703621574364fec16418c4a1e5e1055248401054b", size = 4637126, upload-time = "2026-05-04T22:59:20.197Z" }, + { url = "https://files.pythonhosted.org/packages/f0/0a/7e226dbff530f21480727eb764973a7bff2b912f8e15cd4f129e71b56d1d/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:0890f502ddf7d9c6426129c3f49f5c0a39278ed7cd6322c8755ffca6ee675a13", size = 4667270, upload-time = "2026-05-04T22:59:22.647Z" }, + { url = "https://files.pythonhosted.org/packages/3b/f2/5a72274ca9f1b2a8b44a662ee0bf1b435909deb473d6f97bcd035bcdbc71/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:ecde28a596bead48b0cfd2a1b4416c3d43074c2d785e3a398d7ec1fc4d0f7fbb", size = 4636797, upload-time = "2026-05-04T22:59:24.912Z" }, + { url = "https://files.pythonhosted.org/packages/b4/e1/48cedb2fe63626e91ded1edad159e2a4fb8b6906c4425eb7749673077ce7/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:4defde8685ae324a9eb9d818717e93b4638ef67070ac9bc15b8ca85f63048355", size = 4666800, upload-time = "2026-05-04T22:59:27.474Z" }, + { url = "https://files.pythonhosted.org/packages/a2/ca/7e8365deec19afb2b2c7be7c1c0aa8f99633b54e90c570999acda93260fc/cryptography-48.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:db63bf618e5dea46c07de12e900fe1cdd2541e6dc9dbae772a70b7d4d4765f6a", size = 3739536, upload-time = "2026-05-04T22:59:29.61Z" }, +] + +[[package]] +name = "cuda-bindings" +version = "13.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-pathfinder", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/1a/fe/7351d7e586a8b4c9f89731bfe4cf0148223e8f9903ff09571f78b3fb0682/cuda_bindings-13.2.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08b395f79cb89ce0cd8effff07c4a1e20101b873c256a1aeb286e8fd7bd0f556", size = 5744254, upload-time = "2026-03-11T00:12:29.798Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ef/184aa775e970fc089942cd9ec6302e6e44679d4c14549c6a7ea45bf7f798/cuda_bindings-13.2.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6f3682ec3c4769326aafc67c2ba669d97d688d0b7e63e659d36d2f8b72f32d6", size = 6329075, upload-time = "2026-03-11T00:12:32.319Z" }, + { url = "https://files.pythonhosted.org/packages/e0/a9/3a8241c6e19483ac1f1dcf5c10238205dcb8a6e9d0d4d4709240dff28ff4/cuda_bindings-13.2.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:721104c603f059780d287969be3d194a18d0cc3b713ed9049065a1107706759d", size = 5730273, upload-time = "2026-03-11T00:12:37.18Z" }, + { url = "https://files.pythonhosted.org/packages/e9/94/2748597f47bb1600cd466b20cab4159f1530a3a33fe7f70fee199b3abb9e/cuda_bindings-13.2.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1eba9504ac70667dd48313395fe05157518fd6371b532790e96fbb31bbb5a5e1", size = 6313924, upload-time = "2026-03-11T00:12:39.462Z" }, + { url = "https://files.pythonhosted.org/packages/52/c8/b2589d68acf7e3d63e2be330b84bc25712e97ed799affbca7edd7eae25d6/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e865447abfb83d6a98ad5130ed3c70b1fc295ae3eeee39fd07b4ddb0671b6788", size = 5722404, upload-time = "2026-03-11T00:12:44.041Z" }, + { url = "https://files.pythonhosted.org/packages/1f/92/f899f7bbb5617bb65ec52a6eac1e9a1447a86b916c4194f8a5001b8cde0c/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46d8776a55d6d5da9dd6e9858fba2efcda2abe6743871dee47dd06eb8cb6d955", size = 6320619, upload-time = "2026-03-11T00:12:45.939Z" }, + { url = "https://files.pythonhosted.org/packages/df/93/eef988860a3ca985f82c4f3174fc0cdd94e07331ba9a92e8e064c260337f/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6629ca2df6f795b784752409bcaedbd22a7a651b74b56a165ebc0c9dcbd504d0", size = 5614610, upload-time = "2026-03-11T00:12:50.337Z" }, + { url = "https://files.pythonhosted.org/packages/18/23/6db3aba46864aee357ab2415135b3fe3da7e9f1fa0221fa2a86a5968099c/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7dca0da053d3b4cc4869eff49c61c03f3c5dbaa0bcd712317a358d5b8f3f385d", size = 6149914, upload-time = "2026-03-11T00:12:52.374Z" }, + { url = "https://files.pythonhosted.org/packages/c0/87/87a014f045b77c6de5c8527b0757fe644417b184e5367db977236a141602/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6464b30f46692d6c7f65d4a0e0450d81dd29de3afc1bb515653973d01c2cd6e", size = 5685673, upload-time = "2026-03-11T00:12:56.371Z" }, + { url = "https://files.pythonhosted.org/packages/ee/5e/c0fe77a73aaefd3fff25ffaccaac69c5a63eafdf8b9a4c476626ef0ac703/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4af9f3e1be603fa12d5ad6cfca7844c9d230befa9792b5abdf7dd79979c3626", size = 6191386, upload-time = "2026-03-11T00:12:58.965Z" }, + { url = "https://files.pythonhosted.org/packages/5f/58/ed2c3b39c8dd5f96aa7a4abef0d47a73932c7a988e30f5fa428f00ed0da1/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df850a1ff8ce1b3385257b08e47b70e959932f5f432d0a4e46a355962b4e4771", size = 5507469, upload-time = "2026-03-11T00:13:04.063Z" }, + { url = "https://files.pythonhosted.org/packages/1f/01/0c941b112ceeb21439b05895eace78ca1aa2eaaf695c8521a068fd9b4c00/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8a16384c6494e5485f39314b0b4afb04bee48d49edb16d5d8593fd35bbd231b", size = 6059693, upload-time = "2026-03-11T00:13:06.003Z" }, +] + +[[package]] +name = "cuda-pathfinder" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/d0/c177e29701cf1d3008d7d2b16b5fc626592ce13bd535f8795c5f57187e0e/cuda_pathfinder-1.5.4-py3-none-any.whl", hash = "sha256:9563d3175ce1828531acf4b94e1c1c7d67208c347ca002493e2654878b26f4b7", size = 51657, upload-time = "2026-04-27T22:42:07.712Z" }, +] + +[[package]] +name = "cuda-toolkit" +version = "13.0.2" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/57/b2/453099f5f3b698d7d0eab38916aac44c7f76229f451709e2eb9db6615dcd/cuda_toolkit-13.0.2-py2.py3-none-any.whl", hash = "sha256:b198824cf2f54003f50d64ada3a0f184b42ca0846c1c94192fa269ecd97a66eb", size = 2364, upload-time = "2025-12-19T23:24:07.328Z" }, +] + +[package.optional-dependencies] +cudart = [ + { name = "nvidia-cuda-runtime", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +cufft = [ + { name = "nvidia-cufft", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +cufile = [ + { name = "nvidia-cufile", marker = "sys_platform == 'linux'" }, +] +cupti = [ + { name = "nvidia-cuda-cupti", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +curand = [ + { name = "nvidia-curand", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +cusolver = [ + { name = "nvidia-cusolver", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +cusparse = [ + { name = "nvidia-cusparse", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +nvjitlink = [ + { name = "nvidia-nvjitlink", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +nvrtc = [ + { name = "nvidia-cuda-nvrtc", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +nvtx = [ + { name = "nvidia-nvtx", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] + +[[package]] +name = "cycler" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, +] + +[[package]] +name = "dateparser" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "regex" }, + { name = "tzlocal" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/46/2d/a0ccdb78788064fa0dc901b8524e50615c42be1d78b78d646d0b28d09180/dateparser-1.4.0.tar.gz", hash = "sha256:97a21840d5ecdf7630c584f673338a5afac5dfe84f647baf4d7e8df98f9354a4", size = 321512, upload-time = "2026-03-26T09:56:10.292Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b4/0b/3c3bb7cbe757279e693a0be6049048012f794d01f81099609ecd53b899f0/dateparser-1.4.0-py3-none-any.whl", hash = "sha256:7902b8e85d603494bf70a5a0b1decdddb2270b9c6e6b2bc8a57b93476c0df378", size = 300379, upload-time = "2026-03-26T09:56:08.409Z" }, +] + +[[package]] +name = "dill" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/81/e1/56027a71e31b02ddc53c7d65b01e68edf64dea2932122fe7746a516f75d5/dill-0.4.1.tar.gz", hash = "sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa", size = 187315, upload-time = "2026-01-19T02:36:56.85Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl", hash = "sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d", size = 120019, upload-time = "2026-01-19T02:36:55.663Z" }, +] + +[[package]] +name = "dqsegdb2" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "igwn-auth-utils" }, + { name = "igwn-segments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c4/37/7874b39abede48fe05c3b5a25092e419e521145e9705c97b711965e5f05d/dqsegdb2-1.3.0.tar.gz", hash = "sha256:4e291899cd395daf5913c48a835d213ef20833d78354871cb848988cc2cc8ae4", size = 33661, upload-time = "2025-01-08T16:36:58.072Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/67/c977e7307d432911fe28acf55dba01f84f3a0a4a2a968350195c0f52d23e/dqsegdb2-1.3.0-py3-none-any.whl", hash = "sha256:c93087da332b7d91519a370abed3cfc91b64a6bf393ec64d79c2747c837aeb66", size = 27957, upload-time = "2025-01-08T16:36:56.881Z" }, +] + +[[package]] +name = "dynesty" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "matplotlib" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/56/90/b538da9a03522e91252acc36dceb9e38538db0303f2dc314e0e417404d2c/dynesty-3.0.0.tar.gz", hash = "sha256:806c83b21904cf7df73c2478a9e0e8a2b7d811cd0d613109f3f810a7111d12b0", size = 35559700, upload-time = "2025-10-04T21:34:19.578Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/45/68f4ab90f889abbeec5932751643f055bb811a33ef7361ac849426dcc38a/dynesty-3.0.0-py3-none-any.whl", hash = "sha256:4cdbbb39c61158f4d882445b064b742fcdc4d03ba9b2ba845801cd2ca6fcb761", size = 102885, upload-time = "2025-10-04T21:34:12.006Z" }, +] + +[[package]] +name = "emcee" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cb/53/1045ee878cb24281387079f8ee4f0ade1622c6aae1ed1fd91a53e4fa5b19/emcee-3.1.6.tar.gz", hash = "sha256:11af4daf6ab8f9ca69681e3c29054665db7bbd87fd4eb8e437d2c3a1248c637d", size = 2871117, upload-time = "2024-04-19T10:03:19.555Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/ef/2196b9bf88ffa1bde45853c72df021fbd07a8fa91a0f59a22d14a050dc04/emcee-3.1.6-py2.py3-none-any.whl", hash = "sha256:f2d63752023bdccf744461450e512a5b417ae7d28f18e12acd76a33de87580cb", size = 47351, upload-time = "2024-04-19T10:03:17.522Z" }, +] + +[[package]] +name = "equinox" +version = "0.13.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jax", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "jax", version = "0.10.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "jaxtyping", version = "0.3.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "jaxtyping", version = "0.3.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "typing-extensions" }, + { name = "wadler-lindig" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/ff/522336d2f8264f2ad97119710b76e2cddf66145d03a1e89899175d26b192/equinox-0.13.8.tar.gz", hash = "sha256:dd075050018e2dd02e252e9d29d3060f7e67f085622d8d27a8e89e24bb8523db", size = 145257, upload-time = "2026-05-05T10:03:43.258Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ae/d6/69a76c8ccdef14af687c497040292a46e59fc7a0ab24724b60e50ca61030/equinox-0.13.8-py3-none-any.whl", hash = "sha256:ca004348533cc30a63ebe8823d7dd4bb626dce17743d40bbddb89b402ef2a240", size = 185813, upload-time = "2026-05-05T10:03:41.673Z" }, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, +] + +[[package]] +name = "filelock" +version = "3.29.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/fe/997687a931ab51049acce6fa1f23e8f01216374ea81374ddee763c493db5/filelock-3.29.0.tar.gz", hash = "sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90", size = 57571, upload-time = "2026-04-19T15:39:10.068Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl", hash = "sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258", size = 39812, upload-time = "2026-04-19T15:39:08.752Z" }, +] + +[[package]] +name = "fonttools" +version = "4.63.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/84/69/c97f2c18e0db87d2c7b15da1974dace76ae938f1cfa22e2727a648b7ed43/fonttools-4.63.0.tar.gz", hash = "sha256:caeb583deeb5168e694b65cda8b4ee62abedfa66cf88488734466f2366b9c4e0", size = 3597189, upload-time = "2026-05-14T12:04:30.958Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/c9/4141c90a90db20f807c7e10bfd689fe53eb8f7f4caff58ee4d4dfe46919f/fonttools-4.63.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e3297a6a4059b4acc3a1e9a8b04741f240a80044eef08ebd32e8b5bcdddce75b", size = 2884632, upload-time = "2026-05-14T12:02:38.56Z" }, + { url = "https://files.pythonhosted.org/packages/b8/46/ad12b5c10eae602d7ef814b02afa08aacbf89da917fed5b071282b7eadc2/fonttools-4.63.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b1cd75a03ad8cb5bc40c90bfde68c0c47de423aa19e5c0f362b43520645eea94", size = 2429441, upload-time = "2026-05-14T12:02:41.162Z" }, + { url = "https://files.pythonhosted.org/packages/90/8f/bdca24a84c81d56fffed052229cdcff368f6e05882e526f4558891481f65/fonttools-4.63.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0425b277a59cff3d80ca42162a8de360f318438a2ac83570842a678d826d579", size = 4946346, upload-time = "2026-05-14T12:02:43.41Z" }, + { url = "https://files.pythonhosted.org/packages/04/59/a639c0e136441ee91a65b56fdf89e5d075927e7a09c559d1b0f5276577db/fonttools-4.63.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d7e5c9973aa04c95650c96e5f5ad865fbf42d62079163ecfab1e01cbc2504c22", size = 4903184, upload-time = "2026-05-14T12:02:45.742Z" }, + { url = "https://files.pythonhosted.org/packages/e6/53/91b7e0cb45b536f3da1b29ba8cbab89f27e8b986809e0b1982303a3f4eca/fonttools-4.63.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cb014d58140a38135f16064c74c652ed57aa0b75cbf8bb59cac821f7edb5334e", size = 4922967, upload-time = "2026-05-14T12:02:48.386Z" }, + { url = "https://files.pythonhosted.org/packages/c7/b7/87439bf44e6b97c5538cd29d0b7e366a5b8ce2cc132a4134fb67fa3f2fa2/fonttools-4.63.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:032038247a96c1690f9f31e377c389383c902531b085aa4e4dabd6f57f870e69", size = 5042799, upload-time = "2026-05-14T12:02:50.424Z" }, + { url = "https://files.pythonhosted.org/packages/ad/7c/8b96c3263b89ef99cded544c0f0636686f85dbd3c211c4dceef0231fca23/fonttools-4.63.0-cp310-cp310-win32.whl", hash = "sha256:a8b33a82979e0a6a34ff435cc81317be1f95ec1ebb7a3a2d1c8a6a54f02ae44e", size = 1519704, upload-time = "2026-05-14T12:02:52.523Z" }, + { url = "https://files.pythonhosted.org/packages/e5/4d/2c2f0069970b6907de8fb5b05c5c0193cc22f717df151d1c7aef1c738f58/fonttools-4.63.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c18358a155d75034911c5ee397a5b44cd19dd325dbb8b35fb60bf421d6a72ac", size = 1568666, upload-time = "2026-05-14T12:02:54.917Z" }, + { url = "https://files.pythonhosted.org/packages/75/2b/a7f1545bdf5da69c4bda0cea2a5781f0ad2a6623e0277267672db43c5fe6/fonttools-4.63.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2b8ae05d9eacf6081414d759c0a352769ac28ce31280d6bb8e77b03f9e3c449f", size = 2881793, upload-time = "2026-05-14T12:02:56.645Z" }, + { url = "https://files.pythonhosted.org/packages/49/50/965308c703f085f225db2886813b27e015b8b3438c350b22dd65b52c2a2c/fonttools-4.63.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79cdc9f567aec74a72918fd060283911406750cbc9fd28c1316023deb6ce31a9", size = 2428130, upload-time = "2026-05-14T12:02:58.891Z" }, + { url = "https://files.pythonhosted.org/packages/d8/38/6937fbd7f2dc3a6b48725851bc2c15ec949b9af14d9bbcb5fe83cdf9bdf9/fonttools-4.63.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c14b4fd138c4bafcca294765c547914e1aa431ae1ca94ab99d8db08c958bd3b", size = 5111952, upload-time = "2026-05-14T12:03:01.263Z" }, + { url = "https://files.pythonhosted.org/packages/0b/43/a81f20050a3115b57d62c8e781446949512eac36690dc384ccea65ff4cc1/fonttools-4.63.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d76ac49f929aecaf82d83250b8347e099d7aecba0f4726c1d9b6df3b8bb5fe18", size = 5082308, upload-time = "2026-05-14T12:03:03.211Z" }, + { url = "https://files.pythonhosted.org/packages/67/00/cdd9d4944ca6ae280d01e69cc37bde3bf663630b837a6fc6d2cd65d80e0e/fonttools-4.63.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dcf076a4474fe0d7367e5bbf5b052c7284fa1feca729c04176ce513521afd8a0", size = 5087932, upload-time = "2026-05-14T12:03:05.147Z" }, + { url = "https://files.pythonhosted.org/packages/f5/f1/0aa0dbea778c75adbef223c42019fd47d22262b905974d62d829545d485f/fonttools-4.63.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7dd683fef0663e9f0f45cf541d788d24caa3ec9db50796b588e1757d8b3bc007", size = 5213271, upload-time = "2026-05-14T12:03:07.238Z" }, + { url = "https://files.pythonhosted.org/packages/a8/99/253e4056e1f0e67b9390125a154b73b5eb73ad521bece95c004858fdeec2/fonttools-4.63.0-cp311-cp311-win32.whl", hash = "sha256:afefc1ed0a59785a7fb06ea7e1678e849c193e1e387db783579bc7b3056fcfcb", size = 2304473, upload-time = "2026-05-14T12:03:09.271Z" }, + { url = "https://files.pythonhosted.org/packages/08/60/defa5e69641db890a63be281f41345f4c33b157824eaf0b9fad3e08b0dcb/fonttools-4.63.0-cp311-cp311-win_amd64.whl", hash = "sha256:063e08bd17bd5a90127a14123de0d6a952dbc847695fd98b63c043d58057f90c", size = 2356389, upload-time = "2026-05-14T12:03:11.53Z" }, + { url = "https://files.pythonhosted.org/packages/08/ef/b3c6b9b5be2f82416d73fe2ed2e96e2793cd80e7510bd6a17ca79cdd88ec/fonttools-4.63.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:37dd23e621e3b0aef1baa70a303b80aaf38449632cfc8fd2a55fb285bbccfc02", size = 2881131, upload-time = "2026-05-14T12:03:13.386Z" }, + { url = "https://files.pythonhosted.org/packages/44/a0/c815bea63117fa63e4e1c01f8a1110d2112fa003f838e6467094ec2432ce/fonttools-4.63.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a9faff9e0c1f76f9fd55899d2ce785832efebab37eb8ae13995853aef178bef0", size = 2426704, upload-time = "2026-05-14T12:03:15.801Z" }, + { url = "https://files.pythonhosted.org/packages/44/04/0b91d8e916e92ad1fac9e4624760baf0fd5ff2ead614c2f68fb21373f03f/fonttools-4.63.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef3048ef05dbb552b89817713d9cac912e00d0fde4a3105c00d29e52e10c89af", size = 5044298, upload-time = "2026-05-14T12:03:18.085Z" }, + { url = "https://files.pythonhosted.org/packages/77/c7/2342da9830e3e9d4870305ca5d2091d2a83284f2953079b7bdd3b5e029d8/fonttools-4.63.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58dc6bb86a78d782f00f9190ca02c119cf5bbe2807536e361e18d42019f877d8", size = 4999800, upload-time = "2026-05-14T12:03:20.161Z" }, + { url = "https://files.pythonhosted.org/packages/e6/6d/67fe16c48d7ce050979b33f47e0d28a318f02da030602e944c34f7a16ef3/fonttools-4.63.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ee08ebfa58f6e1aeff5697ab9582105bb620008c1caafb681e4c557e7483027b", size = 4982666, upload-time = "2026-05-14T12:03:22.87Z" }, + { url = "https://files.pythonhosted.org/packages/f2/00/3bbab338c07c71fa56269953845e92c951a61457bbbb0f1022551ea266d9/fonttools-4.63.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:27fdc65af8da6f88b9c6121c47a464cbe359fcfff7ff6fc2d37a1f395d755b78", size = 5133598, upload-time = "2026-05-14T12:03:25.168Z" }, + { url = "https://files.pythonhosted.org/packages/62/f2/aa27c7f98db5b064883dadcc5283947e81e034de42e22a33675878d98b54/fonttools-4.63.0-cp312-cp312-win32.whl", hash = "sha256:af2fd1664d00a397d75f806985ddb36282091c2131a73a6485c23b4a34722263", size = 2292575, upload-time = "2026-05-14T12:03:27.496Z" }, + { url = "https://files.pythonhosted.org/packages/87/36/cccb9bc2a6ab63d1b2980374f0dca72ce95ae267c9b4cfe77455bb70d0d4/fonttools-4.63.0-cp312-cp312-win_amd64.whl", hash = "sha256:59ac449f8cca9b4ffa08d2e7bbadad87ce710d69d1eda5c3c1ce579baa987272", size = 2343211, upload-time = "2026-05-14T12:03:30.057Z" }, + { url = "https://files.pythonhosted.org/packages/0f/8d/d8fec3dcde2963f8c908fb315e5ff2cd0ac34f82394bbbf73a2aa5145ce3/fonttools-4.63.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:cd7e9857e5e63738b9d9fd707bc1f59c8b09e5177726d23664db393c59bb08bd", size = 2876062, upload-time = "2026-05-14T12:03:32.554Z" }, + { url = "https://files.pythonhosted.org/packages/ef/71/d935dc54e4ff121bfdd11e08702db63a7e6f25af21d8a3d7b7212df53641/fonttools-4.63.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c2a2a42198b696a6f48fad91709afb55176e66a5e566131219dba372fb7f8c59", size = 2424594, upload-time = "2026-05-14T12:03:34.86Z" }, + { url = "https://files.pythonhosted.org/packages/8e/40/e76320afa1df918e146155ef239b1719ee266092e96f5423bfd075affba1/fonttools-4.63.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e874792a8212b44583ea02189d9e693906b2f78b261f372f95d6c563210ac1d", size = 5024840, upload-time = "2026-05-14T12:03:36.745Z" }, + { url = "https://files.pythonhosted.org/packages/ce/36/0b805d8c485f872f65a509cbe3b58a5d0d17bee855333b54a150c79d3061/fonttools-4.63.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:22135da48a348785c5e2d5d2d9d6bec5ed44adacbaeb9db12d9493bf6c6bfa68", size = 4975801, upload-time = "2026-05-14T12:03:38.833Z" }, + { url = "https://files.pythonhosted.org/packages/c8/26/2cee03d0aa083ab022da5c07aff9ed3f689da1defb81ad6917c9627896da/fonttools-4.63.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ccf41f2efdf56994d22d73bef4ced1052161958169428d06ba9724ea9e9a64be", size = 4965009, upload-time = "2026-05-14T12:03:41.494Z" }, + { url = "https://files.pythonhosted.org/packages/7e/48/cc4b66d9058c0d0982c833fad10127c4b0e9324606aafa41382295ca4102/fonttools-4.63.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9ced0bd02ac751dd6319b0da88aaef24414e3b0dbc32bb4f24944821a3741a27", size = 5105892, upload-time = "2026-05-14T12:03:43.525Z" }, + { url = "https://files.pythonhosted.org/packages/d8/1f/a98a30a814b9ddef3a2e706025f90b9e0bc94890e6cb15254bc86547d11a/fonttools-4.63.0-cp313-cp313-win32.whl", hash = "sha256:85be818f5506e8a7753153def2c9550178f0ecae6a47b5e0e8dbb23f7cc90380", size = 2291313, upload-time = "2026-05-14T12:03:45.594Z" }, + { url = "https://files.pythonhosted.org/packages/92/46/5177b01f3b4abfdd4409f31cca4ab279c9343a26efbe9ec78c97fc612e02/fonttools-4.63.0-cp313-cp313-win_amd64.whl", hash = "sha256:ba04cb5891d4c0c21b6da95eda8d7b090021508a294fff33464fc7d241e0856b", size = 2342299, upload-time = "2026-05-14T12:03:47.414Z" }, + { url = "https://files.pythonhosted.org/packages/27/d2/23d25e3f247b328be58d04a4c9f894178a0d1eda7d42867cfb388adaf416/fonttools-4.63.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fd1e3094f42d806d3d7c79162fc59e5910fcbe3a7360c385b8da969bc4493745", size = 2875338, upload-time = "2026-05-14T12:03:50.052Z" }, + { url = "https://files.pythonhosted.org/packages/cd/58/7dfa0c761cb3b2964e2a84c4dc986c926a87de0cb9fb60d5b28ded3f2914/fonttools-4.63.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:6e528da43bc3791085f8cb6141b1d13e459226790240340fcbb4625649238b03", size = 2422661, upload-time = "2026-05-14T12:03:52.154Z" }, + { url = "https://files.pythonhosted.org/packages/dd/87/64cfa18a7a1621d17b7f4502b2b0ed8a135a90c3db51ea590ee99043e76b/fonttools-4.63.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b2248c5decb223562f7902ff6325077a073f608ee8e33e88ad88db734eb9f49", size = 5010526, upload-time = "2026-05-14T12:03:54.647Z" }, + { url = "https://files.pythonhosted.org/packages/36/e1/a8933a72c45a87177fbde2696e0d0755c8c9062f8c077a961c6215fa27b1/fonttools-4.63.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:308f957cdeaf8abe4e5f2f124902ef405448af92c90f80e302a3b771c2e6116b", size = 4923946, upload-time = "2026-05-14T12:03:56.984Z" }, + { url = "https://files.pythonhosted.org/packages/27/60/872e6e233b8c5e8b41413796ff18b7fe479661bd40147e071b450dfad7a1/fonttools-4.63.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bf00f21eb5fb721dbaf73d1e9da6d02a1af7768f2ebcf9798be98beab8ba90f6", size = 4962489, upload-time = "2026-05-14T12:03:59.443Z" }, + { url = "https://files.pythonhosted.org/packages/30/c4/83c24f2ec38b90cfda84bf4b1a1f49df80e84a1db4e7ac6e0d41bf23bc39/fonttools-4.63.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c1aaa4b9c75798400ac043ce04d74e7830376c85095a5a6ed7cba2f17a266bf4", size = 5071870, upload-time = "2026-05-14T12:04:02.122Z" }, + { url = "https://files.pythonhosted.org/packages/de/40/3ae22b60ff1d41ce0bd044b31238cdc72cef99f28b976f1e128ebd618c9b/fonttools-4.63.0-cp314-cp314-win32.whl", hash = "sha256:22693918177bd9ceabec4736d338045f357769416fc6b0b2508eefef75b08616", size = 2295026, upload-time = "2026-05-14T12:04:04.47Z" }, + { url = "https://files.pythonhosted.org/packages/c3/d4/98078064ccc76b45cb0f6c002452011e93c4bd26f6850344f0951cc1fe89/fonttools-4.63.0-cp314-cp314-win_amd64.whl", hash = "sha256:7d782fac32985914c351556f68ac0855391572bcd87de50e05970d3cd4c96fc5", size = 2347454, upload-time = "2026-05-14T12:04:06.752Z" }, + { url = "https://files.pythonhosted.org/packages/49/4e/652d1580c5f4e39f7d103b0c793e4773129ad633dce4addd0cf4dfebde02/fonttools-4.63.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:6db5140a60a5d731d21ec076745b40a310607731b0a565b50776393188649001", size = 2958152, upload-time = "2026-05-14T12:04:08.706Z" }, + { url = "https://files.pythonhosted.org/packages/0e/55/ad864c9a9b219f552eb46b32cd7906c466e5a578ba0c3abfcc0fe7413eb6/fonttools-4.63.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7d76edbff9014094dbf03bd2d074709dfa6ec7aba13d838c937a2b33d2d6a86e", size = 2460809, upload-time = "2026-05-14T12:04:10.783Z" }, + { url = "https://files.pythonhosted.org/packages/ea/2b/0aa8db70f18cf52e49b4ed5ecec68547f981160bf5ded3b5aed6faa0a6f9/fonttools-4.63.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0eac00b9118c3c2f87d272e45341871c5b3066baa3c86897fa634a7c3fb59096", size = 5148649, upload-time = "2026-05-14T12:04:12.747Z" }, + { url = "https://files.pythonhosted.org/packages/7f/63/18e4369c25043096f1048e0c9915951adc4f842bd81c6b18155824d6fa99/fonttools-4.63.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:51394295f1a51de8b5f30bdb1e1b9a4231536c7064ef5c6e211eec19fa36036f", size = 4932147, upload-time = "2026-05-14T12:04:14.806Z" }, + { url = "https://files.pythonhosted.org/packages/a1/3f/67f3eac2ffd8a98446c5022f8ed3864eac878a5ff7af8df4c8286dba16cc/fonttools-4.63.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9e12f105d2b6342c559c298afb674006bb2893afc7102dcf8a1b55b0486b4e40", size = 5027237, upload-time = "2026-05-14T12:04:17.675Z" }, + { url = "https://files.pythonhosted.org/packages/1a/ba/4e6214cb38a7b04779e97bb7636de9a5c7f20af7018d03dee0b64c08510a/fonttools-4.63.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:796f27556dbe094c4824f75ca85267e4df776c79036c8441469a4df37038c196", size = 5053933, upload-time = "2026-05-14T12:04:20.818Z" }, + { url = "https://files.pythonhosted.org/packages/34/3b/214dcc19ee31d3d38fb5ad2755c11ef0514e5dc300bbaf41c0b69f393799/fonttools-4.63.0-cp314-cp314t-win32.whl", hash = "sha256:948428a275741f0b64b113c955425a953314f4b9ab9997f73a72c83e68e569c8", size = 2359326, upload-time = "2026-05-14T12:04:24.22Z" }, + { url = "https://files.pythonhosted.org/packages/dd/1e/3ff1a9b523058c2eeb6a9d50f5574e2a738200d0d94107d5bc4105e8da3f/fonttools-4.63.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6d4741eb179121cab9eea4cb2393d24492373a260d7945006358c08cfbf45419", size = 2425829, upload-time = "2026-05-14T12:04:26.829Z" }, + { url = "https://files.pythonhosted.org/packages/2c/47/c99d5268f354002ce80f8d029cd9d7d872969da1de8b93d32de4dc56d6f4/fonttools-4.63.0-py3-none-any.whl", hash = "sha256:445af2eab030a16b9171ea8bdda7ebf7d96bda2df88ee182a464252f6e05e20d", size = 1164562, upload-time = "2026-05-14T12:04:29.092Z" }, +] + +[[package]] +name = "fsspec" +version = "2026.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d5/8d/1c51c094345df128ca4a990d633fe1a0ff28726c9e6b3c41ba65087bba1d/fsspec-2026.4.0.tar.gz", hash = "sha256:301d8ac70ae90ef3ad05dcf94d6c3754a097f9b5fe4667d2787aa359ec7df7e4", size = 312760, upload-time = "2026-04-29T20:42:38.635Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/0c/043d5e551459da400957a1395e0febbf771446ff34291afcbe3d8be2a279/fsspec-2026.4.0-py3-none-any.whl", hash = "sha256:11ef7bb35dab8a394fde6e608221d5cf3e8499401c249bebaeaad760a1a8dec2", size = 203402, upload-time = "2026-04-29T20:42:36.842Z" }, +] + +[[package]] +name = "george" +version = "0.4.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dc/35/1d0a2a7d190dc7406678c78f5a5aeeeab39a3d4ac4021867cd5680928c5f/george-0.4.4.tar.gz", hash = "sha256:dbf5e10aded012a67c48dc52bed5636523552a4ff2775946bad8b9cca9b501e8", size = 5465405, upload-time = "2025-04-12T10:42:22.272Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/91/bcd8ba2923767ac5ea5472331a87736c9c2a2d555953852661c224ba73d4/george-0.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9289a224c39aa382189bfd9ee492ccc6f97431aa3425b7651de3ba7922024758", size = 284613, upload-time = "2025-04-12T10:41:59.361Z" }, + { url = "https://files.pythonhosted.org/packages/5e/c1/c6aaf530580f78c3d89b36ee74d6f774e1d0d17dfd11fcf987119d9aee6f/george-0.4.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e8c66370ae3ce44f0c2d1759bd4fbffdf18daf05eb1712049892a0e2f806722", size = 397262, upload-time = "2025-04-12T10:42:01.323Z" }, + { url = "https://files.pythonhosted.org/packages/91/db/7b0f5abaab57ea4c7e2bc88d63828dbac383f881fffd6a0afa45437c5c56/george-0.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:6c03dbe18799c746e0d22d3667507b2b7d088bb087ffd07d6b3bcfaa2e23e59e", size = 297557, upload-time = "2025-04-12T10:42:02.576Z" }, + { url = "https://files.pythonhosted.org/packages/3a/1f/9a03c0b5d5cbf5c2e2cf2dd4d0d23272b5ca0b852eee62dc025bf10d72dc/george-0.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:be3a6a19e8fa42f8d884c71910e13c18a274097a8368485718a9d025b383378a", size = 287161, upload-time = "2025-04-12T10:42:04.135Z" }, + { url = "https://files.pythonhosted.org/packages/b2/ab/268f3a44becbfe32f7918f4283d480b57ce9b68f773677faae14810ebde0/george-0.4.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdf71de76baab42e4fba16e9c564652d86c1910777b3722d9f5f6b42669604a9", size = 399075, upload-time = "2025-04-12T10:42:05.681Z" }, + { url = "https://files.pythonhosted.org/packages/ad/80/8bf71d45c7c6f750bbab5b5d0cb9fa1b140d912ffa6d9b0fc1ee855255fd/george-0.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:dbd6c64e223394372a8084c5f17d86f8d8cdfe70eee93f0503905a0c4a3505c2", size = 300973, upload-time = "2025-04-12T10:42:06.976Z" }, + { url = "https://files.pythonhosted.org/packages/46/d0/650bba57c684ba9527875c63b2fcb5ea7ee38f61c69f071605ff8c0b318b/george-0.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6ad5d2e31f1916d2816b033eafb23db99b9b9e61a2cda80a41b6a332ba15f77c", size = 285049, upload-time = "2025-04-12T10:42:08.585Z" }, + { url = "https://files.pythonhosted.org/packages/a1/14/0dd927b3644beb3b1ed91f502db797659f484c10ebd9821dbb8c6fae2412/george-0.4.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff67d0e306f2a2c0409b565ae0bd7e225d9d6c17e83a596c6e704f5cee962e5", size = 395606, upload-time = "2025-04-12T10:42:10.221Z" }, + { url = "https://files.pythonhosted.org/packages/cb/5e/20a210bd5c6a5e564b8d556cf43af21b7eaff8fde6c878c4d2039cfdd583/george-0.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:9fd173697c07a31b36d3b471b44dc0f0ecf3b021cb4ab18198a93516381c99b9", size = 308648, upload-time = "2025-04-12T10:42:11.754Z" }, + { url = "https://files.pythonhosted.org/packages/f8/d6/d7d04e73e047114d747fcff4b5a2580e852bac1916a7a28853e4df56ae9f/george-0.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:23dcf23f1af7feab163e280a2c59744b6b1cd50b220abe99b5d3a7b565719400", size = 285176, upload-time = "2025-04-12T10:42:12.864Z" }, + { url = "https://files.pythonhosted.org/packages/41/fd/84151712469ba168eedf73f6f44cc9f85316428b9ba05d38068093f1ee62/george-0.4.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f39c133bab7e6aa06363e8e8bcbb49d1754b64dede950d907d9e152ab54d37a5", size = 395425, upload-time = "2025-04-12T10:42:14.08Z" }, + { url = "https://files.pythonhosted.org/packages/49/05/3d9b1dfaa6bc735232f0d4711d7c4ba6a632484c8793b4aa9535a1c6eea7/george-0.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:e1011dafc3a4d4f8c358e98c0b3e3f11cf523dee9e271c8c15c294080287950d", size = 308736, upload-time = "2025-04-12T10:42:15.698Z" }, +] + +[[package]] +name = "glasflow" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "torch" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/39/6d/3b6c3468c2b1c306dc30d848652ef295e772a6f37d624573b726eca904d7/glasflow-0.4.1.tar.gz", hash = "sha256:e97eaf549743bc4ca6d05434f89ea93102f429f56322a490fca3888c44522c3d", size = 64176, upload-time = "2024-10-10T16:55:45.93Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/09/8d/11398cd06b2b2d8f7e5e34deff766bb8d93b869c88a29fb75a9c961d480a/glasflow-0.4.1-py3-none-any.whl", hash = "sha256:5579bcb53753e5f35f99398f82260947940bd5849565d64436164984158f5245", size = 76605, upload-time = "2024-10-10T16:55:44.539Z" }, +] + +[[package]] +name = "gwdatafind" +version = "2.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "igwn-auth-utils" }, + { name = "igwn-segments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/69/dd/4f517a2a36f71d5bb74b8b0796e3fec87703587503d4583f4a5b962ae60b/gwdatafind-2.1.1.tar.gz", hash = "sha256:e4710256daa7b47e901da2f2846620c551e9caaaaf22b7773c81a8ae052da43e", size = 41311, upload-time = "2025-10-31T09:46:57.9Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/00/07a1a9473ea5bbbc0be3cea79778dfd31e7e199404777907930badc9f399/gwdatafind-2.1.1-py3-none-any.whl", hash = "sha256:6e6d430fa243e6241ca0c214f1916f7973cf1937716bbca52d99a9b88650faeb", size = 45178, upload-time = "2025-10-31T09:46:56.468Z" }, +] + +[[package]] +name = "gwosc" +version = "0.8.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "requests" }, + { name = "tenacity" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/db/28/3dfccc399aca4da49354fb612ade049950ec73b4b34ec1089bb7dbb5a7fb/gwosc-0.8.2.tar.gz", hash = "sha256:6a7a97f1fd86184841f2f9d83eeccf9b3fc889104d8018ecb08276a1a4f6336b", size = 35171, upload-time = "2026-04-02T19:00:17.198Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9f/da/f3f9983547f257de9d359bcc358cdc8254b8cb00d78fe692a60b463f0ff0/gwosc-0.8.2-py3-none-any.whl", hash = "sha256:ec1ecc667e9b148e8229c0a71290921eec4e60c674096d3d639c9f44c198efeb", size = 35151, upload-time = "2026-04-02T19:00:16.09Z" }, +] + +[[package]] +name = "gwpy" +version = "3.0.14" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "astropy", version = "6.1.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "dateparser", marker = "python_full_version < '3.11'" }, + { name = "dqsegdb2", marker = "python_full_version < '3.11'" }, + { name = "gwdatafind", marker = "python_full_version < '3.11'" }, + { name = "gwosc", marker = "python_full_version < '3.11'" }, + { name = "h5py", marker = "python_full_version < '3.11'" }, + { name = "igwn-segments", marker = "python_full_version < '3.11'" }, + { name = "ligotimegps", version = "2.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "matplotlib", marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "packaging", marker = "python_full_version < '3.11'" }, + { name = "python-dateutil", marker = "python_full_version < '3.11'" }, + { name = "requests", marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "tqdm", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/05/cd/bb165e47b18cd255c1ee1fcd59802544d8de57566b449cfdaf848b69108f/gwpy-3.0.14.tar.gz", hash = "sha256:bf25d19763c9128f515144349d3f70925a8334bbd22950c01d35bb954f323f52", size = 1543294, upload-time = "2026-01-16T13:02:28.432Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/46/c893a66bd97f08ddf4a7cfde52cc58fb8e2c83acfa915f3c02c00f0aa721/gwpy-3.0.14-py3-none-any.whl", hash = "sha256:f61ed9d4b3eba7f9f534f72f03956f158ed7f9101726b5387c29fe57ce45c77e", size = 1395825, upload-time = "2026-01-16T13:02:26.55Z" }, +] + +[[package]] +name = "gwpy" +version = "4.0.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "astropy", version = "7.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "dateparser", marker = "python_full_version >= '3.11'" }, + { name = "dqsegdb2", marker = "python_full_version >= '3.11'" }, + { name = "gwdatafind", marker = "python_full_version >= '3.11'" }, + { name = "gwosc", marker = "python_full_version >= '3.11'" }, + { name = "h5py", marker = "python_full_version >= '3.11'" }, + { name = "igwn-segments", marker = "python_full_version >= '3.11'" }, + { name = "ligotimegps", version = "2.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "matplotlib", marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "packaging", marker = "python_full_version >= '3.11'" }, + { name = "python-dateutil", marker = "python_full_version >= '3.11'" }, + { name = "requests", marker = "python_full_version >= '3.11'" }, + { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "tqdm", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/92/71/1e652734a0e8f23e0f9dbf5df5698e66fa49aef4446d2c6c9eb914921192/gwpy-4.0.1.tar.gz", hash = "sha256:477aa69bd40506bfb0df5ec779d9dd6fef562a3bfabe58a52b297e25c57f61f0", size = 1615849, upload-time = "2026-02-03T10:14:59.045Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/ac/1debaaec791da4475687d22c8c2cbccc2008885046754f5e9f65997886b8/gwpy-4.0.1-py3-none-any.whl", hash = "sha256:587eb66443e41450bd89bf64891e323943afad3be385fccc1645eda2710908b7", size = 1559010, upload-time = "2026-02-03T10:14:56.928Z" }, +] + +[[package]] +name = "h5py" +version = "3.16.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/db/33/acd0ce6863b6c0d7735007df01815403f5589a21ff8c2e1ee2587a38f548/h5py-3.16.0.tar.gz", hash = "sha256:a0dbaad796840ccaa67a4c144a0d0c8080073c34c76d5a6941d6818678ef2738", size = 446526, upload-time = "2026-03-06T13:49:08.07Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/6b/231413e58a787a89b316bb0d1777da3c62257e4797e09afd8d17ad3549dc/h5py-3.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e06f864bedb2c8e7c1358e6c73af48519e317457c444d6f3d332bb4e8fa6d7d9", size = 3724137, upload-time = "2026-03-06T13:47:35.242Z" }, + { url = "https://files.pythonhosted.org/packages/74/f9/557ce3aad0fe8471fb5279bab0fc56ea473858a022c4ce8a0b8f303d64e9/h5py-3.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ec86d4fffd87a0f4cb3d5796ceb5a50123a2a6d99b43e616e5504e66a953eca3", size = 3090112, upload-time = "2026-03-06T13:47:37.634Z" }, + { url = "https://files.pythonhosted.org/packages/7a/f5/e15b3d0dc8a18e56409a839e6468d6fb589bc5207c917399c2e0706eeb44/h5py-3.16.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:86385ea895508220b8a7e45efa428aeafaa586bd737c7af9ee04661d8d84a10d", size = 4844847, upload-time = "2026-03-06T13:47:39.811Z" }, + { url = "https://files.pythonhosted.org/packages/cb/92/a8851d936547efe30cc0ce5245feac01f3ec6171f7899bc3f775c72030b3/h5py-3.16.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:8975273c2c5921c25700193b408e28d6bdd0111c37468b2d4e25dcec4cd1d84d", size = 5065352, upload-time = "2026-03-06T13:47:41.489Z" }, + { url = "https://files.pythonhosted.org/packages/2b/ae/f2adc5d0ca9626db3277a3d87516e124cbc5d0eea0bd79bc085702d04f2c/h5py-3.16.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1677ad48b703f44efc9ea0c3ab284527f81bc4f318386aaaebc5fede6bbae56f", size = 4839173, upload-time = "2026-03-06T13:47:43.586Z" }, + { url = "https://files.pythonhosted.org/packages/64/0b/e0c8c69da1d8838da023a50cd3080eae5d475691f7636b35eff20bb6ef20/h5py-3.16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c4dd4cf5f0a4e36083f73172f6cfc25a5710789269547f132a20975bfe2434c", size = 5076216, upload-time = "2026-03-06T13:47:45.315Z" }, + { url = "https://files.pythonhosted.org/packages/66/35/d88fd6718832133c885004c61ceeeb24dbd6397ef877dbed6b3a64d6a286/h5py-3.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:bdef06507725b455fccba9c16529121a5e1fbf56aa375f7d9713d9e8ff42454d", size = 3183639, upload-time = "2026-03-06T13:47:47.041Z" }, + { url = "https://files.pythonhosted.org/packages/ba/95/a825894f3e45cbac7554c4e97314ce886b233a20033787eda755ca8fecc7/h5py-3.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:719439d14b83f74eeb080e9650a6c7aa6d0d9ea0ca7f804347b05fac6fbf18af", size = 3721663, upload-time = "2026-03-06T13:47:49.599Z" }, + { url = "https://files.pythonhosted.org/packages/bf/3b/38ff88b347c3e346cda1d3fc1b65a7aa75d40632228d8b8a5d7b58508c24/h5py-3.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c3f0a0e136f2e95dd0b67146abb6668af4f1a69c81ef8651a2d316e8e01de447", size = 3087630, upload-time = "2026-03-06T13:47:51.249Z" }, + { url = "https://files.pythonhosted.org/packages/98/a8/2594cef906aee761601eff842c7dc598bea2b394a3e1c00966832b8eeb7c/h5py-3.16.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:a6fbc5367d4046801f9b7db9191b31895f22f1c6df1f9987d667854cac493538", size = 4823472, upload-time = "2026-03-06T13:47:53.085Z" }, + { url = "https://files.pythonhosted.org/packages/52/a0/c1f604538ff6db22a0690be2dc44ab59178e115f63c917794e529356ab23/h5py-3.16.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:fb1720028d99040792bb2fb31facb8da44a6f29df7697e0b84f0d79aff2e9bd3", size = 5027150, upload-time = "2026-03-06T13:47:55.043Z" }, + { url = "https://files.pythonhosted.org/packages/2e/fd/301739083c2fc4fd89950f9bcfce75d6e14b40b0ca3d40e48a8993d1722c/h5py-3.16.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:314b6054fe0b1051c2b0cb2df5cbdab15622fb05e80f202e3b6a5eee0d6fe365", size = 4814544, upload-time = "2026-03-06T13:47:56.893Z" }, + { url = "https://files.pythonhosted.org/packages/4c/42/2193ed41ccee78baba8fcc0cff2c925b8b9ee3793305b23e1f22c20bf4c7/h5py-3.16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ffbab2fedd6581f6aa31cf1639ca2cb86e02779de525667892ebf4cc9fd26434", size = 5034013, upload-time = "2026-03-06T13:47:59.01Z" }, + { url = "https://files.pythonhosted.org/packages/f7/20/e6c0ff62ca2ad1a396a34f4380bafccaaf8791ff8fccf3d995a1fc12d417/h5py-3.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:17d1f1630f92ad74494a9a7392ab25982ce2b469fc62da6074c0ce48366a2999", size = 3191673, upload-time = "2026-03-06T13:48:00.626Z" }, + { url = "https://files.pythonhosted.org/packages/f2/48/239cbe352ac4f2b8243a8e620fa1a2034635f633731493a7ff1ed71e8658/h5py-3.16.0-cp311-cp311-win_arm64.whl", hash = "sha256:85b9c49dd58dc44cf70af944784e2c2038b6f799665d0dcbbc812a26e0faa859", size = 2673834, upload-time = "2026-03-06T13:48:02.579Z" }, + { url = "https://files.pythonhosted.org/packages/c8/c0/5d4119dba94093bbafede500d3defd2f5eab7897732998c04b54021e530b/h5py-3.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c5313566f4643121a78503a473f0fb1e6dcc541d5115c44f05e037609c565c4d", size = 3685604, upload-time = "2026-03-06T13:48:04.198Z" }, + { url = "https://files.pythonhosted.org/packages/b0/42/c84efcc1d4caebafb1ecd8be4643f39c85c47a80fe254d92b8b43b1eadaf/h5py-3.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:42b012933a83e1a558c673176676a10ce2fd3759976a0fedee1e672d1e04fc9d", size = 3061940, upload-time = "2026-03-06T13:48:05.783Z" }, + { url = "https://files.pythonhosted.org/packages/89/84/06281c82d4d1686fde1ac6b0f307c50918f1c0151062445ab3b6fa5a921d/h5py-3.16.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:ff24039e2573297787c3063df64b60aab0591980ac898329a08b0320e0cf2527", size = 5198852, upload-time = "2026-03-06T13:48:07.482Z" }, + { url = "https://files.pythonhosted.org/packages/9e/e9/1a19e42cd43cc1365e127db6aae85e1c671da1d9a5d746f4d34a50edb577/h5py-3.16.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:dfc21898ff025f1e8e67e194965a95a8d4754f452f83454538f98f8a3fcb207e", size = 5405250, upload-time = "2026-03-06T13:48:09.628Z" }, + { url = "https://files.pythonhosted.org/packages/b7/8e/9790c1655eabeb85b92b1ecab7d7e62a2069e53baefd58c98f0909c7a948/h5py-3.16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:698dd69291272642ffda44a0ecd6cd3bda5faf9621452d255f57ce91487b9794", size = 5190108, upload-time = "2026-03-06T13:48:11.26Z" }, + { url = "https://files.pythonhosted.org/packages/51/d7/ab693274f1bd7e8c5f9fdd6c7003a88d59bedeaf8752716a55f532924fbb/h5py-3.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2b2c02b0a160faed5fb33f1ba8a264a37ee240b22e049ecc827345d0d9043074", size = 5419216, upload-time = "2026-03-06T13:48:13.322Z" }, + { url = "https://files.pythonhosted.org/packages/03/c1/0976b235cf29ead553e22f2fb6385a8252b533715e00d0ae52ed7b900582/h5py-3.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:96b422019a1c8975c2d5dadcf61d4ba6f01c31f92bbde6e4649607885fe502d6", size = 3182868, upload-time = "2026-03-06T13:48:15.759Z" }, + { url = "https://files.pythonhosted.org/packages/14/d9/866b7e570b39070f92d47b0ff1800f0f8239b6f9e45f02363d7112336c1f/h5py-3.16.0-cp312-cp312-win_arm64.whl", hash = "sha256:39c2838fb1e8d97bcf1755e60ad1f3dd76a7b2a475928dc321672752678b96db", size = 2653286, upload-time = "2026-03-06T13:48:17.279Z" }, + { url = "https://files.pythonhosted.org/packages/0f/9e/6142ebfda0cb6e9349c091eae73c2e01a770b7659255248d637bec54a88b/h5py-3.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:370a845f432c2c9619db8eed334d1e610c6015796122b0e57aa46312c22617d9", size = 3671808, upload-time = "2026-03-06T13:48:19.737Z" }, + { url = "https://files.pythonhosted.org/packages/b0/65/5e088a45d0f43cd814bc5bec521c051d42005a472e804b1a36c48dada09b/h5py-3.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:42108e93326c50c2810025aade9eac9d6827524cdccc7d4b75a546e5ab308edb", size = 3045837, upload-time = "2026-03-06T13:48:21.854Z" }, + { url = "https://files.pythonhosted.org/packages/da/1e/6172269e18cc5a484e2913ced33339aad588e02ba407fafd00d369e22ef3/h5py-3.16.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:099f2525c9dcf28de366970a5fb34879aab20491589fa89ce2863a84218bb524", size = 5193860, upload-time = "2026-03-06T13:48:24.071Z" }, + { url = "https://files.pythonhosted.org/packages/bd/98/ef2b6fe2903e377cbe870c3b2800d62552f1e3dbe81ce49e1923c53d1c5c/h5py-3.16.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:9300ad32dea9dfc5171f94d5f6948e159ed93e4701280b0f508773b3f582f402", size = 5400417, upload-time = "2026-03-06T13:48:25.728Z" }, + { url = "https://files.pythonhosted.org/packages/bc/81/5b62d760039eed64348c98129d17061fdfc7839fc9c04eaaad6dee1004e4/h5py-3.16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:171038f23bccddfc23f344cadabdfc9917ff554db6a0d417180d2747fe4c75a7", size = 5185214, upload-time = "2026-03-06T13:48:27.436Z" }, + { url = "https://files.pythonhosted.org/packages/28/c4/532123bcd9080e250696779c927f2cb906c8bf3447df98f5ceb8dcded539/h5py-3.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7e420b539fb6023a259a1b14d4c9f6df8cf50d7268f48e161169987a57b737ff", size = 5414598, upload-time = "2026-03-06T13:48:29.49Z" }, + { url = "https://files.pythonhosted.org/packages/c3/d9/a27997f84341fc0dfcdd1fe4179b6ba6c32a7aa880fdb8c514d4dad6fba3/h5py-3.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:18f2bbcd545e6991412253b98727374c356d67caa920e68dc79eab36bf5fedad", size = 3175509, upload-time = "2026-03-06T13:48:31.131Z" }, + { url = "https://files.pythonhosted.org/packages/a5/23/bb8647521d4fd770c30a76cfc6cb6a2f5495868904054e92f2394c5a78ff/h5py-3.16.0-cp313-cp313-win_arm64.whl", hash = "sha256:656f00e4d903199a1d58df06b711cf3ca632b874b4207b7dbec86185b5c8c7d4", size = 2647362, upload-time = "2026-03-06T13:48:33.411Z" }, + { url = "https://files.pythonhosted.org/packages/48/3c/7fcd9b4c9eed82e91fb15568992561019ae7a829d1f696b2c844355d95dd/h5py-3.16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9c9d307c0ef862d1cd5714f72ecfafe0a5d7529c44845afa8de9f46e5ba8bd65", size = 3678608, upload-time = "2026-03-06T13:48:35.183Z" }, + { url = "https://files.pythonhosted.org/packages/6a/b7/9366ed44ced9b7ef357ab48c94205280276db9d7f064aa3012a97227e966/h5py-3.16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8c1eff849cdd53cbc73c214c30ebdb6f1bb8b64790b4b4fc36acdb5e43570210", size = 3054773, upload-time = "2026-03-06T13:48:37.139Z" }, + { url = "https://files.pythonhosted.org/packages/58/a5/4964bc0e91e86340c2bbda83420225b2f770dcf1eb8a39464871ad769436/h5py-3.16.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:e2c04d129f180019e216ee5f9c40b78a418634091c8782e1f723a6ca3658b965", size = 5198886, upload-time = "2026-03-06T13:48:38.879Z" }, + { url = "https://files.pythonhosted.org/packages/f1/16/d905e7f53e661ce2c24686c38048d8e2b750ffc4350009d41c4e6c6c9826/h5py-3.16.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:e4360f15875a532bc7b98196c7592ed4fc92672a57c0a621355961cafb17a6dd", size = 5404883, upload-time = "2026-03-06T13:48:41.324Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f2/58f34cb74af46d39f4cd18ea20909a8514960c5a3e5b92fd06a28161e0a8/h5py-3.16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3fae9197390c325e62e0a1aa977f2f62d994aa87aab182abbea85479b791197c", size = 5192039, upload-time = "2026-03-06T13:48:43.117Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ca/934a39c24ce2e2db017268c08da0537c20fa0be7e1549be3e977313fc8f5/h5py-3.16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:43259303989ac8adacc9986695b31e35dba6fd1e297ff9c6a04b7da5542139cc", size = 5421526, upload-time = "2026-03-06T13:48:44.838Z" }, + { url = "https://files.pythonhosted.org/packages/3e/14/615a450205e1b56d16c6783f5ccd116cde05550faad70ae077c955654a75/h5py-3.16.0-cp314-cp314-win_amd64.whl", hash = "sha256:fa48993a0b799737ba7fd21e2350fa0a60701e58180fae9f2de834bc39a147ab", size = 3183263, upload-time = "2026-03-06T13:48:47.117Z" }, + { url = "https://files.pythonhosted.org/packages/7b/48/a6faef5ed632cae0c65ac6b214a6614a0b510c3183532c521bdb0055e117/h5py-3.16.0-cp314-cp314-win_arm64.whl", hash = "sha256:1897a771a7f40d05c262fc8f37376ec37873218544b70216872876c627640f63", size = 2663450, upload-time = "2026-03-06T13:48:48.707Z" }, + { url = "https://files.pythonhosted.org/packages/5d/32/0c8bb8aedb62c772cf7c1d427c7d1951477e8c2835f872bc0a13d1f85f86/h5py-3.16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:15922e485844f77c0b9d275396d435db3baa58292a9c2176a386e072e0cf2491", size = 3760693, upload-time = "2026-03-06T13:48:50.453Z" }, + { url = "https://files.pythonhosted.org/packages/1d/1f/fcc5977d32d6387c5c9a694afee716a5e20658ac08b3ff24fdec79fb05f2/h5py-3.16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:df02dd29bd247f98674634dfe41f89fd7c16ba3d7de8695ec958f58404a4e618", size = 3181305, upload-time = "2026-03-06T13:48:52.221Z" }, + { url = "https://files.pythonhosted.org/packages/f5/a1/af87f64b9f986889884243643621ebbd4ac72472ba8ec8cec891ac8e2ca1/h5py-3.16.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:0f456f556e4e2cebeebd9d66adf8dc321770a42593494a0b6f0af54a7567b242", size = 5074061, upload-time = "2026-03-06T13:48:54.089Z" }, + { url = "https://files.pythonhosted.org/packages/cc/d0/146f5eaff3dc246a9c7f6e5e4f42bd45cc613bce16693bcd4d1f7c958bf5/h5py-3.16.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:3e6cb3387c756de6a9492d601553dffea3fe11b5f22b443aac708c69f3f55e16", size = 5279216, upload-time = "2026-03-06T13:48:56.75Z" }, + { url = "https://files.pythonhosted.org/packages/a1/9d/12a13424f1e604fc7df9497b73c0356fb78c2fb206abd7465ce47226e8fd/h5py-3.16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8389e13a1fd745ad2856873e8187fd10268b2d9677877bb667b41aebd771d8b7", size = 5070068, upload-time = "2026-03-06T13:48:59.169Z" }, + { url = "https://files.pythonhosted.org/packages/41/8c/bbe98f813722b4873818a8db3e15aa3e625b59278566905ac439725e8070/h5py-3.16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:346df559a0f7dcb31cf8e44805319e2ab24b8957c45e7708ce503b2ec79ba725", size = 5300253, upload-time = "2026-03-06T13:49:02.033Z" }, + { url = "https://files.pythonhosted.org/packages/32/9e/87e6705b4d6890e7cecdf876e2a7d3e40654a2ae37482d79a6f1b87f7b92/h5py-3.16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:4c6ab014ab704b4feaa719ae783b86522ed0bf1f82184704ed3c9e4e3228796e", size = 3381671, upload-time = "2026-03-06T13:49:04.351Z" }, + { url = "https://files.pythonhosted.org/packages/96/91/9fad90cfc5f9b2489c7c26ad897157bce82f0e9534a986a221b99760b23b/h5py-3.16.0-cp314-cp314t-win_arm64.whl", hash = "sha256:faca8fb4e4319c09d83337adc80b2ca7d5c5a343c2d6f1b6388f32cfecca13c1", size = 2740706, upload-time = "2026-03-06T13:49:06.347Z" }, +] + +[[package]] +name = "idna" +version = "3.15" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/77/7b3966d0b9d1d31a36ddf1746926a11dface89a83409bf1483f0237aa758/idna-3.15.tar.gz", hash = "sha256:ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc", size = 199245, upload-time = "2026-05-12T22:45:57.011Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/23/408243171aa9aaba178d3e2559159c24c1171a641aa83b67bdd3394ead8e/idna-3.15-py3-none-any.whl", hash = "sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8", size = 72340, upload-time = "2026-05-12T22:45:55.733Z" }, +] + +[[package]] +name = "igwn-auth-utils" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, + { name = "requests" }, + { name = "safe-netrc" }, + { name = "scitokens" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/88/a94ef60db81bb38137a3a7278a71efe729623bfa4a5bf4cabed8a8f44b69/igwn_auth_utils-1.4.0.tar.gz", hash = "sha256:8ebd331a1d6de16e843e94cde2dc0a09d07a7fbc089bc525fa0eabddd89ea187", size = 40693, upload-time = "2025-05-30T14:35:33.009Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/70/a0/6d3b9b7ff91c1cc73a0f45972a5032a4e89439ffb16e617806867cee905b/igwn_auth_utils-1.4.0-py3-none-any.whl", hash = "sha256:3936dbc4e630b1a31eeb1376abf25ac7b518629f84e34ecc37a9d88ebd86e582", size = 34292, upload-time = "2025-05-30T14:35:31.861Z" }, +] + +[[package]] +name = "igwn-ligolw" +version = "2.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "igwn-segments" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "python-dateutil" }, + { name = "pyyaml" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/74/56/7bd63ee4a7bd64b5104b2c3a3cf4cd291865021fd4e50b14974b054a9928/igwn_ligolw-2.1.1.tar.gz", hash = "sha256:4ca1860e79ec5e25d575ae4e27fa8035a269af6fcda9da33f090ceb85b34b64b", size = 2339801, upload-time = "2026-01-19T19:01:35.462Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/29/0c3d0c0f1664b49793125169d61c3c8e3d2af82cc13b8f4fb24e6865a56f/igwn_ligolw-2.1.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:94986bdc229a70b714498453ba62edb6e1ebed0da757d2b9b824fe753253ece1", size = 139994, upload-time = "2026-01-19T19:00:50.836Z" }, + { url = "https://files.pythonhosted.org/packages/fc/17/3569ca16c7520ba8a4933913e6fb8c83605a882d75dba56db22ddcb64063/igwn_ligolw-2.1.1-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:b5c42d9e55ed73bda5e69268ac7a4ba3a7dcaa42bf6915ba498685a2a1d54583", size = 139955, upload-time = "2026-01-19T19:00:52.114Z" }, + { url = "https://files.pythonhosted.org/packages/41/61/e9874d55a9b6a48f4733adeced394d06f0856bf0367ead158443f9620b9e/igwn_ligolw-2.1.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cb3eb5f4b1f3412f4780f126216efbc2b0a42548f5b319e530bb57f729478410", size = 163027, upload-time = "2026-01-19T19:00:53.886Z" }, + { url = "https://files.pythonhosted.org/packages/18/e4/709e8d1ea210e582ee9afb2c7116f9b276a445d6b0c54229aee32821e8ef/igwn_ligolw-2.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1375a3a7a72b2841f5a3929d345d7ea86f0d9e068ca8fef1b32b87eab8417d14", size = 164281, upload-time = "2026-01-19T19:00:55.39Z" }, + { url = "https://files.pythonhosted.org/packages/e5/87/522af9c807a4ac0b22b5c3b2c490d28f04d215f2800e045c28f52ca04843/igwn_ligolw-2.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c1e39ce8160f512f68a2a98f3d5e4077de45c96bbd7e0a4e43627c92d1dbe4f4", size = 163312, upload-time = "2026-01-19T19:00:56.418Z" }, + { url = "https://files.pythonhosted.org/packages/f3/e1/f15beefcf28134e40aba0b4a6090ddf802d210ec07259d5e4cd9b1ba1744/igwn_ligolw-2.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6c03363fb6abb5f5e7a801a6e32e83c8af3249211b800bd33046bff9c779a4ad", size = 162864, upload-time = "2026-01-19T19:00:57.641Z" }, + { url = "https://files.pythonhosted.org/packages/4d/46/3966e1c23a961440698c00010622e0a2f32b11e46c660915d8a72bd259e8/igwn_ligolw-2.1.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:d38825dff55331a35f7280bdfc7b46f82e649499679dfc5e7af4fecfba5fa0ae", size = 139997, upload-time = "2026-01-19T19:00:58.792Z" }, + { url = "https://files.pythonhosted.org/packages/42/08/8c9a9a1c56abfbf7736721c2b430d9b03067c9e7190a348af70b9ef6b61e/igwn_ligolw-2.1.1-cp311-cp311-macosx_13_0_x86_64.whl", hash = "sha256:5b1ebbdece02c73c112f7bfcf54687b8885d2399c904e3bb953840c2461b7d97", size = 139958, upload-time = "2026-01-19T19:01:00.061Z" }, + { url = "https://files.pythonhosted.org/packages/e9/d6/c8da65edf532a1d166aa433227cabd3a0193769c928362304900e5bd9209/igwn_ligolw-2.1.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ff984d9ea6d83e03ea604b8761fca5c43862a22c856f619a624150b55aad4850", size = 165917, upload-time = "2026-01-19T19:01:01.092Z" }, + { url = "https://files.pythonhosted.org/packages/ec/e0/08766c66048cb905e2c3ab4b5f3864786a79845ed7c3cc7163c52c3cfea5/igwn_ligolw-2.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56d359a2a2271b8db33021f9dcc09d331953451a7b56b980ae245fd9d21f4de5", size = 167203, upload-time = "2026-01-19T19:01:02.272Z" }, + { url = "https://files.pythonhosted.org/packages/57/1d/cbf99ca2a6f29b169662cb66ed02246521b1f638761e725532962ba32e37/igwn_ligolw-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e9bde91cf3208bac074400e339c06f9ffd0836f8021981d3637f3ee7786de746", size = 166137, upload-time = "2026-01-19T19:01:03.323Z" }, + { url = "https://files.pythonhosted.org/packages/58/26/abc641f6f311853b44905c36e9f5cfd3d58d1b766c13adf1259655091489/igwn_ligolw-2.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4330d2350f779b8265df79b39b44520e4545a3ca3e25121521d6423f09e717d7", size = 165710, upload-time = "2026-01-19T19:01:04.607Z" }, + { url = "https://files.pythonhosted.org/packages/37/cd/9b72058881126d7fcdd8b29c306c345417996259faf44f2b1c3f5e52e7cd/igwn_ligolw-2.1.1-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:c45d42d7b3752c7452e88c5bbf98ebb71b84446e6d2dcb84f6779acd0f5a02ec", size = 140111, upload-time = "2026-01-19T19:01:05.841Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a5/0941742b1bd313a451087f37448033d2a053c0260741d2648a1daa7a6b01/igwn_ligolw-2.1.1-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:9a31c61dae5eb5ab48dc8f8ec16e7fe454710d40154fecea9d25272a490d5c0b", size = 140387, upload-time = "2026-01-19T19:01:06.993Z" }, + { url = "https://files.pythonhosted.org/packages/29/3b/54173d9a3691c6cdcce6b7971e46af0436feebab00585966c37a5e6bbd3c/igwn_ligolw-2.1.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5b72a5a5d2b630fbfb659102bc9dc06a5f9a0b4740ca2202d319dcba172642d1", size = 168723, upload-time = "2026-01-19T19:01:08.286Z" }, + { url = "https://files.pythonhosted.org/packages/2b/1c/ab0e78d5fe594ff700eb17367e4fd92ba5ed4c95c24fcaa1e70327b24b90/igwn_ligolw-2.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8d05aab5f4e9e681203fcfd4b2162312b03ae496afbd6734c8de4833e1efc585", size = 169556, upload-time = "2026-01-19T19:01:09.629Z" }, + { url = "https://files.pythonhosted.org/packages/53/b8/332b9ff77ae0b35add7fd0f0b833c82987a4c0e057a750e57d1f246f4227/igwn_ligolw-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:444a9d8b3f60c9ca964fc6735094faf9e4d4ba188ac7268cf27ed408be052130", size = 168091, upload-time = "2026-01-19T19:01:10.621Z" }, + { url = "https://files.pythonhosted.org/packages/8e/1b/274a3c85f976ce929d77599a07d5acafffa5363f7a5c33dd19b7693f0c73/igwn_ligolw-2.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d6893c9642b14e7972e156e37ca5867bbd80a3f5e5c1c7ab2a8cc98f9389795f", size = 167987, upload-time = "2026-01-19T19:01:11.625Z" }, + { url = "https://files.pythonhosted.org/packages/db/14/115b6f1afc6f6e1185a0c8248fcc2dab0cb5ec638b7d8edf89b22e742182/igwn_ligolw-2.1.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:8ed8e9aa2e22201ae10932c2027185553eb40ca4a358444434cc0764faca64fd", size = 140034, upload-time = "2026-01-19T19:01:13.268Z" }, + { url = "https://files.pythonhosted.org/packages/22/29/72a83bf1ee9e39090359319874f97c38aa5a3cb0903329dc8fb470e40eb7/igwn_ligolw-2.1.1-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:03fca460d68563f98b9f6792598c1050b0e5178f5278c6a6a11f4fa457ecbf18", size = 140077, upload-time = "2026-01-19T19:01:14.342Z" }, + { url = "https://files.pythonhosted.org/packages/82/41/3b4e4a12c4eac61a9a286070f79d71aac595467898229ea8712aa31d2994/igwn_ligolw-2.1.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a3b3573d6f0704702412cb3bf73e50ae91d5a8748bd55ea36b5da49bdc4f4a95", size = 168633, upload-time = "2026-01-19T19:01:15.627Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f8/aa58ba53e8a6224282477a15897c2f1bf30f05000ce81d75c88317e895f5/igwn_ligolw-2.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:504e8554659489da8a574d616f3f1d7e5c0a3e63a31d1ae3bf10b2b673461c59", size = 169552, upload-time = "2026-01-19T19:01:17.029Z" }, + { url = "https://files.pythonhosted.org/packages/d3/d0/1a27a0d51332c486ac50019056f909e788a90c7eea00f5cba9263800eec8/igwn_ligolw-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdf07edc462ddae640b9fd327205b83123599efac05463d8be6badbe3473adde", size = 168149, upload-time = "2026-01-19T19:01:18.3Z" }, + { url = "https://files.pythonhosted.org/packages/39/cf/dae49ce938a438af61dfa857b36dfd1d6ec098fa5389b59fc76b431eb757/igwn_ligolw-2.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:de5ba73f5d05135e40b9e46befd36fe187a8f884367bed9450b0cbe7ef6e9212", size = 168044, upload-time = "2026-01-19T19:01:19.443Z" }, + { url = "https://files.pythonhosted.org/packages/a0/82/dc028303a14ca51c9cb6f909bb3c934a8399d97e5e52700f8cc3ea9b2977/igwn_ligolw-2.1.1-cp314-cp314-macosx_13_0_x86_64.whl", hash = "sha256:169ae1f74a72c5191b18060231ae5662e32946ea20939ec34be20cfc86df71b7", size = 140101, upload-time = "2026-01-19T19:01:20.465Z" }, + { url = "https://files.pythonhosted.org/packages/3c/8e/308c4f105e1209507d11220ce034b5030045b53e6f4b29f83e9165ba8efe/igwn_ligolw-2.1.1-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:e4971dc9eaff99f39706019d8a2596dd164e8a352005f4395ac91d99d31add34", size = 140404, upload-time = "2026-01-19T19:01:21.937Z" }, + { url = "https://files.pythonhosted.org/packages/a2/c0/fe74d6c94935a8ab3b345f24d9539bbafa60880b33735a6b2ec083e891d2/igwn_ligolw-2.1.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cc20fc62c9948cf82ee49bc615ed8c1ddf233a9d6d011f9e978de23448ef8d63", size = 168515, upload-time = "2026-01-19T19:01:22.96Z" }, + { url = "https://files.pythonhosted.org/packages/f1/de/d97acd60af7fdd5fd6192be7279f9db1615000363e8c5418c610e6636ebb/igwn_ligolw-2.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ab01dd3320c0ef8bba208962384161cbfc02f16b300852a9839bd7dcae27fff4", size = 169634, upload-time = "2026-01-19T19:01:24.367Z" }, + { url = "https://files.pythonhosted.org/packages/ed/fa/1924a653a6b8751736907cc119ebd3aa98b868cd1dd6c5f293db0e45367f/igwn_ligolw-2.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:02de381133d8775f6dfa79c12bdabe8973aba5a33788b06b44cb12c0044dcc98", size = 168124, upload-time = "2026-01-19T19:01:25.677Z" }, + { url = "https://files.pythonhosted.org/packages/33/c0/acf7db509ca842b80100e782ff3f6c0c40547f29c3e9dfcfba4c2ec0e845/igwn_ligolw-2.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:314960894bb73d1b0d5ca2e9a754d344facd966fac499f1cf43c914745c0ff03", size = 167829, upload-time = "2026-01-19T19:01:26.717Z" }, +] + +[[package]] +name = "igwn-segments" +version = "2.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/92/ef/3500687ef4a61887bc218ab7ef8352ab7110afababaa82e958f2ca95967b/igwn_segments-2.1.1.tar.gz", hash = "sha256:672814d78f5de7582b5f39189cdc6016d7ffde95d2b8b8b8b82aa9b45e53f3dd", size = 60805, upload-time = "2026-01-19T17:51:05.142Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/87/f5dc189af8817fc722dfac1798bf0d70a4b795701be4adafd215a45e3b8d/igwn_segments-2.1.1-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:185993971cf4cac593156f47361ff62f51158bc197f2cdbad6f8e0165bae43dd", size = 49090, upload-time = "2026-01-19T17:50:25.921Z" }, + { url = "https://files.pythonhosted.org/packages/51/2b/cf74ffaff55de002a5717c3843c5c40b019587a8f55c26c62d1fdf6f9c7b/igwn_segments-2.1.1-cp310-cp310-macosx_15_0_arm64.whl", hash = "sha256:48867076f643508cb9f66fd460d72548ea60dab5192f49770d77883be7f00275", size = 49251, upload-time = "2026-01-19T17:50:27.427Z" }, + { url = "https://files.pythonhosted.org/packages/8d/d7/42cda68c97f11532dc8c8a7db93ee9593ef9c0f52b5d26db5dfe06934155/igwn_segments-2.1.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:edd31f115f8cc3411491300f56279be3417afde486b6cab6ee80c43c1bae5034", size = 108410, upload-time = "2026-01-19T17:50:28.603Z" }, + { url = "https://files.pythonhosted.org/packages/4c/b5/fdea6bf16f756811c6c9225e1e9c79ddf57a37c1d9609fb374afb8912573/igwn_segments-2.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:76b5626ed1f81197333fdafeac262c15339c5e5e21a92a1e4254b01487fd49ad", size = 109833, upload-time = "2026-01-19T17:50:29.948Z" }, + { url = "https://files.pythonhosted.org/packages/1e/16/1a272ce8fe797a962d6a66b2c5a43528df3b5368ef432e5537785d2a617b/igwn_segments-2.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74b765df7d2ac6e46d48a74ee8dda6663748e9341b6d06f7f5968443564b5822", size = 108421, upload-time = "2026-01-19T17:50:30.933Z" }, + { url = "https://files.pythonhosted.org/packages/91/47/ac87852a682e548ebc926fd97975b92352f63b54943a96b6bbcb7ffa7d1a/igwn_segments-2.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:22abde65098e53b41a299f0e7606b23671e07cae87128c95dfb0baf80f0ff6a8", size = 107778, upload-time = "2026-01-19T17:50:31.951Z" }, + { url = "https://files.pythonhosted.org/packages/bf/c3/3ba15630cf8b873cb10bf188793b7828cf2f7e13ba0bd5673bdad42e8636/igwn_segments-2.1.1-cp311-cp311-macosx_13_0_x86_64.whl", hash = "sha256:774c84a5c0ebf4fe6e4de3e1b8c73c02b6f53db96598a07e7d26de9cd156ef64", size = 49068, upload-time = "2026-01-19T17:50:32.953Z" }, + { url = "https://files.pythonhosted.org/packages/b4/b1/7ffbe8b7fe6b6597d913bfd32db3898ee3d1495003bfedd497b14ea696b9/igwn_segments-2.1.1-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:8cc0dde1d4f9de3a36eddf38e25baa166492737c93ad71658db25dfc46745ddd", size = 49249, upload-time = "2026-01-19T17:50:34.158Z" }, + { url = "https://files.pythonhosted.org/packages/71/f7/9110ee0e83f43ba97155897be0a864d53801ff259a674bc8e37f80e854b9/igwn_segments-2.1.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6c2231f6a8e2adeb6403e4ba5fa8fb9cd9bcb7c55fa9e67e90139020a32aff79", size = 112480, upload-time = "2026-01-19T17:50:35.064Z" }, + { url = "https://files.pythonhosted.org/packages/50/06/eb25c8ffefcbe8c9f3275855bbfad607e20d61ea6c3f5be778f8b5c0d82f/igwn_segments-2.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cfbef53fcd95c56e200b9211cd3bad4fa48c5b82586166c6fdb44306ea3b9289", size = 114264, upload-time = "2026-01-19T17:50:36.092Z" }, + { url = "https://files.pythonhosted.org/packages/84/18/ce2b83e8993ddb4d8f6c409ff3405f3e424215a88876b019f7a0d9b38d70/igwn_segments-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5ba2483b066c8f95e6e916437f7a375c0497313e1a425642b89ef1d647980f80", size = 112686, upload-time = "2026-01-19T17:50:37.479Z" }, + { url = "https://files.pythonhosted.org/packages/13/78/ec606472610da3f545cbdd03b134fc09cfb86eae3674f5198ee8a4c36501/igwn_segments-2.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5351901272f8a33b4cb8546930e744bcd912c5d899879e8477f064cd11d0d874", size = 111852, upload-time = "2026-01-19T17:50:38.627Z" }, + { url = "https://files.pythonhosted.org/packages/71/78/6764ceb793728334df4dc87d9089ca5a8010ad280c7ca13c0e4304818f83/igwn_segments-2.1.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:d8a1e36f21f9420f254a7f78c289035c8d9dc4e0f44b5a77495e87d71469bd7b", size = 49329, upload-time = "2026-01-19T17:50:39.843Z" }, + { url = "https://files.pythonhosted.org/packages/0c/76/eb614178340cbae66af617303f80748d61d74c57f0b02105b9ca85c90033/igwn_segments-2.1.1-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:139abaec24edc25e7409fe48445c5aa8d9448e98c038079d84485497abc838c0", size = 49704, upload-time = "2026-01-19T17:50:41.013Z" }, + { url = "https://files.pythonhosted.org/packages/e0/5c/f10140682e77bdf4160cd474bbc21a8e8d362dfec46131b4e66d5929b04e/igwn_segments-2.1.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f550aa55fa4165c1d3d8571d67523a010b5b365ee363516e542f732cbe54a1d8", size = 114953, upload-time = "2026-01-19T17:50:42.126Z" }, + { url = "https://files.pythonhosted.org/packages/61/05/b347e6ac22c5cbae568e410c56729a188d9d64ce340e859fc8db5b82f195/igwn_segments-2.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9aa802dfd96d24909975d92c24aee088d4be0fb3636a2d893cb196bbeb19f175", size = 115998, upload-time = "2026-01-19T17:50:43.169Z" }, + { url = "https://files.pythonhosted.org/packages/5d/a5/f6a2b14786e62630fa3adad1b87155c4d71c3b1360e8563092793b81dfae/igwn_segments-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:74abd9ab5502bfba09343d2d6fcffb7c75f935385e1ca389b8ced82e9fe67a8f", size = 114540, upload-time = "2026-01-19T17:50:44.438Z" }, + { url = "https://files.pythonhosted.org/packages/48/9e/604b28e22207db111c9f43e4214a7aa086d6d65be38c024d393a5aaab781/igwn_segments-2.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:029a7d55c786ef24f1c07850a291e111ef2fceeaae2518347203dcaa019f811c", size = 114640, upload-time = "2026-01-19T17:50:45.749Z" }, + { url = "https://files.pythonhosted.org/packages/53/e2/7eed3f40e8df9892377d638c50d653fde9b0d8fa208d451c30b19a48e3a1/igwn_segments-2.1.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:ae73f8aa905a156c25e288ddb32a03aed69b960f05d95e5b84fede85a46676d9", size = 49262, upload-time = "2026-01-19T17:50:46.676Z" }, + { url = "https://files.pythonhosted.org/packages/ec/6e/655970d647e9eb725d1cc0240971dc07606914a059704413a70e90eb3502/igwn_segments-2.1.1-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:2b6900a212aadac1fb94da7e113d351b6289ddaf8957a715b19c5ca5f2a40a22", size = 49536, upload-time = "2026-01-19T17:50:47.574Z" }, + { url = "https://files.pythonhosted.org/packages/ed/ec/24bd1f5b5fce5ba944dcdadcb7e083655d822a5ab245636c6df64dae195a/igwn_segments-2.1.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b4507bdea2de5dcbc623518c5c51e66c04ecb550341ed542f42092a8f2c38305", size = 114080, upload-time = "2026-01-19T17:50:48.467Z" }, + { url = "https://files.pythonhosted.org/packages/8a/a3/b31b6db442aca5e39f0286e6f561a19b76b759d5ef4736a20bd2c06b27ff/igwn_segments-2.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f7fa75ab219400a195485d3686871231dd653c636c8052656411041f7f7a44a", size = 115381, upload-time = "2026-01-19T17:50:49.461Z" }, + { url = "https://files.pythonhosted.org/packages/c5/ff/3f8bd0fa10ace48437d08134fcb17ff869f9fd2fd8e50f4c2a0d70886414/igwn_segments-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c230878d7d95562948f3a7067fc05abb4956632d49981d872e3bd49c11cf5835", size = 113893, upload-time = "2026-01-19T17:50:50.409Z" }, + { url = "https://files.pythonhosted.org/packages/1e/62/e50dbdeaf7503febb55be1becd3ef8f6d647945c39dc722ace0ffb577ca9/igwn_segments-2.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:95022b867b8c1eb667228822e4c2933efdf675f7d7a89798cd08809f6f1bf980", size = 113759, upload-time = "2026-01-19T17:50:51.327Z" }, + { url = "https://files.pythonhosted.org/packages/89/f8/8cde0ca1c4b4b272c012ef8913c913e24e4976ba64c4a7509b8d2a2dfba2/igwn_segments-2.1.1-cp314-cp314-macosx_13_0_x86_64.whl", hash = "sha256:16c241bae04926e415715007768e08a93bf5ad3abef71814297625e460e711f1", size = 49695, upload-time = "2026-01-19T17:50:52.636Z" }, + { url = "https://files.pythonhosted.org/packages/21/28/35643a4fdcb43748bc95acb74d4a1393e82298cc12b0d367e828c8be6416/igwn_segments-2.1.1-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:06b3333204053f9ed094b0a4476bdd817881913cc2f6be28e99acfec9f683748", size = 49610, upload-time = "2026-01-19T17:50:53.679Z" }, + { url = "https://files.pythonhosted.org/packages/92/9f/303dc735a15cc07b4264ece860325481d7a58b026408c3c52929f4f9ffe1/igwn_segments-2.1.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:24cd58c55b1bd811df1e7de76132c6f3718b5e46be6ae3d3952738d4cf438666", size = 112671, upload-time = "2026-01-19T17:50:54.72Z" }, + { url = "https://files.pythonhosted.org/packages/96/f9/aa989728fa9ae042d16ffafe73f2ed264fbbffc8e7a3b3e63eae3bc33989/igwn_segments-2.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c6d08ca43df100ae497b4e83f9c76a87e21d06fb664b17c759b24c3a56ac9b1b", size = 114766, upload-time = "2026-01-19T17:50:55.694Z" }, + { url = "https://files.pythonhosted.org/packages/4d/d8/b0d8b6d5f4d03f478a239ddca19b3de90cd5a6f4c78f581d4ea456e1438c/igwn_segments-2.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d2718a139d3e72301ce362e7a1dd5b0220a8f533118acfd846eb4b0576c6b275", size = 113475, upload-time = "2026-01-19T17:50:56.634Z" }, + { url = "https://files.pythonhosted.org/packages/39/01/d12122ca1698433f0da7b19fd2e49bccc53ffca2c6106a8cb952c74c3970/igwn_segments-2.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:482ef5b3a5f9bf07d22e1d6a46e8892f54dbfa8c088817d99ab424c09b24c60d", size = 112427, upload-time = "2026-01-19T17:50:57.554Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "interpax" +version = "0.3.14" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "equinox" }, + { name = "jax", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "jax", version = "0.10.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "jaxtyping", version = "0.3.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "jaxtyping", version = "0.3.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "lineax" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/b2/06d071b6eade1ea7e3bb0660aa3c1f8fd6484fa611cc45cd424d2bbebecf/interpax-0.3.14.tar.gz", hash = "sha256:5018c18a07d946be7466504f385b4e5ac632cd7b006c1cead42b1aefe37e16e3", size = 58669, upload-time = "2026-04-18T00:35:09.305Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/77/3b4c3db18738ffc146887f3b47e44159d370509c3ee302f0456a8f0dfcac/interpax-0.3.14-py3-none-any.whl", hash = "sha256:070c5242227fa65bd7dafc472ae2ed0c4df9891fbbbede73a8b04e21859a5d47", size = 28104, upload-time = "2026-04-18T00:35:07.774Z" }, +] + +[[package]] +name = "jax" +version = "0.6.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "jaxlib", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "ml-dtypes", marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "opt-einsum", marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cf/1e/267f59c8fb7f143c3f778c76cb7ef1389db3fd7e4540f04b9f42ca90764d/jax-0.6.2.tar.gz", hash = "sha256:a437d29038cbc8300334119692744704ca7941490867b9665406b7f90665cd96", size = 2334091, upload-time = "2025-06-17T23:10:27.186Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/a8/97ef0cbb7a17143ace2643d600a7b80d6705b2266fc31078229e406bdef2/jax-0.6.2-py3-none-any.whl", hash = "sha256:bb24a82dc60ccf704dcaf6dbd07d04957f68a6c686db19630dd75260d1fb788c", size = 2722396, upload-time = "2025-06-17T23:10:25.293Z" }, +] + +[[package]] +name = "jax" +version = "0.10.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "jaxlib", version = "0.10.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "ml-dtypes", marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "opt-einsum", marker = "python_full_version >= '3.11'" }, + { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/f0/bcb81d28267d2054d0daed766c7fa16bcee5e481331b4d1e14f5fbe662be/jax-0.10.0.tar.gz", hash = "sha256:0119c767de1645f407df72345d28a3837dc904f1d698911c121d8f2b396fdece", size = 2663397, upload-time = "2026-04-22T13:22:28.563Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/70/aa/dfac6d72cc35bc07e7587115b6946e333ef4ccb2e6cd26ecf639438c5d26/jax-0.10.0-py3-none-any.whl", hash = "sha256:76c42ba163c8db3dc2e449e225b888c0edfb623ded31efdc96d85e0fda1d26e8", size = 3094950, upload-time = "2026-04-16T12:32:11.576Z" }, +] + +[[package]] +name = "jaxlib" +version = "0.6.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "ml-dtypes", marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/c5/41598634c99cbebba46e6777286fb76abc449d33d50aeae5d36128ca8803/jaxlib-0.6.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:da4601b2b5dc8c23d6afb293eacfb9aec4e1d1871cb2f29c5a151d103e73b0f8", size = 54298019, upload-time = "2025-06-17T23:10:36.916Z" }, + { url = "https://files.pythonhosted.org/packages/81/af/db07d746cd5867d5967528e7811da53374e94f64e80a890d6a5a4b95b130/jaxlib-0.6.2-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:4205d098ce8efb5f7fe2fe5098bae6036094dc8d8829f5e0e0d7a9b155326336", size = 79440052, upload-time = "2025-06-17T23:10:41.282Z" }, + { url = "https://files.pythonhosted.org/packages/7e/d8/b7ae9e819c62c1854dbc2c70540a5c041173fbc8bec5e78ab7fd615a4aee/jaxlib-0.6.2-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:c087a0eb6fb7f6f8f54d56f4730328dfde5040dd3b5ddfa810e7c28ea7102b42", size = 89917034, upload-time = "2025-06-17T23:10:45.897Z" }, + { url = "https://files.pythonhosted.org/packages/fd/e5/87e91bc70569ac5c3e3449eefcaf47986e892f10cfe1d5e5720dceae3068/jaxlib-0.6.2-cp310-cp310-win_amd64.whl", hash = "sha256:153eaa51f778b60851720729d4f461a91edd9ba3932f6f3bc598d4413870038b", size = 57896337, upload-time = "2025-06-17T23:10:50.179Z" }, + { url = "https://files.pythonhosted.org/packages/a4/ee/6899b0aed36a4acc51319465ddd83c7c300a062a9e236cceee00984ffe0b/jaxlib-0.6.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a208ff61c58128d306bb4e5ad0858bd2b0960f2c1c10ad42c548f74a60c0020e", size = 54300346, upload-time = "2025-06-17T23:10:54.591Z" }, + { url = "https://files.pythonhosted.org/packages/e6/03/34bb6b346609079a71942cfbf507892e3c877a06a430a0df8429c455cebc/jaxlib-0.6.2-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:11eae7e05bc5a79875da36324afb9eddd4baeaef2a0386caf6d4f3720b9aef28", size = 79438425, upload-time = "2025-06-17T23:10:58.356Z" }, + { url = "https://files.pythonhosted.org/packages/80/02/49b05cbab519ffd3cb79586336451fbbf8b6523f67128a794acc9f179000/jaxlib-0.6.2-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:335d7e3515ce78b52a410136f46aa4a7ea14d0e7d640f34e1e137409554ad0ac", size = 89920354, upload-time = "2025-06-17T23:11:03.086Z" }, + { url = "https://files.pythonhosted.org/packages/a7/7a/93b28d9452b46c15fc28dd65405672fc8a158b35d46beabaa0fe9631afb0/jaxlib-0.6.2-cp311-cp311-win_amd64.whl", hash = "sha256:c6815509997d6b05e5c9daa7994b9ad473ce3e8c8a17bdbbcacc3c744f76f7a0", size = 57895707, upload-time = "2025-06-17T23:11:07.074Z" }, + { url = "https://files.pythonhosted.org/packages/ac/db/05e702d2534e87abf606b1067b46a273b120e6adc7d459696e3ce7399317/jaxlib-0.6.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d8a684a8be949dd87dd4acc97101b4106a0dc9ad151ec891da072319a57b99", size = 54301644, upload-time = "2025-06-17T23:11:10.977Z" }, + { url = "https://files.pythonhosted.org/packages/0d/8a/b0a96887b97a25d45ae2c30e4acecd2f95acd074c18ec737dda8c5cc7016/jaxlib-0.6.2-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:87ec2dc9c3ed9ab936eec8535160c5fbd2c849948559f1c5daa75f63fabe5942", size = 79439161, upload-time = "2025-06-17T23:11:14.822Z" }, + { url = "https://files.pythonhosted.org/packages/ba/e8/71c2555431edb5dd115cf86a7b599aa7e1be26728d89ae59aa11251d299c/jaxlib-0.6.2-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:f1dd09b481a93c1d4c750013f467f74194493ba7bd29fcd4d1cec16e3a214f65", size = 89942952, upload-time = "2025-06-17T23:11:19.181Z" }, + { url = "https://files.pythonhosted.org/packages/de/3a/06849113c844b86d20174df54735c84202ccf82cbd36d805f478c834418b/jaxlib-0.6.2-cp312-cp312-win_amd64.whl", hash = "sha256:921dbd4db214eba19a29ba9f2450d880e08b2b2c7b968f28cc89da3e62366af4", size = 57919603, upload-time = "2025-06-17T23:11:23.207Z" }, + { url = "https://files.pythonhosted.org/packages/af/38/bed4279c2a3407820ed8bcd72dbad43c330ada35f88fafe9952b35abf785/jaxlib-0.6.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bff67b188133ce1f0111c7b163ac321fd646b59ed221ea489063e2e0f85cb967", size = 54300638, upload-time = "2025-06-17T23:11:26.372Z" }, + { url = "https://files.pythonhosted.org/packages/52/dc/9e35a1dc089ddf3d6be53ef2e6ba4718c5b6c0f90bccc535a20edac0c895/jaxlib-0.6.2-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:70498837caf538bd458ff6858c8bfd404db82015aba8f663670197fa9900ff02", size = 79439983, upload-time = "2025-06-17T23:11:30.016Z" }, + { url = "https://files.pythonhosted.org/packages/34/16/e93f0184b80a4e1ad38c6998aa3a2f7569c0b0152cbae39f7572393eda04/jaxlib-0.6.2-cp313-cp313-manylinux2014_x86_64.whl", hash = "sha256:f94163f14c8fd3ba93ae14b631abacf14cb031bba0b59138869984b4d10375f8", size = 89941720, upload-time = "2025-06-17T23:11:34.62Z" }, + { url = "https://files.pythonhosted.org/packages/06/b9/ea50792ee0333dba764e06c305fe098bce1cb938dcb66fbe2fc47ef5dd02/jaxlib-0.6.2-cp313-cp313-win_amd64.whl", hash = "sha256:b977604cd36c74b174d25ed685017379468138eb747d865f75e466cb273c801d", size = 57919073, upload-time = "2025-06-17T23:11:39.344Z" }, + { url = "https://files.pythonhosted.org/packages/09/ce/9596391c104a0547fcaf6a8c72078bbae79dbc8e7f0843dc8318f6606328/jaxlib-0.6.2-cp313-cp313t-manylinux2014_aarch64.whl", hash = "sha256:39cf9555f85ae1ce2e2c1a59fc71f2eca4f9867a7cb934fef881ba56b11371d1", size = 79579638, upload-time = "2025-06-17T23:11:43.054Z" }, + { url = "https://files.pythonhosted.org/packages/10/79/f6e80f7f4cacfc9f03e64ac57ecb856b140de7c2f939b25f8dcf1aff63f9/jaxlib-0.6.2-cp313-cp313t-manylinux2014_x86_64.whl", hash = "sha256:3abd536e44b05fb1657507e3ff1fc3691f99613bae3921ecab9e82f27255f784", size = 90066675, upload-time = "2025-06-17T23:11:47.454Z" }, +] + +[[package]] +name = "jaxlib" +version = "0.10.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "ml-dtypes", marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/5c/64a60f90d48bb6ab68ece63b7fa78855e8f8cefc4045f198a5c8695bfd99/jaxlib-0.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:277032e9f074c3fd5ffd1e0cb03d4fe66e272de472667cdbc418ad99b21b646a", size = 60115498, upload-time = "2026-04-16T12:33:15.93Z" }, + { url = "https://files.pythonhosted.org/packages/71/bc/b75d9e09bcf46e00fd9cdd6c457219a8fe2033d351c2d133917662e8cbaa/jaxlib-0.10.0-cp311-cp311-manylinux_2_27_aarch64.whl", hash = "sha256:3db94ebc859375d955de3504182add7ce1733ce3d30c15e0ef031602cb51a559", size = 79395106, upload-time = "2026-04-16T12:33:19.648Z" }, + { url = "https://files.pythonhosted.org/packages/64/13/a94b53b0acd3fccce0441e3811e86224e5b21ac122f2dea4be1ccdeb7dc0/jaxlib-0.10.0-cp311-cp311-manylinux_2_27_x86_64.whl", hash = "sha256:9be229993a41e5b2b84f234ecc19a5de02f35eddb1195cf027bd539e1601e15d", size = 85005588, upload-time = "2026-04-16T12:33:23.368Z" }, + { url = "https://files.pythonhosted.org/packages/d2/36/fbc303c0a41ac26daceeba0a9884d9206657e8eb1981f3f76da17f1ecc7f/jaxlib-0.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:421cdf3a4a5c2ee41471035e586954c8dc599d677ce9b11b063c3926a82a7850", size = 64195649, upload-time = "2026-04-16T12:33:26.972Z" }, + { url = "https://files.pythonhosted.org/packages/79/0c/279cb4dc009fe87a8315d1b182f520693236ad07b852152df344ea4e4021/jaxlib-0.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7c1d9b463327c7a2333f210114ecb04f28fefc51ba8233a85a2280cce75bdb42", size = 60137156, upload-time = "2026-04-16T12:33:30.306Z" }, + { url = "https://files.pythonhosted.org/packages/e3/cd/59ead5a90df739d1b8c1d1d00443558fd30adf5abb0319966ce340d49ff3/jaxlib-0.10.0-cp312-cp312-manylinux_2_27_aarch64.whl", hash = "sha256:aa1d70f1a4e27eb403654e71e2fb28d5786d3e9b77fc1847e8c5389880927ca4", size = 79398938, upload-time = "2026-04-16T12:33:34.14Z" }, + { url = "https://files.pythonhosted.org/packages/b5/20/9b07fc8b327b222b6f72a4978eb4f2ebe856ee71237d63c4d808ec3945e0/jaxlib-0.10.0-cp312-cp312-manylinux_2_27_x86_64.whl", hash = "sha256:b0bfb865a07df2e6d7418c0b0c292dd294b5500523b1dd5872b180db2aa480d4", size = 85028702, upload-time = "2026-04-16T12:33:37.815Z" }, + { url = "https://files.pythonhosted.org/packages/08/3b/4f798fffed4229a2d7de07c1f4feabac7676a26c695a418796dbe29bae7f/jaxlib-0.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:25bf167e0d8b594e0ec50783ff4892c0b7ec37236c88b2b425a7c252823f8680", size = 64221923, upload-time = "2026-04-16T12:33:41.343Z" }, + { url = "https://files.pythonhosted.org/packages/d4/b6/b66b0abb9df8f9f8f19a5244b849cb07fc7389a4a5e1fb7794f7cefd7f26/jaxlib-0.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:384635fff55899a295bbc82ee6c6f773a300e787dc472ca92bbe79abfaac8369", size = 60138213, upload-time = "2026-04-16T12:33:45.13Z" }, + { url = "https://files.pythonhosted.org/packages/30/1e/844e525a72a08a2744ae2722e2332a0159a6d0efdc1e561cf378f7259a01/jaxlib-0.10.0-cp313-cp313-manylinux_2_27_aarch64.whl", hash = "sha256:6d8d78b7070b34e4c5bba5f7e10927e7f4aac9b69be17e9b0a5898553a4338f3", size = 79401054, upload-time = "2026-04-16T12:33:49.263Z" }, + { url = "https://files.pythonhosted.org/packages/f7/95/305854c2ef2b645f7df1666be66b1167c392cc39384d09aca2e9499b71bf/jaxlib-0.10.0-cp313-cp313-manylinux_2_27_x86_64.whl", hash = "sha256:d303dc31b65e8b793d5600f81b1583be03dc9b876a4c10b3e259b6609a1cbe3b", size = 85027218, upload-time = "2026-04-16T12:33:54.325Z" }, + { url = "https://files.pythonhosted.org/packages/a8/63/a5e1dcb65dca6efbae7189f185588fc939e17c284f272254fbeb68a39817/jaxlib-0.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:3869be623c2f3391be2ee86f8b412372b102492e67cac0a5f0ab1037bbc3a5cc", size = 64221972, upload-time = "2026-04-16T12:46:24.762Z" }, + { url = "https://files.pythonhosted.org/packages/ce/d6/411e580b70f64a5a4b095cb2c03c1e2c7b3b35c6754e5cacd4a8f8a2d480/jaxlib-0.10.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9050ce2ae7eeca62b1a235065056cad62cac590ddc035486faa4472a47eed9f6", size = 60250897, upload-time = "2026-04-16T12:46:13.185Z" }, + { url = "https://files.pythonhosted.org/packages/2c/5c/f40ac9d40eb39c359f268e087ff1f21bdad664f86691c52a288d0f9152e8/jaxlib-0.10.0-cp313-cp313t-manylinux_2_27_aarch64.whl", hash = "sha256:59e07aab3bdfaad9bdd3cf32e0d3d4f228837b9b231c53f5ae1c0fc284481094", size = 79518774, upload-time = "2026-04-16T12:46:16.684Z" }, + { url = "https://files.pythonhosted.org/packages/49/36/dea7a0ea64a5551244e2140ef6ad36e2dff308b6f5facaa6f1c1272bb47f/jaxlib-0.10.0-cp313-cp313t-manylinux_2_27_x86_64.whl", hash = "sha256:3088503812cfe49f34a3083d3b7ef5cb3aaf33d89ceb1b3f647fa52713aee59d", size = 85134776, upload-time = "2026-04-16T12:46:20.855Z" }, + { url = "https://files.pythonhosted.org/packages/a7/25/e1e52a21786b321fb6a2edf9ef9971aa70f06bb2738aef9afd6d8f46a441/jaxlib-0.10.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:98b26672943672742873f65bc03216819fc55325c99f146590d007c0172bff30", size = 60141273, upload-time = "2026-04-16T12:46:27.922Z" }, + { url = "https://files.pythonhosted.org/packages/9c/3b/21e3382ce6f4ee84bcce52810f3786ae3663991ec863acadcd0765b6f767/jaxlib-0.10.0-cp314-cp314-manylinux_2_27_aarch64.whl", hash = "sha256:ad47e072430979ec21637aa487d4dc464028b8e9be27268f37de69536c76e341", size = 79416404, upload-time = "2026-04-16T12:46:31.326Z" }, + { url = "https://files.pythonhosted.org/packages/a1/8e/b2a08ffc51c93842de71f7f988865cebfa7f43d6721957812dc8cc8b9d40/jaxlib-0.10.0-cp314-cp314-manylinux_2_27_x86_64.whl", hash = "sha256:2a42cf04c0f88bc03b150a17fa7ddbb2f40e096667ec8a1b840ed87913e6e735", size = 85035152, upload-time = "2026-04-16T12:46:36.129Z" }, + { url = "https://files.pythonhosted.org/packages/24/08/26e6a3ecf0a95f1ec0dcd7a668d5c9a72e581c40fe4ae51e102ca63174c5/jaxlib-0.10.0-cp314-cp314-win_amd64.whl", hash = "sha256:450b771c01b3662c3497e2dceada3f6fc893112ae637ef85ef1dcc7dc68892a8", size = 66661443, upload-time = "2026-04-16T12:46:51.088Z" }, + { url = "https://files.pythonhosted.org/packages/37/d7/06383d19217824134c4a6119d2efe7b53cde6a0a66fb1d643d9f725d2697/jaxlib-0.10.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f62026c9fb1f05998592082a6dcb62f70b466342bc139f711802a9b184ba9a46", size = 60253088, upload-time = "2026-04-16T12:46:39.666Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ce/f66f955c01cce1ffda0cfbb1c02bb9234e0cac1d40b46fe17c315155d62f/jaxlib-0.10.0-cp314-cp314t-manylinux_2_27_aarch64.whl", hash = "sha256:e66bdc0b57ed5649950799d3f0d67a6bb67f03d06b49ea3fced0bdd6140a9943", size = 79517974, upload-time = "2026-04-16T12:46:43.147Z" }, + { url = "https://files.pythonhosted.org/packages/5e/74/b358923d0cce13fc7608051d0cc60ce3379f14350dc42540bdbabdbffab2/jaxlib-0.10.0-cp314-cp314t-manylinux_2_27_x86_64.whl", hash = "sha256:4dccd9065b30954879869641472d5d12fe4d7914175a5cad56293af8429ce7e0", size = 85134286, upload-time = "2026-04-16T12:46:47.416Z" }, +] + +[[package]] +name = "jaxtyping" +version = "0.3.7" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "wadler-lindig", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/40/a2ea3ce0e3e5f540eb970de7792c90fa58fef1b27d34c83f9fa94fea4729/jaxtyping-0.3.7.tar.gz", hash = "sha256:3bd7d9beb7d3cb01a89f93f90581c6f4fff3e5c5dc3c9307e8f8687a040d10c4", size = 45721, upload-time = "2026-01-30T14:18:47.409Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/42/caf65e9a0576a3abadc537e2f831701ba9081f21317fb3be87d64451587a/jaxtyping-0.3.7-py3-none-any.whl", hash = "sha256:303ab8599edf412eeb40bf06c863e3168fa186cf0e7334703fa741ddd7046e66", size = 56101, upload-time = "2026-01-30T14:18:45.954Z" }, +] + +[[package]] +name = "jaxtyping" +version = "0.3.9" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "wadler-lindig", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c2/be/00294e369938937e31b094437d5ea040e4fd1a20b998ebe572c4a1dcfa68/jaxtyping-0.3.9.tar.gz", hash = "sha256:f8c02d1b623d5f1b6665d4f3ddaec675d70004f16a792102c2fc51264190951d", size = 45857, upload-time = "2026-02-16T10:35:13.263Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/05/3e39d416fb92b2738a76e8265e6bfc5d10542f90a7c32ad1eb831eea3fa3/jaxtyping-0.3.9-py3-none-any.whl", hash = "sha256:a00557a9d616eff157491f06ed2e21ed94886fad3832399273eb912b345da378", size = 56274, upload-time = "2026-02-16T10:35:11.795Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "joblib" +version = "1.5.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/41/f2/d34e8b3a08a9cc79a50b2208a93dce981fe615b64d5a4d4abee421d898df/joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3", size = 331603, upload-time = "2025-12-15T08:41:46.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071, upload-time = "2025-12-15T08:41:44.973Z" }, +] + +[[package]] +name = "kiwisolver" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/67/9c61eccb13f0bdca9307614e782fec49ffdde0f7a2314935d489fa93cd9c/kiwisolver-1.5.0.tar.gz", hash = "sha256:d4193f3d9dc3f6f79aaed0e5637f45d98850ebf01f7ca20e69457f3e8946b66a", size = 103482, upload-time = "2026-03-09T13:15:53.382Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ac/f8/06549565caa026e540b7e7bab5c5a90eb7ca986015f4c48dace243cd24d9/kiwisolver-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:32cc0a5365239a6ea0c6ed461e8838d053b57e397443c0ca894dcc8e388d4374", size = 122802, upload-time = "2026-03-09T13:12:37.515Z" }, + { url = "https://files.pythonhosted.org/packages/84/eb/8476a0818850c563ff343ea7c9c05dcdcbd689a38e01aa31657df01f91fa/kiwisolver-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cc0b66c1eec9021353a4b4483afb12dfd50e3669ffbb9152d6842eb34c7e29fd", size = 66216, upload-time = "2026-03-09T13:12:38.812Z" }, + { url = "https://files.pythonhosted.org/packages/f3/c4/f9c8a6b4c21aed4198566e45923512986d6cef530e7263b3a5f823546561/kiwisolver-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86e0287879f75621ae85197b0877ed2f8b7aa57b511c7331dce2eb6f4de7d476", size = 63917, upload-time = "2026-03-09T13:12:40.053Z" }, + { url = "https://files.pythonhosted.org/packages/f1/0e/ba4ae25d03722f64de8b2c13e80d82ab537a06b30fc7065183c6439357e3/kiwisolver-1.5.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:62f59da443c4f4849f73a51a193b1d9d258dcad0c41bc4d1b8fb2bcc04bfeb22", size = 1628776, upload-time = "2026-03-09T13:12:41.976Z" }, + { url = "https://files.pythonhosted.org/packages/8a/e4/3f43a011bc8a0860d1c96f84d32fa87439d3feedf66e672fef03bf5e8bac/kiwisolver-1.5.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9190426b7aa26c5229501fa297b8d0653cfd3f5a36f7990c264e157cbf886b3b", size = 1228164, upload-time = "2026-03-09T13:12:44.002Z" }, + { url = "https://files.pythonhosted.org/packages/4b/34/3a901559a1e0c218404f9a61a93be82d45cb8f44453ba43088644980f033/kiwisolver-1.5.0-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c8277104ded0a51e699c8c3aff63ce2c56d4ed5519a5f73e0fd7057f959a2b9e", size = 1246656, upload-time = "2026-03-09T13:12:45.557Z" }, + { url = "https://files.pythonhosted.org/packages/87/9e/f78c466ea20527822b95ad38f141f2de1dcd7f23fb8716b002b0d91bbe59/kiwisolver-1.5.0-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8f9baf6f0a6e7571c45c8863010b45e837c3ee1c2c77fcd6ef423be91b21fedb", size = 1295562, upload-time = "2026-03-09T13:12:47.562Z" }, + { url = "https://files.pythonhosted.org/packages/0a/66/fd0e4a612e3a286c24e6d6f3a5428d11258ed1909bc530ba3b59807fd980/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cff8e5383db4989311f99e814feeb90c4723eb4edca425b9d5d9c3fefcdd9537", size = 2178473, upload-time = "2026-03-09T13:12:50.254Z" }, + { url = "https://files.pythonhosted.org/packages/dc/8e/6cac929e0049539e5ee25c1ee937556f379ba5204840d03008363ced662d/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ebae99ed6764f2b5771c522477b311be313e8841d2e0376db2b10922daebbba4", size = 2274035, upload-time = "2026-03-09T13:12:51.785Z" }, + { url = "https://files.pythonhosted.org/packages/ca/d3/9d0c18f1b52ea8074b792452cf17f1f5a56bd0302a85191f405cfbf9da16/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:d5cd5189fc2b6a538b75ae45433140c4823463918f7b1617c31e68b085c0022c", size = 2443217, upload-time = "2026-03-09T13:12:53.329Z" }, + { url = "https://files.pythonhosted.org/packages/45/2a/6e19368803a038b2a90857bf4ee9e3c7b667216d045866bf22d3439fd75e/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f42c23db5d1521218a3276bb08666dcb662896a0be7347cba864eca45ff64ede", size = 2249196, upload-time = "2026-03-09T13:12:55.057Z" }, + { url = "https://files.pythonhosted.org/packages/75/2b/3f641dfcbe72e222175d626bacf2f72c3b34312afec949dd1c50afa400f5/kiwisolver-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:94eff26096eb5395136634622515b234ecb6c9979824c1f5004c6e3c3c85ccd2", size = 73389, upload-time = "2026-03-09T13:12:56.496Z" }, + { url = "https://files.pythonhosted.org/packages/da/88/299b137b9e0025d8982e03d2d52c123b0a2b159e84b0ef1501ef446339cf/kiwisolver-1.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:dd952e03bfbb096cfe2dd35cd9e00f269969b67536cb4370994afc20ff2d0875", size = 64782, upload-time = "2026-03-09T13:12:57.609Z" }, + { url = "https://files.pythonhosted.org/packages/12/dd/a495a9c104be1c476f0386e714252caf2b7eca883915422a64c50b88c6f5/kiwisolver-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9eed0f7edbb274413b6ee781cca50541c8c0facd3d6fd289779e494340a2b85c", size = 122798, upload-time = "2026-03-09T13:12:58.963Z" }, + { url = "https://files.pythonhosted.org/packages/11/60/37b4047a2af0cf5ef6d8b4b26e91829ae6fc6a2d1f74524bcb0e7cd28a32/kiwisolver-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c4923e404d6bcd91b6779c009542e5647fef32e4a5d75e115e3bbac6f2335eb", size = 66216, upload-time = "2026-03-09T13:13:00.155Z" }, + { url = "https://files.pythonhosted.org/packages/0a/aa/510dc933d87767584abfe03efa445889996c70c2990f6f87c3ebaa0a18c5/kiwisolver-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0df54df7e686afa55e6f21fb86195224a6d9beb71d637e8d7920c95cf0f89aac", size = 63911, upload-time = "2026-03-09T13:13:01.671Z" }, + { url = "https://files.pythonhosted.org/packages/80/46/bddc13df6c2a40741e0cc7865bb1c9ed4796b6760bd04ce5fae3928ef917/kiwisolver-1.5.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2517e24d7315eb51c10664cdb865195df38ab74456c677df67bb47f12d088a27", size = 1438209, upload-time = "2026-03-09T13:13:03.385Z" }, + { url = "https://files.pythonhosted.org/packages/fd/d6/76621246f5165e5372f02f5e6f3f48ea336a8f9e96e43997d45b240ed8cd/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff710414307fefa903e0d9bdf300972f892c23477829f49504e59834f4195398", size = 1248888, upload-time = "2026-03-09T13:13:05.231Z" }, + { url = "https://files.pythonhosted.org/packages/b2/c1/31559ec6fb39a5b48035ce29bb63ade628f321785f38c384dee3e2c08bc1/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6176c1811d9d5a04fa391c490cc44f451e240697a16977f11c6f722efb9041db", size = 1266304, upload-time = "2026-03-09T13:13:06.743Z" }, + { url = "https://files.pythonhosted.org/packages/5e/ef/1cb8276f2d29cc6a41e0a042f27946ca347d3a4a75acf85d0a16aa6dcc82/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50847dca5d197fcbd389c805aa1a1cf32f25d2e7273dc47ab181a517666b68cc", size = 1319650, upload-time = "2026-03-09T13:13:08.607Z" }, + { url = "https://files.pythonhosted.org/packages/4c/e4/5ba3cecd7ce6236ae4a80f67e5d5531287337d0e1f076ca87a5abe4cd5d0/kiwisolver-1.5.0-cp311-cp311-manylinux_2_39_riscv64.whl", hash = "sha256:01808c6d15f4c3e8559595d6d1fe6411c68e4a3822b4b9972b44473b24f4e679", size = 970949, upload-time = "2026-03-09T13:13:10.299Z" }, + { url = "https://files.pythonhosted.org/packages/5a/69/dc61f7ae9a2f071f26004ced87f078235b5507ab6e5acd78f40365655034/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f1f9f4121ec58628c96baa3de1a55a4e3a333c5102c8e94b64e23bf7b2083309", size = 2199125, upload-time = "2026-03-09T13:13:11.841Z" }, + { url = "https://files.pythonhosted.org/packages/e5/7b/abbe0f1b5afa85f8d084b73e90e5f801c0939eba16ac2e49af7c61a6c28d/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b7d335370ae48a780c6e6a6bbfa97342f563744c39c35562f3f367665f5c1de2", size = 2293783, upload-time = "2026-03-09T13:13:14.399Z" }, + { url = "https://files.pythonhosted.org/packages/8a/80/5908ae149d96d81580d604c7f8aefd0e98f4fd728cf172f477e9f2a81744/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:800ee55980c18545af444d93fdd60c56b580db5cc54867d8cbf8a1dc0829938c", size = 1960726, upload-time = "2026-03-09T13:13:16.047Z" }, + { url = "https://files.pythonhosted.org/packages/84/08/a78cb776f8c085b7143142ce479859cfec086bd09ee638a317040b6ef420/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c438f6ca858697c9ab67eb28246c92508af972e114cac34e57a6d4ba17a3ac08", size = 2464738, upload-time = "2026-03-09T13:13:17.897Z" }, + { url = "https://files.pythonhosted.org/packages/b1/e1/65584da5356ed6cb12c63791a10b208860ac40a83de165cb6a6751a686e3/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8c63c91f95173f9c2a67c7c526b2cea976828a0e7fced9cdcead2802dc10f8a4", size = 2270718, upload-time = "2026-03-09T13:13:19.421Z" }, + { url = "https://files.pythonhosted.org/packages/be/6c/28f17390b62b8f2f520e2915095b3c94d88681ecf0041e75389d9667f202/kiwisolver-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:beb7f344487cdcb9e1efe4b7a29681b74d34c08f0043a327a74da852a6749e7b", size = 73480, upload-time = "2026-03-09T13:13:20.818Z" }, + { url = "https://files.pythonhosted.org/packages/d8/0e/2ee5debc4f77a625778fec5501ff3e8036fe361b7ee28ae402a485bb9694/kiwisolver-1.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:ad4ae4ffd1ee9cd11357b4c66b612da9888f4f4daf2f36995eda64bd45370cac", size = 64930, upload-time = "2026-03-09T13:13:21.997Z" }, + { url = "https://files.pythonhosted.org/packages/4d/b2/818b74ebea34dabe6d0c51cb1c572e046730e64844da6ed646d5298c40ce/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4e9750bc21b886308024f8a54ccb9a2cc38ac9fa813bf4348434e3d54f337ff9", size = 123158, upload-time = "2026-03-09T13:13:23.127Z" }, + { url = "https://files.pythonhosted.org/packages/bf/d9/405320f8077e8e1c5c4bd6adc45e1e6edf6d727b6da7f2e2533cf58bff71/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:72ec46b7eba5b395e0a7b63025490d3214c11013f4aacb4f5e8d6c3041829588", size = 66388, upload-time = "2026-03-09T13:13:24.765Z" }, + { url = "https://files.pythonhosted.org/packages/99/9f/795fedf35634f746151ca8839d05681ceb6287fbed6cc1c9bf235f7887c2/kiwisolver-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ed3a984b31da7481b103f68776f7128a89ef26ed40f4dc41a2223cda7fb24819", size = 64068, upload-time = "2026-03-09T13:13:25.878Z" }, + { url = "https://files.pythonhosted.org/packages/c4/13/680c54afe3e65767bed7ec1a15571e1a2f1257128733851ade24abcefbcc/kiwisolver-1.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb5136fb5352d3f422df33f0c879a1b0c204004324150cc3b5e3c4f310c9049f", size = 1477934, upload-time = "2026-03-09T13:13:27.166Z" }, + { url = "https://files.pythonhosted.org/packages/c8/2f/cebfcdb60fd6a9b0f6b47a9337198bcbad6fbe15e68189b7011fd914911f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2af221f268f5af85e776a73d62b0845fc8baf8ef0abfae79d29c77d0e776aaf", size = 1278537, upload-time = "2026-03-09T13:13:28.707Z" }, + { url = "https://files.pythonhosted.org/packages/f2/0d/9b782923aada3fafb1d6b84e13121954515c669b18af0c26e7d21f579855/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b0f172dc8ffaccb8522d7c5d899de00133f2f1ca7b0a49b7da98e901de87bf2d", size = 1296685, upload-time = "2026-03-09T13:13:30.528Z" }, + { url = "https://files.pythonhosted.org/packages/27/70/83241b6634b04fe44e892688d5208332bde130f38e610c0418f9ede47ded/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6ab8ba9152203feec73758dad83af9a0bbe05001eb4639e547207c40cfb52083", size = 1346024, upload-time = "2026-03-09T13:13:32.818Z" }, + { url = "https://files.pythonhosted.org/packages/e4/db/30ed226fb271ae1a6431fc0fe0edffb2efe23cadb01e798caeb9f2ceae8f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:cdee07c4d7f6d72008d3f73b9bf027f4e11550224c7c50d8df1ae4a37c1402a6", size = 987241, upload-time = "2026-03-09T13:13:34.435Z" }, + { url = "https://files.pythonhosted.org/packages/ec/bd/c314595208e4c9587652d50959ead9e461995389664e490f4dce7ff0f782/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7c60d3c9b06fb23bd9c6139281ccbdc384297579ae037f08ae90c69f6845c0b1", size = 2227742, upload-time = "2026-03-09T13:13:36.4Z" }, + { url = "https://files.pythonhosted.org/packages/c1/43/0499cec932d935229b5543d073c2b87c9c22846aab48881e9d8d6e742a2d/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e315e5ec90d88e140f57696ff85b484ff68bb311e36f2c414aa4286293e6dee0", size = 2323966, upload-time = "2026-03-09T13:13:38.204Z" }, + { url = "https://files.pythonhosted.org/packages/3d/6f/79b0d760907965acfd9d61826a3d41f8f093c538f55cd2633d3f0db269f6/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:1465387ac63576c3e125e5337a6892b9e99e0627d52317f3ca79e6930d889d15", size = 1977417, upload-time = "2026-03-09T13:13:39.966Z" }, + { url = "https://files.pythonhosted.org/packages/ab/31/01d0537c41cb75a551a438c3c7a80d0c60d60b81f694dac83dd436aec0d0/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:530a3fd64c87cffa844d4b6b9768774763d9caa299e9b75d8eca6a4423b31314", size = 2491238, upload-time = "2026-03-09T13:13:41.698Z" }, + { url = "https://files.pythonhosted.org/packages/e4/34/8aefdd0be9cfd00a44509251ba864f5caf2991e36772e61c408007e7f417/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1d9daea4ea6b9be74fe2f01f7fbade8d6ffab263e781274cffca0dba9be9eec9", size = 2294947, upload-time = "2026-03-09T13:13:43.343Z" }, + { url = "https://files.pythonhosted.org/packages/ad/cf/0348374369ca588f8fe9c338fae49fa4e16eeb10ffb3d012f23a54578a9e/kiwisolver-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:f18c2d9782259a6dc132fdc7a63c168cbc74b35284b6d75c673958982a378384", size = 73569, upload-time = "2026-03-09T13:13:45.792Z" }, + { url = "https://files.pythonhosted.org/packages/28/26/192b26196e2316e2bd29deef67e37cdf9870d9af8e085e521afff0fed526/kiwisolver-1.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:f7c7553b13f69c1b29a5bde08ddc6d9d0c8bfb84f9ed01c30db25944aeb852a7", size = 64997, upload-time = "2026-03-09T13:13:46.878Z" }, + { url = "https://files.pythonhosted.org/packages/9d/69/024d6711d5ba575aa65d5538042e99964104e97fa153a9f10bc369182bc2/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:fd40bb9cd0891c4c3cb1ddf83f8bbfa15731a248fdc8162669405451e2724b09", size = 123166, upload-time = "2026-03-09T13:13:48.032Z" }, + { url = "https://files.pythonhosted.org/packages/ce/48/adbb40df306f587054a348831220812b9b1d787aff714cfbc8556e38fccd/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c0e1403fd7c26d77c1f03e096dc58a5c726503fa0db0456678b8668f76f521e3", size = 66395, upload-time = "2026-03-09T13:13:49.365Z" }, + { url = "https://files.pythonhosted.org/packages/a8/3a/d0a972b34e1c63e2409413104216cd1caa02c5a37cb668d1687d466c1c45/kiwisolver-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dda366d548e89a90d88a86c692377d18d8bd64b39c1fb2b92cb31370e2896bbd", size = 64065, upload-time = "2026-03-09T13:13:50.562Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0a/7b98e1e119878a27ba8618ca1e18b14f992ff1eda40f47bccccf4de44121/kiwisolver-1.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:332b4f0145c30b5f5ad9374881133e5aa64320428a57c2c2b61e9d891a51c2f3", size = 1477903, upload-time = "2026-03-09T13:13:52.084Z" }, + { url = "https://files.pythonhosted.org/packages/18/d8/55638d89ffd27799d5cc3d8aa28e12f4ce7a64d67b285114dbedc8ea4136/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c50b89ffd3e1a911c69a1dd3de7173c0cd10b130f56222e57898683841e4f96", size = 1278751, upload-time = "2026-03-09T13:13:54.673Z" }, + { url = "https://files.pythonhosted.org/packages/b8/97/b4c8d0d18421ecceba20ad8701358453b88e32414e6f6950b5a4bad54e65/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4db576bb8c3ef9365f8b40fe0f671644de6736ae2c27a2c62d7d8a1b4329f099", size = 1296793, upload-time = "2026-03-09T13:13:56.287Z" }, + { url = "https://files.pythonhosted.org/packages/c4/10/f862f94b6389d8957448ec9df59450b81bec4abb318805375c401a1e6892/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0b85aad90cea8ac6797a53b5d5f2e967334fa4d1149f031c4537569972596cb8", size = 1346041, upload-time = "2026-03-09T13:13:58.269Z" }, + { url = "https://files.pythonhosted.org/packages/a3/6a/f1650af35821eaf09de398ec0bc2aefc8f211f0cda50204c9f1673741ba9/kiwisolver-1.5.0-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:d36ca54cb4c6c4686f7cbb7b817f66f5911c12ddb519450bbe86707155028f87", size = 987292, upload-time = "2026-03-09T13:13:59.871Z" }, + { url = "https://files.pythonhosted.org/packages/de/19/d7fb82984b9238115fe629c915007be608ebd23dc8629703d917dbfaffd4/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:38f4a703656f493b0ad185211ccfca7f0386120f022066b018eb5296d8613e23", size = 2227865, upload-time = "2026-03-09T13:14:01.401Z" }, + { url = "https://files.pythonhosted.org/packages/7f/b9/46b7f386589fd222dac9e9de9c956ce5bcefe2ee73b4e79891381dda8654/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3ac2360e93cb41be81121755c6462cff3beaa9967188c866e5fce5cf13170859", size = 2324369, upload-time = "2026-03-09T13:14:02.972Z" }, + { url = "https://files.pythonhosted.org/packages/92/8b/95e237cf3d9c642960153c769ddcbe278f182c8affb20cecc1cc983e7cc5/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c95cab08d1965db3d84a121f1c7ce7479bdd4072c9b3dafd8fecce48a2e6b902", size = 1977989, upload-time = "2026-03-09T13:14:04.503Z" }, + { url = "https://files.pythonhosted.org/packages/1b/95/980c9df53501892784997820136c01f62bc1865e31b82b9560f980c0e649/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fc20894c3d21194d8041a28b65622d5b86db786da6e3cfe73f0c762951a61167", size = 2491645, upload-time = "2026-03-09T13:14:06.106Z" }, + { url = "https://files.pythonhosted.org/packages/cb/32/900647fd0840abebe1561792c6b31e6a7c0e278fc3973d30572a965ca14c/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7a32f72973f0f950c1920475d5c5ea3d971b81b6f0ec53b8d0a956cc965f22e0", size = 2295237, upload-time = "2026-03-09T13:14:08.891Z" }, + { url = "https://files.pythonhosted.org/packages/be/8a/be60e3bbcf513cc5a50f4a3e88e1dcecebb79c1ad607a7222877becaa101/kiwisolver-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bf3acf1419fa93064a4c2189ac0b58e3be7872bf6ee6177b0d4c63dc4cea276", size = 73573, upload-time = "2026-03-09T13:14:12.327Z" }, + { url = "https://files.pythonhosted.org/packages/4d/d2/64be2e429eb4fca7f7e1c52a91b12663aeaf25de3895e5cca0f47ef2a8d0/kiwisolver-1.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:fa8eb9ecdb7efb0b226acec134e0d709e87a909fa4971a54c0c4f6e88635484c", size = 64998, upload-time = "2026-03-09T13:14:13.469Z" }, + { url = "https://files.pythonhosted.org/packages/b0/69/ce68dd0c85755ae2de490bf015b62f2cea5f6b14ff00a463f9d0774449ff/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:db485b3847d182b908b483b2ed133c66d88d49cacf98fd278fadafe11b4478d1", size = 125700, upload-time = "2026-03-09T13:14:14.636Z" }, + { url = "https://files.pythonhosted.org/packages/74/aa/937aac021cf9d4349990d47eb319309a51355ed1dbdc9c077cdc9224cb11/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:be12f931839a3bdfe28b584db0e640a65a8bcbc24560ae3fdb025a449b3d754e", size = 67537, upload-time = "2026-03-09T13:14:15.808Z" }, + { url = "https://files.pythonhosted.org/packages/ee/20/3a87fbece2c40ad0f6f0aefa93542559159c5f99831d596050e8afae7a9f/kiwisolver-1.5.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:16b85d37c2cbb3253226d26e64663f755d88a03439a9c47df6246b35defbdfb7", size = 65514, upload-time = "2026-03-09T13:14:18.035Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7f/f943879cda9007c45e1f7dba216d705c3a18d6b35830e488b6c6a4e7cdf0/kiwisolver-1.5.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4432b835675f0ea7414aab3d37d119f7226d24869b7a829caeab49ebda407b0c", size = 1584848, upload-time = "2026-03-09T13:14:19.745Z" }, + { url = "https://files.pythonhosted.org/packages/37/f8/4d4f85cc1870c127c88d950913370dd76138482161cd07eabbc450deff01/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b0feb50971481a2cc44d94e88bdb02cdd497618252ae226b8eb1201b957e368", size = 1391542, upload-time = "2026-03-09T13:14:21.54Z" }, + { url = "https://files.pythonhosted.org/packages/04/0b/65dd2916c84d252b244bd405303220f729e7c17c9d7d33dca6feeff9ffc4/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56fa888f10d0f367155e76ce849fa1166fc9730d13bd2d65a2aa13b6f5424489", size = 1404447, upload-time = "2026-03-09T13:14:23.205Z" }, + { url = "https://files.pythonhosted.org/packages/39/5c/2606a373247babce9b1d056c03a04b65f3cf5290a8eac5d7bdead0a17e21/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:940dda65d5e764406b9fb92761cbf462e4e63f712ab60ed98f70552e496f3bf1", size = 1455918, upload-time = "2026-03-09T13:14:24.74Z" }, + { url = "https://files.pythonhosted.org/packages/d5/d1/c6078b5756670658e9192a2ef11e939c92918833d2745f85cd14a6004bdf/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_39_riscv64.whl", hash = "sha256:89fc958c702ee9a745e4700378f5d23fddbc46ff89e8fdbf5395c24d5c1452a3", size = 1072856, upload-time = "2026-03-09T13:14:26.597Z" }, + { url = "https://files.pythonhosted.org/packages/cb/c8/7def6ddf16eb2b3741d8b172bdaa9af882b03c78e9b0772975408801fa63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9027d773c4ff81487181a925945743413f6069634d0b122d0b37684ccf4f1e18", size = 2333580, upload-time = "2026-03-09T13:14:28.237Z" }, + { url = "https://files.pythonhosted.org/packages/9e/87/2ac1fce0eb1e616fcd3c35caa23e665e9b1948bb984f4764790924594128/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:5b233ea3e165e43e35dba1d2b8ecc21cf070b45b65ae17dd2747d2713d942021", size = 2423018, upload-time = "2026-03-09T13:14:30.018Z" }, + { url = "https://files.pythonhosted.org/packages/67/13/c6700ccc6cc218716bfcda4935e4b2997039869b4ad8a94f364c5a3b8e63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ce9bf03dad3b46408c08649c6fbd6ca28a9fce0eb32fdfffa6775a13103b5310", size = 2062804, upload-time = "2026-03-09T13:14:32.888Z" }, + { url = "https://files.pythonhosted.org/packages/1b/bd/877056304626943ff0f1f44c08f584300c199b887cb3176cd7e34f1515f1/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:fc4d3f1fb9ca0ae9f97b095963bc6326f1dbfd3779d6679a1e016b9baaa153d3", size = 2597482, upload-time = "2026-03-09T13:14:34.971Z" }, + { url = "https://files.pythonhosted.org/packages/75/19/c60626c47bf0f8ac5dcf72c6c98e266d714f2fbbfd50cf6dab5ede3aaa50/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f443b4825c50a51ee68585522ab4a1d1257fac65896f282b4c6763337ac9f5d2", size = 2394328, upload-time = "2026-03-09T13:14:36.816Z" }, + { url = "https://files.pythonhosted.org/packages/47/84/6a6d5e5bb8273756c27b7d810d47f7ef2f1f9b9fd23c9ee9a3f8c75c9cef/kiwisolver-1.5.0-cp313-cp313t-win_arm64.whl", hash = "sha256:893ff3a711d1b515ba9da14ee090519bad4610ed1962fbe298a434e8c5f8db53", size = 68410, upload-time = "2026-03-09T13:14:38.695Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d7/060f45052f2a01ad5762c8fdecd6d7a752b43400dc29ff75cd47225a40fd/kiwisolver-1.5.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8df31fe574b8b3993cc61764f40941111b25c2d9fea13d3ce24a49907cd2d615", size = 123231, upload-time = "2026-03-09T13:14:41.323Z" }, + { url = "https://files.pythonhosted.org/packages/c2/a7/78da680eadd06ff35edef6ef68a1ad273bad3e2a0936c9a885103230aece/kiwisolver-1.5.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1d49a49ac4cbfb7c1375301cd1ec90169dfeae55ff84710d782260ce77a75a02", size = 66489, upload-time = "2026-03-09T13:14:42.534Z" }, + { url = "https://files.pythonhosted.org/packages/49/b2/97980f3ad4fae37dd7fe31626e2bf75fbf8bdf5d303950ec1fab39a12da8/kiwisolver-1.5.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0cbe94b69b819209a62cb27bdfa5dc2a8977d8de2f89dfd97ba4f53ed3af754e", size = 64063, upload-time = "2026-03-09T13:14:44.759Z" }, + { url = "https://files.pythonhosted.org/packages/e7/f9/b06c934a6aa8bc91f566bd2a214fd04c30506c2d9e2b6b171953216a65b6/kiwisolver-1.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:80aa065ffd378ff784822a6d7c3212f2d5f5e9c3589614b5c228b311fd3063ac", size = 1475913, upload-time = "2026-03-09T13:14:46.247Z" }, + { url = "https://files.pythonhosted.org/packages/6b/f0/f768ae564a710135630672981231320bc403cf9152b5596ec5289de0f106/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e7f886f47ab881692f278ae901039a234e4025a68e6dfab514263a0b1c4ae05", size = 1282782, upload-time = "2026-03-09T13:14:48.458Z" }, + { url = "https://files.pythonhosted.org/packages/e2/9f/1de7aad00697325f05238a5f2eafbd487fb637cc27a558b5367a5f37fb7f/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5060731cc3ed12ca3a8b57acd4aeca5bbc2f49216dd0bec1650a1acd89486bcd", size = 1300815, upload-time = "2026-03-09T13:14:50.721Z" }, + { url = "https://files.pythonhosted.org/packages/5a/c2/297f25141d2e468e0ce7f7a7b92e0cf8918143a0cbd3422c1ad627e85a06/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7a4aa69609f40fce3cbc3f87b2061f042eee32f94b8f11db707b66a26461591a", size = 1347925, upload-time = "2026-03-09T13:14:52.304Z" }, + { url = "https://files.pythonhosted.org/packages/b9/d3/f4c73a02eb41520c47610207b21afa8cdd18fdbf64ffd94674ae21c4812d/kiwisolver-1.5.0-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:d168fda2dbff7b9b5f38e693182d792a938c31db4dac3a80a4888de603c99554", size = 991322, upload-time = "2026-03-09T13:14:54.637Z" }, + { url = "https://files.pythonhosted.org/packages/7b/46/d3f2efef7732fcda98d22bf4ad5d3d71d545167a852ca710a494f4c15343/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:413b820229730d358efd838ecbab79902fe97094565fdc80ddb6b0a18c18a581", size = 2232857, upload-time = "2026-03-09T13:14:56.471Z" }, + { url = "https://files.pythonhosted.org/packages/3f/ec/2d9756bf2b6d26ae4349b8d3662fb3993f16d80c1f971c179ce862b9dbae/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5124d1ea754509b09e53738ec185584cc609aae4a3b510aaf4ed6aa047ef9303", size = 2329376, upload-time = "2026-03-09T13:14:58.072Z" }, + { url = "https://files.pythonhosted.org/packages/8f/9f/876a0a0f2260f1bde92e002b3019a5fabc35e0939c7d945e0fa66185eb20/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e4415a8db000bf49a6dd1c478bf70062eaacff0f462b92b0ba68791a905861f9", size = 1982549, upload-time = "2026-03-09T13:14:59.668Z" }, + { url = "https://files.pythonhosted.org/packages/6c/4f/ba3624dfac23a64d54ac4179832860cb537c1b0af06024936e82ca4154a0/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d618fd27420381a4f6044faa71f46d8bfd911bd077c555f7138ed88729bfbe79", size = 2494680, upload-time = "2026-03-09T13:15:01.364Z" }, + { url = "https://files.pythonhosted.org/packages/39/b7/97716b190ab98911b20d10bf92eca469121ec483b8ce0edd314f51bc85af/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5092eb5b1172947f57d6ea7d89b2f29650414e4293c47707eb499ec07a0ac796", size = 2297905, upload-time = "2026-03-09T13:15:03.925Z" }, + { url = "https://files.pythonhosted.org/packages/a3/36/4e551e8aa55c9188bca9abb5096805edbf7431072b76e2298e34fd3a3008/kiwisolver-1.5.0-cp314-cp314-win_amd64.whl", hash = "sha256:d76e2d8c75051d58177e762164d2e9ab92886534e3a12e795f103524f221dd8e", size = 75086, upload-time = "2026-03-09T13:15:07.775Z" }, + { url = "https://files.pythonhosted.org/packages/70/15/9b90f7df0e31a003c71649cf66ef61c3c1b862f48c81007fa2383c8bd8d7/kiwisolver-1.5.0-cp314-cp314-win_arm64.whl", hash = "sha256:fa6248cd194edff41d7ea9425ced8ca3a6f838bfb295f6f1d6e6bb694a8518df", size = 66577, upload-time = "2026-03-09T13:15:09.139Z" }, + { url = "https://files.pythonhosted.org/packages/17/01/7dc8c5443ff42b38e72731643ed7cf1ed9bf01691ae5cdca98501999ed83/kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:d1ffeb80b5676463d7a7d56acbe8e37a20ce725570e09549fe738e02ca6b7e1e", size = 125794, upload-time = "2026-03-09T13:15:10.525Z" }, + { url = "https://files.pythonhosted.org/packages/46/8a/b4ebe46ebaac6a303417fab10c2e165c557ddaff558f9699d302b256bc53/kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:bc4d8e252f532ab46a1de9349e2d27b91fce46736a9eedaa37beaca66f574ed4", size = 67646, upload-time = "2026-03-09T13:15:12.016Z" }, + { url = "https://files.pythonhosted.org/packages/60/35/10a844afc5f19d6f567359bf4789e26661755a2f36200d5d1ed8ad0126e5/kiwisolver-1.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6783e069732715ad0c3ce96dbf21dbc2235ab0593f2baf6338101f70371f4028", size = 65511, upload-time = "2026-03-09T13:15:13.311Z" }, + { url = "https://files.pythonhosted.org/packages/f8/8a/685b297052dd041dcebce8e8787b58923b6e78acc6115a0dc9189011c44b/kiwisolver-1.5.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e7c4c09a490dc4d4a7f8cbee56c606a320f9dc28cf92a7157a39d1ce7676a657", size = 1584858, upload-time = "2026-03-09T13:15:15.103Z" }, + { url = "https://files.pythonhosted.org/packages/9e/80/04865e3d4638ac5bddec28908916df4a3075b8c6cc101786a96803188b96/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2a075bd7bd19c70cf67c8badfa36cf7c5d8de3c9ddb8420c51e10d9c50e94920", size = 1392539, upload-time = "2026-03-09T13:15:16.661Z" }, + { url = "https://files.pythonhosted.org/packages/ba/01/77a19cacc0893fa13fafa46d1bba06fb4dc2360b3292baf4b56d8e067b24/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bdd3e53429ff02aa319ba59dfe4ceeec345bf46cf180ec2cf6fd5b942e7975e9", size = 1405310, upload-time = "2026-03-09T13:15:18.229Z" }, + { url = "https://files.pythonhosted.org/packages/53/39/bcaf5d0cca50e604cfa9b4e3ae1d64b50ca1ae5b754122396084599ef903/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cdcb35dc9d807259c981a85531048ede628eabcffb3239adf3d17463518992d", size = 1456244, upload-time = "2026-03-09T13:15:20.444Z" }, + { url = "https://files.pythonhosted.org/packages/d0/7a/72c187abc6975f6978c3e39b7cf67aeb8b3c0a8f9790aa7fd412855e9e1f/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:70d593af6a6ca332d1df73d519fddb5148edb15cd90d5f0155e3746a6d4fcc65", size = 1073154, upload-time = "2026-03-09T13:15:22.039Z" }, + { url = "https://files.pythonhosted.org/packages/c7/ca/cf5b25783ebbd59143b4371ed0c8428a278abe68d6d0104b01865b1bbd0f/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:377815a8616074cabbf3f53354e1d040c35815a134e01d7614b7692e4bf8acfa", size = 2334377, upload-time = "2026-03-09T13:15:23.741Z" }, + { url = "https://files.pythonhosted.org/packages/4a/e5/b1f492adc516796e88751282276745340e2a72dcd0d36cf7173e0daf3210/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0255a027391d52944eae1dbb5d4cc5903f57092f3674e8e544cdd2622826b3f0", size = 2425288, upload-time = "2026-03-09T13:15:25.789Z" }, + { url = "https://files.pythonhosted.org/packages/e6/e5/9b21fbe91a61b8f409d74a26498706e97a48008bfcd1864373d32a6ba31c/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:012b1eb16e28718fa782b5e61dc6f2da1f0792ca73bd05d54de6cb9561665fc9", size = 2063158, upload-time = "2026-03-09T13:15:27.63Z" }, + { url = "https://files.pythonhosted.org/packages/b1/02/83f47986138310f95ea95531f851b2a62227c11cbc3e690ae1374fe49f0f/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0e3aafb33aed7479377e5e9a82e9d4bf87063741fc99fc7ae48b0f16e32bdd6f", size = 2597260, upload-time = "2026-03-09T13:15:29.421Z" }, + { url = "https://files.pythonhosted.org/packages/07/18/43a5f24608d8c313dd189cf838c8e68d75b115567c6279de7796197cfb6a/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e7a116ae737f0000343218c4edf5bd45893bfeaff0993c0b215d7124c9f77646", size = 2394403, upload-time = "2026-03-09T13:15:31.517Z" }, + { url = "https://files.pythonhosted.org/packages/3b/b5/98222136d839b8afabcaa943b09bd05888c2d36355b7e448550211d1fca4/kiwisolver-1.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1dd9b0b119a350976a6d781e7278ec7aca0b201e1a9e2d23d9804afecb6ca681", size = 79687, upload-time = "2026-03-09T13:15:33.204Z" }, + { url = "https://files.pythonhosted.org/packages/99/a2/ca7dc962848040befed12732dff6acae7fb3c4f6fc4272b3f6c9a30b8713/kiwisolver-1.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:58f812017cd2985c21fbffb4864d59174d4903dd66fa23815e74bbc7a0e2dd57", size = 70032, upload-time = "2026-03-09T13:15:34.411Z" }, + { url = "https://files.pythonhosted.org/packages/1c/fa/2910df836372d8761bb6eff7d8bdcb1613b5c2e03f260efe7abe34d388a7/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_10_13_x86_64.whl", hash = "sha256:5ae8e62c147495b01a0f4765c878e9bfdf843412446a247e28df59936e99e797", size = 130262, upload-time = "2026-03-09T13:15:35.629Z" }, + { url = "https://files.pythonhosted.org/packages/0f/41/c5f71f9f00aabcc71fee8b7475e3f64747282580c2fe748961ba29b18385/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:f6764a4ccab3078db14a632420930f6186058750df066b8ea2a7106df91d3203", size = 138036, upload-time = "2026-03-09T13:15:36.894Z" }, + { url = "https://files.pythonhosted.org/packages/fa/06/7399a607f434119c6e1fdc8ec89a8d51ccccadf3341dee4ead6bd14caaf5/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c31c13da98624f957b0fb1b5bae5383b2333c2c3f6793d9825dd5ce79b525cb7", size = 194295, upload-time = "2026-03-09T13:15:38.22Z" }, + { url = "https://files.pythonhosted.org/packages/b5/91/53255615acd2a1eaca307ede3c90eb550bae9c94581f8c00081b6b1c8f44/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-win_amd64.whl", hash = "sha256:1f1489f769582498610e015a8ef2d36f28f505ab3096d0e16b4858a9ec214f57", size = 75987, upload-time = "2026-03-09T13:15:39.65Z" }, + { url = "https://files.pythonhosted.org/packages/17/6f/6fd4f690a40c2582fa34b97d2678f718acf3706b91d270c65ecb455d0a06/kiwisolver-1.5.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:295d9ffe712caa9f8a3081de8d32fc60191b4b51c76f02f951fd8407253528f4", size = 59606, upload-time = "2026-03-09T13:15:40.81Z" }, + { url = "https://files.pythonhosted.org/packages/82/a0/2355d5e3b338f13ce63f361abb181e3b6ea5fffdb73f739b3e80efa76159/kiwisolver-1.5.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:51e8c4084897de9f05898c2c2a39af6318044ae969d46ff7a34ed3f96274adca", size = 57537, upload-time = "2026-03-09T13:15:42.071Z" }, + { url = "https://files.pythonhosted.org/packages/c8/b9/1d50e610ecadebe205b71d6728fd224ce0e0ca6aba7b9cbe1da049203ac5/kiwisolver-1.5.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b83af57bdddef03c01a9138034c6ff03181a3028d9a1003b301eb1a55e161a3f", size = 79888, upload-time = "2026-03-09T13:15:43.317Z" }, + { url = "https://files.pythonhosted.org/packages/cd/ee/b85ffcd75afed0357d74f0e6fc02a4507da441165de1ca4760b9f496390d/kiwisolver-1.5.0-pp310-pypy310_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf4679a3d71012a7c2bf360e5cd878fbd5e4fcac0896b56393dec239d81529ed", size = 77584, upload-time = "2026-03-09T13:15:44.605Z" }, + { url = "https://files.pythonhosted.org/packages/6b/dd/644d0dde6010a8583b4cd66dd41c5f83f5325464d15c4f490b3340ab73b4/kiwisolver-1.5.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:41024ed50e44ab1a60d3fe0a9d15a4ccc9f5f2b1d814ff283c8d01134d5b81bc", size = 73390, upload-time = "2026-03-09T13:15:45.832Z" }, + { url = "https://files.pythonhosted.org/packages/e9/eb/5fcbbbf9a0e2c3a35effb88831a483345326bbc3a030a3b5b69aee647f84/kiwisolver-1.5.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ec4c85dc4b687c7f7f15f553ff26a98bfe8c58f5f7f0ac8905f0ba4c7be60232", size = 59532, upload-time = "2026-03-09T13:15:47.047Z" }, + { url = "https://files.pythonhosted.org/packages/c3/9b/e17104555bb4db148fd52327feea1e96be4b88e8e008b029002c281a21ab/kiwisolver-1.5.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:12e91c215a96e39f57989c8912ae761286ac5a9584d04030ceb3368a357f017a", size = 57420, upload-time = "2026-03-09T13:15:48.199Z" }, + { url = "https://files.pythonhosted.org/packages/48/44/2b5b95b7aa39fb2d8d9d956e0f3d5d45aef2ae1d942d4c3ffac2f9cfed1a/kiwisolver-1.5.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be4a51a55833dc29ab5d7503e7bcb3b3af3402d266018137127450005cdfe737", size = 79892, upload-time = "2026-03-09T13:15:49.694Z" }, + { url = "https://files.pythonhosted.org/packages/52/7d/7157f9bba6b455cfb4632ed411e199fc8b8977642c2b12082e1bd9e6d173/kiwisolver-1.5.0-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:daae526907e262de627d8f70058a0f64acc9e2641c164c99c8f594b34a799a16", size = 77603, upload-time = "2026-03-09T13:15:50.945Z" }, + { url = "https://files.pythonhosted.org/packages/0a/dd/8050c947d435c8d4bc94e3252f4d8bb8a76cfb424f043a8680be637a57f1/kiwisolver-1.5.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:59cd8683f575d96df5bb48f6add94afc055012c29e28124fcae2b63661b9efb1", size = 73558, upload-time = "2026-03-09T13:15:52.112Z" }, +] + +[[package]] +name = "lalsuite" +version = "7.26.11" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "astropy", version = "6.1.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "astropy", version = "7.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "igwn-ligolw" }, + { name = "igwn-segments" }, + { name = "lscsoft-glue" }, + { name = "matplotlib" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "python-dateutil" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/f1/c745b5bc297c27e15dcf5c6e4e93de34fcdfbf62ff7cdd279c72c3c537d3/lalsuite-7.26.11-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:e09d6c7bccf98d230702f53b1c75345c0f2bd8c3bca0d52f3ee3f8e4760340bc", size = 38241394, upload-time = "2026-04-27T08:25:51.558Z" }, + { url = "https://files.pythonhosted.org/packages/9d/6d/863e4477633a5f8d5cfc37a920bed776a3a499ea7437f43265be674d9ba9/lalsuite-7.26.11-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:e93b95b201337dcc50d2549ed8161edcab3eed93908b06d96e5fa755d64c7df2", size = 39559904, upload-time = "2026-04-27T08:25:55.102Z" }, + { url = "https://files.pythonhosted.org/packages/72/9c/7c0a4b303c50d769ba2317a328bf71ece25468226ea24c5d7713df349bbf/lalsuite-7.26.11-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6d8020057afd9f5a342119f89f25eae48f81644e53b942f5176c5158d2484281", size = 38077102, upload-time = "2026-04-27T08:25:58.305Z" }, + { url = "https://files.pythonhosted.org/packages/97/0e/6cb88e6ce4a0fe47fd19a878c70e32f7feff7b228a61584ab66ccee6c0d4/lalsuite-7.26.11-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:0b86e12bec3c92dba75f507242729c5d4d77f8228afb24431a663b04b504d7d3", size = 40137837, upload-time = "2026-04-27T08:26:01.459Z" }, + { url = "https://files.pythonhosted.org/packages/e3/5d/8dc7dd53cd14f58dcb6d75912a79977c9f5b3fd0ff8fc45bc5b521ceeeb8/lalsuite-7.26.11-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:53f3099bd87f4512ddd50819549a725197e250371f5b43150ceb73695573d689", size = 38710999, upload-time = "2026-04-27T08:26:04.495Z" }, + { url = "https://files.pythonhosted.org/packages/1a/50/f0ce97154c4e749a6f1d1a0763d647a157d2289895a3e87041427bc7f070/lalsuite-7.26.11-cp311-cp311-macosx_13_0_x86_64.whl", hash = "sha256:2f6a75deb4a46fea4f8be5efbdc2d5349a4c2042584328b2e97645037537a622", size = 40219823, upload-time = "2026-04-27T08:26:07.673Z" }, + { url = "https://files.pythonhosted.org/packages/e1/4e/0d27d65f94c2ac4bd7e22ac81beee48a646d81d911c9dc4145cb1078296e/lalsuite-7.26.11-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:6e52504199a95e6df747f269085c061396687398c332923a5c049f9401ab75a6", size = 38077168, upload-time = "2026-04-27T08:26:10.806Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f6/9c1b0d9d4d7465aeeda5ef725dc78616674ff14f83f3f8f04c05f4d17904/lalsuite-7.26.11-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:35d53b1f38e64320368aaf9257c6aea34dfe4c727f3ddafec6e9debccbcd7686", size = 40139374, upload-time = "2026-04-27T08:26:14.147Z" }, + { url = "https://files.pythonhosted.org/packages/68/2d/7b11e16d87780e2a7d779f9db4e1b1a9838d2f38c87b4da04ba7536928de/lalsuite-7.26.11-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:637733bb5ee5987c12d290b920b3e168d62eea720f40224bad21aad0a29be8a3", size = 38888265, upload-time = "2026-04-27T08:26:17.028Z" }, + { url = "https://files.pythonhosted.org/packages/36/4d/c59f85c1675362b70ef660521b81621792f5039fffcbf205ece019a0b343/lalsuite-7.26.11-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:bc7ba72e82a15c4bf6d8af28a228a76ed4dd01b6b1f99d67827426a7161e4998", size = 40319766, upload-time = "2026-04-27T08:26:20.875Z" }, + { url = "https://files.pythonhosted.org/packages/85/fd/9313f82301340d5ee90fb9c9adab017d63ed696ea2350c579f8add88d0a3/lalsuite-7.26.11-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:035024031fe2e0f9a2dfee811c307534b859acadaba7398aaf09edb061c970f6", size = 38055937, upload-time = "2026-04-27T08:26:24.463Z" }, + { url = "https://files.pythonhosted.org/packages/3f/74/9cf796165b9f730db561524c63bb959531785eceb7ef94637c1ba00ffa47/lalsuite-7.26.11-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:cc667d2676655d9cdf53f2d0b17c0fcc048cbb55298af18b3adf088f56c0f666", size = 40115490, upload-time = "2026-04-27T08:26:27.574Z" }, + { url = "https://files.pythonhosted.org/packages/f6/aa/89231f955274443473b182b2474b68b34b7941218a38c5334ca9ceee771b/lalsuite-7.26.11-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:6e3eca2f13f7b953f57d5c7610f22c095ede887025c599fa718e4b6344b190c6", size = 38778514, upload-time = "2026-04-27T08:26:31.123Z" }, + { url = "https://files.pythonhosted.org/packages/ce/7b/dd86fa3caa5012131514b254e1c22f4900bc1bebe48a6c11f877cf8e9a41/lalsuite-7.26.11-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:e0eedf77f4fa99ef2a826a79b50311a7dbed3c46836ceee110c0535b0f7726ce", size = 40431172, upload-time = "2026-04-27T08:26:34.462Z" }, + { url = "https://files.pythonhosted.org/packages/6f/c1/b1352c27faeaa1a0c8394c182c78d2949277e94342ca971956762afc91ab/lalsuite-7.26.11-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:dece4ae7ee5ddb7924bf83dc0fd928c52aa981958054d7a08ce633506695883f", size = 38053524, upload-time = "2026-04-27T08:26:37.635Z" }, + { url = "https://files.pythonhosted.org/packages/e3/aa/79c6c4b44dbd3f52bb1d66b83fdb40a3530c77e44aca67a1a201cf6f14a5/lalsuite-7.26.11-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5d324806f50aba1efad7f25671f6c814116f301f7d2940bb1f69416da2970570", size = 40113540, upload-time = "2026-04-27T08:26:40.903Z" }, + { url = "https://files.pythonhosted.org/packages/1f/52/f34c4d0bf38056439c0cdc41d8f231727680d217858f542013a620424894/lalsuite-7.26.11-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:30ca69d6844fd942b536b197346488388d50db0dc12f1d097a22fbeb53172e41", size = 39222715, upload-time = "2026-04-27T08:26:44.371Z" }, + { url = "https://files.pythonhosted.org/packages/d6/d8/840d2156c7b984fc33131c1f47ce062e311b9f4c9d717bc0b4b2f62f2e30/lalsuite-7.26.11-cp314-cp314-macosx_13_0_x86_64.whl", hash = "sha256:cd3ca3a05f91250d357c63eb4e0795e914197e1a9a274c0f2d2da3d785d78566", size = 40407326, upload-time = "2026-04-27T08:26:47.879Z" }, + { url = "https://files.pythonhosted.org/packages/f2/d1/d7b3a5a7af9fd808bc0660c61696387b7ea51222133297be82287f6636b8/lalsuite-7.26.11-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:68122eff45fc3d4f6906d701ceddeb360ebb1dc0d8784aca8ea27dcd8c2379b1", size = 38088119, upload-time = "2026-04-27T08:26:51.311Z" }, + { url = "https://files.pythonhosted.org/packages/aa/1c/7875d7e53282606ec1ad14eb4982bb34a48cf884de25c218348f7be1301b/lalsuite-7.26.11-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:7b5ae63f512459586f7856a81186489742922ba647f97fa21be3bb0fc434ee52", size = 40133782, upload-time = "2026-04-27T08:26:54.543Z" }, + { url = "https://files.pythonhosted.org/packages/22/d2/42d97504dd88ee4962d39e89335106dcb1a36afd316acd1d48a1b81830f7/lalsuite-7.26.11-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:ce4aca4e9f055dbb2c4fb35bab3958588d15aa68303071e6a6ad3f4b7498d009", size = 38091760, upload-time = "2026-04-27T08:26:57.57Z" }, + { url = "https://files.pythonhosted.org/packages/0b/fe/526f5caa17888d26c0cb98ec976b3485fd299e981651b74379c4d8f328a2/lalsuite-7.26.11-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:a1f37616585f92a5fe20f16a041f25135b73e7d53c8ab59d59fec6c2c7ee43d8", size = 40120289, upload-time = "2026-04-27T08:27:00.577Z" }, +] + +[[package]] +name = "ligotimegps" +version = "2.0.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +sdist = { url = "https://files.pythonhosted.org/packages/39/2e/cef2ee4c4f3f1f04566e3e7e9343811a74f6e9a0bc6ef4711248f132e3bb/ligotimegps-2.0.1.tar.gz", hash = "sha256:88626c02ad9a464d1242a1147b40074792f424bafa2ab013eee629c7d1b6469c", size = 35191, upload-time = "2019-04-25T16:00:25.481Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/b6/6d6d0585fa2ae936a9f5d411b1f0fbe9fcb0aca0c51a775aa4f8f95fdf5e/ligotimegps-2.0.1-py2.py3-none-any.whl", hash = "sha256:da8c1289ba1310337ef5177e7936e25ce47d4e8e6a269cbdd5e9abfc5b5db490", size = 19930, upload-time = "2019-04-25T16:00:23.354Z" }, +] + +[[package]] +name = "ligotimegps" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +sdist = { url = "https://files.pythonhosted.org/packages/7e/9b/521be61daa7603603826d40fa6327a22e04df4f20e69d9ed42182370a7f8/ligotimegps-2.1.0.tar.gz", hash = "sha256:d948ffc4d58472b303478a983ec56d2e6a7f35e54cbb351ca6f1292e68398cb2", size = 28069, upload-time = "2025-10-07T10:55:28.124Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/92/38674acb59c663bcdb69522e11afd3b4e6371f74b5764aeec2e99308cd79/ligotimegps-2.1.0-py3-none-any.whl", hash = "sha256:14dbbb07b175b94b4e1a519d7baa4548f0ea07bc71ef7d7f096ad8397d359043", size = 23370, upload-time = "2025-10-07T10:55:28.855Z" }, +] + +[[package]] +name = "lineax" +version = "0.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "equinox" }, + { name = "jax", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "jax", version = "0.10.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "jaxtyping", version = "0.3.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "jaxtyping", version = "0.3.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/35/d6/4e28416a6fe58dd6bc7565b1ffa330f4d0ba7d74212642b1b734c511299e/lineax-0.1.0.tar.gz", hash = "sha256:5f1a8f060142af2cdbf7d66b99e8d3071c3aa734b677df6339df4b4c4c0554d2", size = 50209, upload-time = "2026-01-27T21:17:26.652Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/80/0c/2ed47112fc1958a0a81c9b015d4e1861953a1ec3a17b081c0180a25ce82c/lineax-0.1.0-py3-none-any.whl", hash = "sha256:f00911c6b07d427c4835db46856970c8348bc82a035b51f4386ad09382af957a", size = 74600, upload-time = "2026-01-27T21:17:25.33Z" }, +] + +[[package]] +name = "lscsoft-glue" +version = "4.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "igwn-segments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/de/0b/cf319595547297bc21d6f6228c07f6e63b6c7f47fc9d3ac47ee6998b06ee/lscsoft_glue-4.1.1.tar.gz", hash = "sha256:487df9085eaafe635dd62445e744017c1c90cc696be76ac6b654bd70841ae733", size = 56280, upload-time = "2025-07-18T14:52:45.284Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/cc/a78f98ed9c3ac814072e4db226a4e2b27a58a62fed8341de2459c70b8036/lscsoft_glue-4.1.1-py3-none-any.whl", hash = "sha256:3e34b0dee5a1e8d2085f701398b184cd4541c4c23baf7f3ae451ee3d8eab4251", size = 51071, upload-time = "2025-07-18T14:52:44.376Z" }, +] + +[[package]] +name = "markdown-it-py" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", size = 11631, upload-time = "2025-09-27T18:36:05.558Z" }, + { url = "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", size = 12057, upload-time = "2025-09-27T18:36:07.165Z" }, + { url = "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", size = 22050, upload-time = "2025-09-27T18:36:08.005Z" }, + { url = "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", size = 20681, upload-time = "2025-09-27T18:36:08.881Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", size = 20705, upload-time = "2025-09-27T18:36:10.131Z" }, + { url = "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", size = 21524, upload-time = "2025-09-27T18:36:11.324Z" }, + { url = "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", size = 20282, upload-time = "2025-09-27T18:36:12.573Z" }, + { url = "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", size = 20745, upload-time = "2025-09-27T18:36:13.504Z" }, + { url = "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", size = 14571, upload-time = "2025-09-27T18:36:14.779Z" }, + { url = "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", size = 15056, upload-time = "2025-09-27T18:36:16.125Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", size = 13932, upload-time = "2025-09-27T18:36:17.311Z" }, + { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" }, + { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" }, + { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" }, + { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" }, + { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" }, + { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" }, + { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" }, + { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" }, + { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload-time = "2025-09-27T18:36:28.045Z" }, + { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload-time = "2025-09-27T18:36:29.025Z" }, + { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload-time = "2025-09-27T18:36:29.954Z" }, + { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, +] + +[[package]] +name = "matplotlib" +version = "3.10.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "contourpy", version = "1.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "contourpy", version = "1.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "cycler" }, + { name = "fonttools" }, + { name = "kiwisolver" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "packaging" }, + { name = "pillow" }, + { name = "pyparsing" }, + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/1b/4be5be87d43d327a0cf4de1a56e86f7f84c89312452406cf122efe2839e6/matplotlib-3.10.9.tar.gz", hash = "sha256:fd66508e8c6877d98e586654b608a0456db8d7e8a546eb1e2600efd957302358", size = 34811233, upload-time = "2026-04-24T00:14:13.539Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/6f/340b04986e67aac6f66c5145ce68bf72c64bed30f92c8913499a6e6b8f99/matplotlib-3.10.9-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77210dce9cb8153dffc967efaae990543392563d5a376d4dd8539bebcb0ed217", size = 8296625, upload-time = "2026-04-24T00:11:43.376Z" }, + { url = "https://files.pythonhosted.org/packages/bb/2f/127081eb83162053ebb9678ceac64220b93a663e0167432566e9c7c82aab/matplotlib-3.10.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1e7698ac9868428e84d2c967424803b2472ff7167d9d6590d4204ed775343c3b", size = 8188790, upload-time = "2026-04-24T00:11:46.556Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b7/d8bcec2626c35f96972bff656299fef4578113ea6193c8fdad324710410c/matplotlib-3.10.9-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1aa972116abb4c9d201bf245620b433726cb6856f3bef6a78f776a00f5c92d37", size = 8769389, upload-time = "2026-04-24T00:11:48.959Z" }, + { url = "https://files.pythonhosted.org/packages/12/49/b78e214a527ea732033b7f4d37f7afb504d74ba9d134bd47938230dfb8b1/matplotlib-3.10.9-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae2f11957b27ce53497dd4d7b235c4d4f1faf383dfb39d0c5beb833bff883294", size = 9589657, upload-time = "2026-04-24T00:11:51.915Z" }, + { url = "https://files.pythonhosted.org/packages/5f/15/5246f7b43beae19c74dfee651d58d6cc8112e06f77adb4e88cc04f2e3a23/matplotlib-3.10.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b049278ddce116aaa1c1377ebf58adea909132dfce0281cf7e3a1ea9fc2e2c65", size = 9651983, upload-time = "2026-04-24T00:11:54.766Z" }, + { url = "https://files.pythonhosted.org/packages/75/77/5acecfe672ba0fa1b8c0454f69ce155d1e6fc5852fa7206bf9afaf767121/matplotlib-3.10.9-cp310-cp310-win_amd64.whl", hash = "sha256:82834c3c292d24d3a8aae77cd2d20019de69d692a34a970e4fdb8d33e2ea3dda", size = 8199701, upload-time = "2026-04-24T00:11:58.389Z" }, + { url = "https://files.pythonhosted.org/packages/4c/8c/290f021104741fea63769c31494f5324c0cd249bf536a65a4350767b1f22/matplotlib-3.10.9-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:68cfdcede415f7c8f5577b03303dd94526cdb6d11036cecdc205e08733b2d2bb", size = 8306860, upload-time = "2026-04-24T00:12:01.207Z" }, + { url = "https://files.pythonhosted.org/packages/51/18/325cd32ece1120d1da51cc4e4294c6580190699490183fc2fe8cb6d61ec5/matplotlib-3.10.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfca0129678bd56379db26c52b5d77ed7de314c047492fbdc763aa7501710cfb", size = 8199254, upload-time = "2026-04-24T00:12:04.239Z" }, + { url = "https://files.pythonhosted.org/packages/79/db/e28c1b83e3680740aa78925f5fb2ae4d16207207419ad75ea9fe604f8676/matplotlib-3.10.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8e436d155fa8a3399dc62683f8f5d0e2e50d25d0144a73edd73f82eec8f4abfb", size = 8777092, upload-time = "2026-04-24T00:12:06.793Z" }, + { url = "https://files.pythonhosted.org/packages/55/fa/3ce7adfe9ba101748f465211660d9c6374c876b671bdb8c2bb6d347e8b94/matplotlib-3.10.9-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56fc0bd271b00025c6edfdc7c2dcd247372c8e1544971d62e1dc7c17367e8bf9", size = 9595691, upload-time = "2026-04-24T00:12:09.706Z" }, + { url = "https://files.pythonhosted.org/packages/36/c4/6960a76686ed668f2c60f84e9799ba4c0d56abdb36b1577b60c1d061d1ec/matplotlib-3.10.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5a6104ed666402ba5106d7f36e0e0cdca4e8d7fa4d39708ca88019e2835a2eb", size = 9659771, upload-time = "2026-04-24T00:12:12.766Z" }, + { url = "https://files.pythonhosted.org/packages/7e/0d/271aace3342157c64700c9ff4c59c7b392f3dbab393692e8db6fbe7ab96c/matplotlib-3.10.9-cp311-cp311-win_amd64.whl", hash = "sha256:d730e984eddf56974c3e72b6129c7ca462ac38dc624338f4b0b23eb23ecba00f", size = 8205112, upload-time = "2026-04-24T00:12:15.773Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ee/cb57ad4754f3e7b9174ce6ce66d9205fb827067e48a9f58ac09d7e7d6b77/matplotlib-3.10.9-cp311-cp311-win_arm64.whl", hash = "sha256:51bf0ddbdc598e060d46c16b5590708f81a1624cefbaaf62f6a81bf9285b8c80", size = 8132310, upload-time = "2026-04-24T00:12:18.645Z" }, + { url = "https://files.pythonhosted.org/packages/35/c6/5581e26c72233ebb2a2a6fed2d24fb7c66b4700120b813f51b0555acf0b6/matplotlib-3.10.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f0c3c28d9fbcc1fe7a03be236d73430cf6409c41fb2383a7ac52fe932b072cb1", size = 8319908, upload-time = "2026-04-24T00:12:21.323Z" }, + { url = "https://files.pythonhosted.org/packages/b7/18/4880dd762e40cd360c1bf06e890c5a97b997e91cb324602b1a19950ad5ce/matplotlib-3.10.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41cb28c2bd769aa3e98322c6ab09854cbcc52ab69d2759d681bba3e327b2b320", size = 8216016, upload-time = "2026-04-24T00:12:23.4Z" }, + { url = "https://files.pythonhosted.org/packages/32/91/d024616abdba99e83120e07a20658976f6a343646710760c4a51df126029/matplotlib-3.10.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ae20801130378b82d647ff5047c07316295b68dc054ca6b3c13519d0ea624285", size = 8789336, upload-time = "2026-04-24T00:12:26.096Z" }, + { url = "https://files.pythonhosted.org/packages/5c/04/030a2f61ef2158f5e4c259487a92ac877732499fb33d871585d89e03c42d/matplotlib-3.10.9-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c63ebcd8b4b169eb2f5c200552ae6b8be8999a005b6b507ed76fb8d7d674fe2", size = 9604602, upload-time = "2026-04-24T00:12:29.052Z" }, + { url = "https://files.pythonhosted.org/packages/fc/c2/541e4d09d87bb6b5830fc28b4c887a9a8cf4e1c6cee698a8c05552ae2003/matplotlib-3.10.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d75d11c949914165976c621b2324f9ef162af7ebf4b057ddf95dd1dba7e5edcf", size = 9670966, upload-time = "2026-04-24T00:12:32.131Z" }, + { url = "https://files.pythonhosted.org/packages/04/a1/4571fc46e7702de8d0c2dc54ad1b2f8e29328dea3ee90831181f7353d93c/matplotlib-3.10.9-cp312-cp312-win_amd64.whl", hash = "sha256:d091f9d758b34aaaaa6331d13574bf01891d903b3dec59bfff458ef7551de5d6", size = 8217462, upload-time = "2026-04-24T00:12:35.226Z" }, + { url = "https://files.pythonhosted.org/packages/4b/d0/2269edb12aa30c13c8bcc9382892e39943ce1d28aab4ec296e0381798e81/matplotlib-3.10.9-cp312-cp312-win_arm64.whl", hash = "sha256:10cc5ce06d10231c36f40e875f3c7e8050362a4ee8f0ee5d29a6b3277d57bb42", size = 8136688, upload-time = "2026-04-24T00:12:37.442Z" }, + { url = "https://files.pythonhosted.org/packages/aa/d3/8d4f6afbecb49fc04e060a57c0fce39ea51cc163a6bd87303ccd698e4fa6/matplotlib-3.10.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b580440f1ff81a0e34122051a3dfabb7e4b7f9e380629929bde0eff9af72165f", size = 8320331, upload-time = "2026-04-24T00:12:39.688Z" }, + { url = "https://files.pythonhosted.org/packages/63/d9/9e14bc7564bf92d5ffa801ae5fac819ce74b925dfb55e3ebde61a3bbad3e/matplotlib-3.10.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b1b745c489cd1a77a0dc1120a05dc87af9798faebc913601feb8c73d89bf2d1e", size = 8216461, upload-time = "2026-04-24T00:12:42.494Z" }, + { url = "https://files.pythonhosted.org/packages/8a/17/4402d0d14ccf1dfc70932600b68097fbbf9c898a4871d2cbbe79c7801a32/matplotlib-3.10.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8f3bcac1ca5ed000a6f4337d47ba67dfddf37ed6a46c15fd7f014997f7bf865f", size = 8790091, upload-time = "2026-04-24T00:12:44.789Z" }, + { url = "https://files.pythonhosted.org/packages/3e/0b/322aeec06dd9b91411f92028b37d447342770a24392aa4813e317064dad5/matplotlib-3.10.9-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a8d66a55def891c33147ba3ba9bfcabf0b526a43764c818acbb4525e5ed0838", size = 9605027, upload-time = "2026-04-24T00:12:47.583Z" }, + { url = "https://files.pythonhosted.org/packages/74/88/5f13482f55e7b00bcfc09838b093c2456e1379978d2a146844aae05350ad/matplotlib-3.10.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d843374407c4017a6403b59c6c81606773d136f3259d5b6da3131bc814542cc2", size = 9671269, upload-time = "2026-04-24T00:12:50.878Z" }, + { url = "https://files.pythonhosted.org/packages/c5/e0/0840fd2f93da988ec660b8ad1984abe9f25d2aed22a5e394ff1c68c88307/matplotlib-3.10.9-cp313-cp313-win_amd64.whl", hash = "sha256:f4399f64b3e94cd500195490972ae1ee81170df1636fa15364d157d5bdd7b921", size = 8217588, upload-time = "2026-04-24T00:12:53.784Z" }, + { url = "https://files.pythonhosted.org/packages/47/b9/d706d06dd605c49b9f83a2aed8c13e3e5db70697d7a80b7e3d7915de6b17/matplotlib-3.10.9-cp313-cp313-win_arm64.whl", hash = "sha256:ba7b3b8ef09eab7df0e86e9ae086faa433efbfbdb46afcb3aa16aabf779469a8", size = 8136913, upload-time = "2026-04-24T00:12:56.501Z" }, + { url = "https://files.pythonhosted.org/packages/9b/45/6e32d96978264c8ca8c4b1010adb955a1a49cfaf314e212bbc8908f04a61/matplotlib-3.10.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:09218df8a93712bd6ea133e83a153c755448cf7868316c531cffcc43f69d1cc9", size = 8368019, upload-time = "2026-04-24T00:12:58.896Z" }, + { url = "https://files.pythonhosted.org/packages/86/0a/c8e3d3bba245f0f7fc424937f8ff7ef77291a36af3edb97ccd78aa93d84f/matplotlib-3.10.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:82368699727bfb7b0182e1aa13082e3c08e092fa1a25d3e1fd92405bff96f6d4", size = 8264645, upload-time = "2026-04-24T00:13:01.406Z" }, + { url = "https://files.pythonhosted.org/packages/3d/aa/5bf5a14fe4fed73a4209a155606f8096ff797aad89c6c35179026571133e/matplotlib-3.10.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3225f4e1edcb8c86c884ddf79ebe20ecd0a67d30188f279897554ccd8fded4dc", size = 8802194, upload-time = "2026-04-24T00:13:03.702Z" }, + { url = "https://files.pythonhosted.org/packages/dd/5e/b4be852d6bba6fd15893fadf91ff26ae49cb91aac789e95dde9d342e664f/matplotlib-3.10.9-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de2445a0c6690d21b7eb6ce071cebad6d40a2e9bdf10d039074a96ba19797b99", size = 9622684, upload-time = "2026-04-24T00:13:06.647Z" }, + { url = "https://files.pythonhosted.org/packages/4c/3d/ed428c971139112ef730f62770654d609467346d09d4b62617e1afd68a5a/matplotlib-3.10.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:b2b9516251cb89ff618d757daec0e2ed1bf21248013844a853d87ef85ab3081d", size = 9680790, upload-time = "2026-04-24T00:13:10.009Z" }, + { url = "https://files.pythonhosted.org/packages/e7/09/052e884aaf2b985c63cb79f715f1d5b6a3eaa7de78f6a52b9dbc077d5b53/matplotlib-3.10.9-cp313-cp313t-win_amd64.whl", hash = "sha256:e9fae004b941b23ff2edcf1567a857ed77bafc8086ffa258190462328434faf8", size = 8287571, upload-time = "2026-04-24T00:13:13.087Z" }, + { url = "https://files.pythonhosted.org/packages/f4/38/ae27288e788c35a4250491422f3db7750366fc8c97d6f36fbdecfc1f5518/matplotlib-3.10.9-cp313-cp313t-win_arm64.whl", hash = "sha256:6b63d9c7c769b88ab81e10dc86e4e0607cf56817b9f9e6cf24b2a5f1693b8e38", size = 8188292, upload-time = "2026-04-24T00:13:15.546Z" }, + { url = "https://files.pythonhosted.org/packages/d6/e6/3bd8afd04949f02eabc1c17115ea5255e19cacd4d06fc5abdde4eeb0052c/matplotlib-3.10.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:172db52c9e683f5d12eaf57f0f54834190e12581fe1cc2a19595a8f5acb4e77d", size = 8321276, upload-time = "2026-04-24T00:13:18.318Z" }, + { url = "https://files.pythonhosted.org/packages/41/86/86231232fff41c9f8e4a1a7d7a597d349a02527109c3af7d618366122139/matplotlib-3.10.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:97e35e8d39ccc85859095e01a53847432ba9a53ddf7986f7a54a11b73d0e143f", size = 8218218, upload-time = "2026-04-24T00:13:20.974Z" }, + { url = "https://files.pythonhosted.org/packages/85/8f/becc9722cafc64f5d2eb0b7c1bf5f585271c618a45dbd8fabeb021f898b6/matplotlib-3.10.9-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aba1615dabe83188e19d4f75a253c6a08423e04c1425e64039f800050a69de6b", size = 9608145, upload-time = "2026-04-24T00:13:23.228Z" }, + { url = "https://files.pythonhosted.org/packages/32/5d/f7e914f7d9325abff4057cee62c0fa70263683189f774473cbfb534cd13b/matplotlib-3.10.9-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34cf8167e023ad956c15f36302911d5406bd99a9862c1a8499ea6f7c0e015dc2", size = 9885085, upload-time = "2026-04-24T00:13:25.849Z" }, + { url = "https://files.pythonhosted.org/packages/a5/fd/fa69f2221534e80cc5772ac2b7d222011a2acafc2ec7216d5dd174c864ae/matplotlib-3.10.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:59476c6d29d612b8e9bb6ce8c5b631be6ba8f9e3a2421f22a02b192c7dd28716", size = 9672358, upload-time = "2026-04-24T00:13:28.906Z" }, + { url = "https://files.pythonhosted.org/packages/ab/1a/5a4f747a8b271cbb024946d2dd3c913ab5032ba430626f8c3528ada96b4b/matplotlib-3.10.9-cp314-cp314-win_amd64.whl", hash = "sha256:336b9acc64d309063126edcdaca00db9373af3c476bb94388fe9c5a53ad13e6f", size = 8349970, upload-time = "2026-04-24T00:13:31.904Z" }, + { url = "https://files.pythonhosted.org/packages/64/dc/95d60ecaefe30680a154b52ea96ab4b0dab547f1fd6aa12f5fb655e89cae/matplotlib-3.10.9-cp314-cp314-win_arm64.whl", hash = "sha256:2dc9477819ffd78ad12a20df1d9d6a6bd4fec6aaa9072681465fddca052f1456", size = 8272785, upload-time = "2026-04-24T00:13:34.511Z" }, + { url = "https://files.pythonhosted.org/packages/70/a0/005d68bc8b8418300ce6591f18586910a8526806e2ab663933d9f20a41e9/matplotlib-3.10.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:da4e09638420548f31c354032a6250e473c68e5a4e96899b4844cf39ddea23fe", size = 8367999, upload-time = "2026-04-24T00:13:36.962Z" }, + { url = "https://files.pythonhosted.org/packages/22/05/1236cc9290be70b2498af20ca348add76e3fffe7f67b477db5133a84f3ea/matplotlib-3.10.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:345f6f68ecc8da0ca56fad2ea08fde1a115eda530079eca185d50a7bc3e146c6", size = 8264543, upload-time = "2026-04-24T00:13:39.851Z" }, + { url = "https://files.pythonhosted.org/packages/cd/c2/071f5a5ff6c5bd63aaaf2f45c811d9bf2ced94bde188d9e1a519e21d0cba/matplotlib-3.10.9-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4edcfbd8565339aa62f1cd4012f7180926fdbe71850f7b0d3c379c175cd6b66c", size = 9622800, upload-time = "2026-04-24T00:13:42.296Z" }, + { url = "https://files.pythonhosted.org/packages/95/57/da7d1f10a85624b9e7db68e069dd94e58dc41dbf9463c5921632ecbe3661/matplotlib-3.10.9-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6be157fe17fc37cb95ac1d7374cf717ce9259616edec911a78d9d26dae8522d4", size = 9888561, upload-time = "2026-04-24T00:13:45.026Z" }, + { url = "https://files.pythonhosted.org/packages/67/b2/ef8d6bb59b0edb6c16c968b70f548aa13b54348972def5aa6ac85df67145/matplotlib-3.10.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4e42042d54db34fda4e95a7bd3e5789c2a995d2dad3eb8850232ee534092fbbf", size = 9680884, upload-time = "2026-04-24T00:13:48.066Z" }, + { url = "https://files.pythonhosted.org/packages/61/1c/d21bfeb9931881ebe96bcfcff27c7ae4b160ae0ec291a714c42641a56d75/matplotlib-3.10.9-cp314-cp314t-win_amd64.whl", hash = "sha256:c27df8b3848f32a83d1767566595e43cfaa4460380974da06f4279a7ec143c39", size = 8432333, upload-time = "2026-04-24T00:13:51.008Z" }, + { url = "https://files.pythonhosted.org/packages/78/23/92493c3e6e1b635ccfff146f7b99e674808787915420373ac399283764c2/matplotlib-3.10.9-cp314-cp314t-win_arm64.whl", hash = "sha256:a49f1eadc84ca85fd72fa4e89e70e61bf86452df6f971af04b12c60761a0772c", size = 8324785, upload-time = "2026-04-24T00:13:53.633Z" }, + { url = "https://files.pythonhosted.org/packages/2c/2b/0e92ad0ac446633f928a1563db4aa8add407e1924faf0ded5b95b35afb27/matplotlib-3.10.9-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1872fb212a05b729e649754a72d5da61d03e0554d76e80303b6f83d1d2c0552b", size = 8293058, upload-time = "2026-04-24T00:13:56.339Z" }, + { url = "https://files.pythonhosted.org/packages/4b/23/74682fd369f5299ceda438fea2a0662e6383b85c9383fb9cdfcf04713e07/matplotlib-3.10.9-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:985f2238880e2e69093f588f5fe2e46771747febf0649f3cf7f7b7480875317f", size = 8186627, upload-time = "2026-04-24T00:13:58.623Z" }, + { url = "https://files.pythonhosted.org/packages/ca/e8/368aab88f3c4cd8992800f31abfe0670c3e47540ba20a97e9fdbcde594b3/matplotlib-3.10.9-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6640f75af2c6148293caa0a2b39dd806a492dd66c8a8b04035813e33d0fd2585", size = 8764117, upload-time = "2026-04-24T00:14:01.684Z" }, + { url = "https://files.pythonhosted.org/packages/63/e2/9f66ca6a651a52abfe0d4964ce01439ed34f3f1e119de10ff3a07f403043/matplotlib-3.10.9-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:42fb814efabe95c06c1994d8ab5a8385f43a249e23badd3ba931d4308e5bca20", size = 8304420, upload-time = "2026-04-24T00:14:04.57Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e8/467c03568218792906aa87b5e7bb379b605e056ed0c74fe00c051786d925/matplotlib-3.10.9-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f76e640a5268850bfda54b5131b1b1941cc685e42c5fa98ed9f2d64038308cba", size = 8197981, upload-time = "2026-04-24T00:14:07.233Z" }, + { url = "https://files.pythonhosted.org/packages/6f/87/afead29192170917537934c6aff4b008c805fff7b1ccea0c79120d96beda/matplotlib-3.10.9-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3fc0364dfbe1d07f6d15c5ebd0c5bf89e126916e5a8667dd4a7a6e84c36653d4", size = 8774002, upload-time = "2026-04-24T00:14:09.816Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + +[[package]] +name = "ml-dtypes" +version = "0.5.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0e/4a/c27b42ed9b1c7d13d9ba8b6905dece787d6259152f2309338aed29b2447b/ml_dtypes-0.5.4.tar.gz", hash = "sha256:8ab06a50fb9bf9666dd0fe5dfb4676fa2b0ac0f31ecff72a6c3af8e22c063453", size = 692314, upload-time = "2025-11-17T22:32:31.031Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/3a/c5b855752a70267ff729c349e650263adb3c206c29d28cc8ea7ace30a1d5/ml_dtypes-0.5.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b95e97e470fe60ed493fd9ae3911d8da4ebac16bd21f87ffa2b7c588bf22ea2c", size = 679735, upload-time = "2025-11-17T22:31:31.367Z" }, + { url = "https://files.pythonhosted.org/packages/41/79/7433f30ee04bd4faa303844048f55e1eb939131c8e5195a00a96a0939b64/ml_dtypes-0.5.4-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4b801ebe0b477be666696bda493a9be8356f1f0057a57f1e35cd26928823e5a", size = 5051883, upload-time = "2025-11-17T22:31:33.658Z" }, + { url = "https://files.pythonhosted.org/packages/10/b1/8938e8830b0ee2e167fc75a094dea766a1152bde46752cd9bfc57ee78a82/ml_dtypes-0.5.4-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:388d399a2152dd79a3f0456a952284a99ee5c93d3e2f8dfe25977511e0515270", size = 5030369, upload-time = "2025-11-17T22:31:35.595Z" }, + { url = "https://files.pythonhosted.org/packages/c7/a3/51886727bd16e2f47587997b802dd56398692ce8c6c03c2e5bb32ecafe26/ml_dtypes-0.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:4ff7f3e7ca2972e7de850e7b8fcbb355304271e2933dd90814c1cb847414d6e2", size = 210738, upload-time = "2025-11-17T22:31:37.43Z" }, + { url = "https://files.pythonhosted.org/packages/c6/5e/712092cfe7e5eb667b8ad9ca7c54442f21ed7ca8979745f1000e24cf8737/ml_dtypes-0.5.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6c7ecb74c4bd71db68a6bea1edf8da8c34f3d9fe218f038814fd1d310ac76c90", size = 679734, upload-time = "2025-11-17T22:31:39.223Z" }, + { url = "https://files.pythonhosted.org/packages/4f/cf/912146dfd4b5c0eea956836c01dcd2fce6c9c844b2691f5152aca196ce4f/ml_dtypes-0.5.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc11d7e8c44a65115d05e2ab9989d1e045125d7be8e05a071a48bc76eb6d6040", size = 5056165, upload-time = "2025-11-17T22:31:41.071Z" }, + { url = "https://files.pythonhosted.org/packages/a9/80/19189ea605017473660e43762dc853d2797984b3c7bf30ce656099add30c/ml_dtypes-0.5.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:19b9a53598f21e453ea2fbda8aa783c20faff8e1eeb0d7ab899309a0053f1483", size = 5034975, upload-time = "2025-11-17T22:31:42.758Z" }, + { url = "https://files.pythonhosted.org/packages/b4/24/70bd59276883fdd91600ca20040b41efd4902a923283c4d6edcb1de128d2/ml_dtypes-0.5.4-cp311-cp311-win_amd64.whl", hash = "sha256:7c23c54a00ae43edf48d44066a7ec31e05fdc2eee0be2b8b50dd1903a1db94bb", size = 210742, upload-time = "2025-11-17T22:31:44.068Z" }, + { url = "https://files.pythonhosted.org/packages/a0/c9/64230ef14e40aa3f1cb254ef623bf812735e6bec7772848d19131111ac0d/ml_dtypes-0.5.4-cp311-cp311-win_arm64.whl", hash = "sha256:557a31a390b7e9439056644cb80ed0735a6e3e3bb09d67fd5687e4b04238d1de", size = 160709, upload-time = "2025-11-17T22:31:46.557Z" }, + { url = "https://files.pythonhosted.org/packages/a8/b8/3c70881695e056f8a32f8b941126cf78775d9a4d7feba8abcb52cb7b04f2/ml_dtypes-0.5.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a174837a64f5b16cab6f368171a1a03a27936b31699d167684073ff1c4237dac", size = 676927, upload-time = "2025-11-17T22:31:48.182Z" }, + { url = "https://files.pythonhosted.org/packages/54/0f/428ef6881782e5ebb7eca459689448c0394fa0a80bea3aa9262cba5445ea/ml_dtypes-0.5.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a7f7c643e8b1320fd958bf098aa7ecf70623a42ec5154e3be3be673f4c34d900", size = 5028464, upload-time = "2025-11-17T22:31:50.135Z" }, + { url = "https://files.pythonhosted.org/packages/3a/cb/28ce52eb94390dda42599c98ea0204d74799e4d8047a0eb559b6fd648056/ml_dtypes-0.5.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ad459e99793fa6e13bd5b7e6792c8f9190b4e5a1b45c63aba14a4d0a7f1d5ff", size = 5009002, upload-time = "2025-11-17T22:31:52.001Z" }, + { url = "https://files.pythonhosted.org/packages/f5/f0/0cfadd537c5470378b1b32bd859cf2824972174b51b873c9d95cfd7475a5/ml_dtypes-0.5.4-cp312-cp312-win_amd64.whl", hash = "sha256:c1a953995cccb9e25a4ae19e34316671e4e2edaebe4cf538229b1fc7109087b7", size = 212222, upload-time = "2025-11-17T22:31:53.742Z" }, + { url = "https://files.pythonhosted.org/packages/16/2e/9acc86985bfad8f2c2d30291b27cd2bb4c74cea08695bd540906ed744249/ml_dtypes-0.5.4-cp312-cp312-win_arm64.whl", hash = "sha256:9bad06436568442575beb2d03389aa7456c690a5b05892c471215bfd8cf39460", size = 160793, upload-time = "2025-11-17T22:31:55.358Z" }, + { url = "https://files.pythonhosted.org/packages/d9/a1/4008f14bbc616cfb1ac5b39ea485f9c63031c4634ab3f4cf72e7541f816a/ml_dtypes-0.5.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c760d85a2f82e2bed75867079188c9d18dae2ee77c25a54d60e9cc79be1bc48", size = 676888, upload-time = "2025-11-17T22:31:56.907Z" }, + { url = "https://files.pythonhosted.org/packages/d3/b7/dff378afc2b0d5a7d6cd9d3209b60474d9819d1189d347521e1688a60a53/ml_dtypes-0.5.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce756d3a10d0c4067172804c9cc276ba9cc0ff47af9078ad439b075d1abdc29b", size = 5036993, upload-time = "2025-11-17T22:31:58.497Z" }, + { url = "https://files.pythonhosted.org/packages/eb/33/40cd74219417e78b97c47802037cf2d87b91973e18bb968a7da48a96ea44/ml_dtypes-0.5.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:533ce891ba774eabf607172254f2e7260ba5f57bdd64030c9a4fcfbd99815d0d", size = 5010956, upload-time = "2025-11-17T22:31:59.931Z" }, + { url = "https://files.pythonhosted.org/packages/e1/8b/200088c6859d8221454825959df35b5244fa9bdf263fd0249ac5fb75e281/ml_dtypes-0.5.4-cp313-cp313-win_amd64.whl", hash = "sha256:f21c9219ef48ca5ee78402d5cc831bd58ea27ce89beda894428bc67a52da5328", size = 212224, upload-time = "2025-11-17T22:32:01.349Z" }, + { url = "https://files.pythonhosted.org/packages/8f/75/dfc3775cb36367816e678f69a7843f6f03bd4e2bcd79941e01ea960a068e/ml_dtypes-0.5.4-cp313-cp313-win_arm64.whl", hash = "sha256:35f29491a3e478407f7047b8a4834e4640a77d2737e0b294d049746507af5175", size = 160798, upload-time = "2025-11-17T22:32:02.864Z" }, + { url = "https://files.pythonhosted.org/packages/4f/74/e9ddb35fd1dd43b1106c20ced3f53c2e8e7fc7598c15638e9f80677f81d4/ml_dtypes-0.5.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:304ad47faa395415b9ccbcc06a0350800bc50eda70f0e45326796e27c62f18b6", size = 702083, upload-time = "2025-11-17T22:32:04.08Z" }, + { url = "https://files.pythonhosted.org/packages/74/f5/667060b0aed1aa63166b22897fdf16dca9eb704e6b4bbf86848d5a181aa7/ml_dtypes-0.5.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6a0df4223b514d799b8a1629c65ddc351b3efa833ccf7f8ea0cf654a61d1e35d", size = 5354111, upload-time = "2025-11-17T22:32:05.546Z" }, + { url = "https://files.pythonhosted.org/packages/40/49/0f8c498a28c0efa5f5c95a9e374c83ec1385ca41d0e85e7cf40e5d519a21/ml_dtypes-0.5.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:531eff30e4d368cb6255bc2328d070e35836aa4f282a0fb5f3a0cd7260257298", size = 5366453, upload-time = "2025-11-17T22:32:07.115Z" }, + { url = "https://files.pythonhosted.org/packages/8c/27/12607423d0a9c6bbbcc780ad19f1f6baa2b68b18ce4bddcdc122c4c68dc9/ml_dtypes-0.5.4-cp313-cp313t-win_amd64.whl", hash = "sha256:cb73dccfc991691c444acc8c0012bee8f2470da826a92e3a20bb333b1a7894e6", size = 225612, upload-time = "2025-11-17T22:32:08.615Z" }, + { url = "https://files.pythonhosted.org/packages/e5/80/5a5929e92c72936d5b19872c5fb8fc09327c1da67b3b68c6a13139e77e20/ml_dtypes-0.5.4-cp313-cp313t-win_arm64.whl", hash = "sha256:3bbbe120b915090d9dd1375e4684dd17a20a2491ef25d640a908281da85e73f1", size = 164145, upload-time = "2025-11-17T22:32:09.782Z" }, + { url = "https://files.pythonhosted.org/packages/72/4e/1339dc6e2557a344f5ba5590872e80346f76f6cb2ac3dd16e4666e88818c/ml_dtypes-0.5.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:2b857d3af6ac0d39db1de7c706e69c7f9791627209c3d6dedbfca8c7e5faec22", size = 673781, upload-time = "2025-11-17T22:32:11.364Z" }, + { url = "https://files.pythonhosted.org/packages/04/f9/067b84365c7e83bda15bba2b06c6ca250ce27b20630b1128c435fb7a09aa/ml_dtypes-0.5.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:805cef3a38f4eafae3a5bf9ebdcdb741d0bcfd9e1bd90eb54abd24f928cd2465", size = 5036145, upload-time = "2025-11-17T22:32:12.783Z" }, + { url = "https://files.pythonhosted.org/packages/c6/bb/82c7dcf38070b46172a517e2334e665c5bf374a262f99a283ea454bece7c/ml_dtypes-0.5.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14a4fd3228af936461db66faccef6e4f41c1d82fcc30e9f8d58a08916b1d811f", size = 5010230, upload-time = "2025-11-17T22:32:14.38Z" }, + { url = "https://files.pythonhosted.org/packages/e9/93/2bfed22d2498c468f6bcd0d9f56b033eaa19f33320389314c19ef6766413/ml_dtypes-0.5.4-cp314-cp314-win_amd64.whl", hash = "sha256:8c6a2dcebd6f3903e05d51960a8058d6e131fe69f952a5397e5dbabc841b6d56", size = 221032, upload-time = "2025-11-17T22:32:15.763Z" }, + { url = "https://files.pythonhosted.org/packages/76/a3/9c912fe6ea747bb10fe2f8f54d027eb265db05dfb0c6335e3e063e74e6e8/ml_dtypes-0.5.4-cp314-cp314-win_arm64.whl", hash = "sha256:5a0f68ca8fd8d16583dfa7793973feb86f2fbb56ce3966daf9c9f748f52a2049", size = 163353, upload-time = "2025-11-17T22:32:16.932Z" }, + { url = "https://files.pythonhosted.org/packages/cd/02/48aa7d84cc30ab4ee37624a2fd98c56c02326785750cd212bc0826c2f15b/ml_dtypes-0.5.4-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:bfc534409c5d4b0bf945af29e5d0ab075eae9eecbb549ff8a29280db822f34f9", size = 702085, upload-time = "2025-11-17T22:32:18.175Z" }, + { url = "https://files.pythonhosted.org/packages/5a/e7/85cb99fe80a7a5513253ec7faa88a65306be071163485e9a626fce1b6e84/ml_dtypes-0.5.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2314892cdc3fcf05e373d76d72aaa15fda9fb98625effa73c1d646f331fcecb7", size = 5355358, upload-time = "2025-11-17T22:32:19.7Z" }, + { url = "https://files.pythonhosted.org/packages/79/2b/a826ba18d2179a56e144aef69e57fb2ab7c464ef0b2111940ee8a3a223a2/ml_dtypes-0.5.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d2ffd05a2575b1519dc928c0b93c06339eb67173ff53acb00724502cda231cf", size = 5366332, upload-time = "2025-11-17T22:32:21.193Z" }, + { url = "https://files.pythonhosted.org/packages/84/44/f4d18446eacb20ea11e82f133ea8f86e2bf2891785b67d9da8d0ab0ef525/ml_dtypes-0.5.4-cp314-cp314t-win_amd64.whl", hash = "sha256:4381fe2f2452a2d7589689693d3162e876b3ddb0a832cde7a414f8e1adf7eab1", size = 236612, upload-time = "2025-11-17T22:32:22.579Z" }, + { url = "https://files.pythonhosted.org/packages/ad/3f/3d42e9a78fe5edf792a83c074b13b9b770092a4fbf3462872f4303135f09/ml_dtypes-0.5.4-cp314-cp314t-win_arm64.whl", hash = "sha256:11942cbf2cf92157db91e5022633c0d9474d4dfd813a909383bd23ce828a4b7d", size = 168825, upload-time = "2025-11-17T22:32:23.766Z" }, +] + +[[package]] +name = "mpmath" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, +] + +[[package]] +name = "narwhals" +version = "2.21.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/0e/3ad61eb87088cc4932e0d851531fa82f845a6230b68b091a0e298cc7e537/narwhals-2.21.0.tar.gz", hash = "sha256:7c6e7f50528e62b7a967dd864d7e117d2955d38d4f730653ce46a9861358e2dc", size = 633083, upload-time = "2026-05-08T12:29:02.587Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/e1/68c2256b69a314eba133673377ba9118c356f6342a0c02b61de449cf2bf2/narwhals-2.21.0-py3-none-any.whl", hash = "sha256:1e6617d0fca68ae1fda29e5397c4eaacd3ffc9fffe6bcd6ded0c690475e853be", size = 451943, upload-time = "2026-05-08T12:29:01.058Z" }, +] + +[[package]] +name = "networkx" +version = "3.4.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368, upload-time = "2024-10-21T12:39:38.695Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263, upload-time = "2024-10-21T12:39:36.247Z" }, +] + +[[package]] +name = "networkx" +version = "3.6.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025, upload-time = "2025-12-08T17:02:39.908Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504, upload-time = "2025-12-08T17:02:38.159Z" }, +] + +[[package]] +name = "numpy" +version = "2.2.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" }, + { url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" }, + { url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" }, + { url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" }, + { url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" }, + { url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" }, + { url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" }, + { url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" }, + { url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" }, + { url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" }, + { url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" }, + { url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" }, + { url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" }, + { url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" }, + { url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" }, + { url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" }, + { url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" }, + { url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866, upload-time = "2025-05-17T21:33:50.273Z" }, + { url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455, upload-time = "2025-05-17T21:34:09.135Z" }, + { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" }, + { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" }, + { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" }, + { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" }, + { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" }, + { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" }, + { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" }, + { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" }, + { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" }, + { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" }, + { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" }, + { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" }, + { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" }, + { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" }, + { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" }, + { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" }, + { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" }, + { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" }, + { url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" }, + { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" }, + { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" }, + { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" }, + { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" }, + { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" }, + { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" }, + { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" }, + { url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" }, + { url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" }, + { url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" }, + { url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" }, +] + +[[package]] +name = "numpy" +version = "2.4.4" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +sdist = { url = "https://files.pythonhosted.org/packages/d7/9f/b8cef5bffa569759033adda9481211426f12f53299629b410340795c2514/numpy-2.4.4.tar.gz", hash = "sha256:2d390634c5182175533585cc89f3608a4682ccb173cc9bb940b2881c8d6f8fa0", size = 20731587, upload-time = "2026-03-29T13:22:01.298Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/c6/4218570d8c8ecc9704b5157a3348e486e84ef4be0ed3e38218ab473c83d2/numpy-2.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f983334aea213c99992053ede6168500e5f086ce74fbc4acc3f2b00f5762e9db", size = 16976799, upload-time = "2026-03-29T13:18:15.438Z" }, + { url = "https://files.pythonhosted.org/packages/dd/92/b4d922c4a5f5dab9ed44e6153908a5c665b71acf183a83b93b690996e39b/numpy-2.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:72944b19f2324114e9dc86a159787333b77874143efcf89a5167ef83cfee8af0", size = 14971552, upload-time = "2026-03-29T13:18:18.606Z" }, + { url = "https://files.pythonhosted.org/packages/8a/dc/df98c095978fa6ee7b9a9387d1d58cbb3d232d0e69ad169a4ce784bde4fd/numpy-2.4.4-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:86b6f55f5a352b48d7fbfd2dbc3d5b780b2d79f4d3c121f33eb6efb22e9a2015", size = 5476566, upload-time = "2026-03-29T13:18:21.532Z" }, + { url = "https://files.pythonhosted.org/packages/28/34/b3fdcec6e725409223dd27356bdf5a3c2cc2282e428218ecc9cb7acc9763/numpy-2.4.4-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:ba1f4fc670ed79f876f70082eff4f9583c15fb9a4b89d6188412de4d18ae2f40", size = 6806482, upload-time = "2026-03-29T13:18:23.634Z" }, + { url = "https://files.pythonhosted.org/packages/68/62/63417c13aa35d57bee1337c67446761dc25ea6543130cf868eace6e8157b/numpy-2.4.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a87ec22c87be071b6bdbd27920b129b94f2fc964358ce38f3822635a3e2e03d", size = 15973376, upload-time = "2026-03-29T13:18:26.677Z" }, + { url = "https://files.pythonhosted.org/packages/cf/c5/9fcb7e0e69cef59cf10c746b84f7d58b08bc66a6b7d459783c5a4f6101a6/numpy-2.4.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df3775294accfdd75f32c74ae39fcba920c9a378a2fc18a12b6820aa8c1fb502", size = 16925137, upload-time = "2026-03-29T13:18:30.14Z" }, + { url = "https://files.pythonhosted.org/packages/7e/43/80020edacb3f84b9efdd1591120a4296462c23fd8db0dde1666f6ef66f13/numpy-2.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0d4e437e295f18ec29bc79daf55e8a47a9113df44d66f702f02a293d93a2d6dd", size = 17329414, upload-time = "2026-03-29T13:18:33.733Z" }, + { url = "https://files.pythonhosted.org/packages/fd/06/af0658593b18a5f73532d377188b964f239eb0894e664a6c12f484472f97/numpy-2.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6aa3236c78803afbcb255045fbef97a9e25a1f6c9888357d205ddc42f4d6eba5", size = 18658397, upload-time = "2026-03-29T13:18:37.511Z" }, + { url = "https://files.pythonhosted.org/packages/e6/ce/13a09ed65f5d0ce5c7dd0669250374c6e379910f97af2c08c57b0608eee4/numpy-2.4.4-cp311-cp311-win32.whl", hash = "sha256:30caa73029a225b2d40d9fae193e008e24b2026b7ee1a867b7ee8d96ca1a448e", size = 6239499, upload-time = "2026-03-29T13:18:40.372Z" }, + { url = "https://files.pythonhosted.org/packages/bd/63/05d193dbb4b5eec1eca73822d80da98b511f8328ad4ae3ca4caf0f4db91d/numpy-2.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:6bbe4eb67390b0a0265a2c25458f6b90a409d5d069f1041e6aff1e27e3d9a79e", size = 12614257, upload-time = "2026-03-29T13:18:42.95Z" }, + { url = "https://files.pythonhosted.org/packages/87/c5/8168052f080c26fa984c413305012be54741c9d0d74abd7fbeeccae3889f/numpy-2.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:fcfe2045fd2e8f3cb0ce9d4ba6dba6333b8fa05bb8a4939c908cd43322d14c7e", size = 10486775, upload-time = "2026-03-29T13:18:45.835Z" }, + { url = "https://files.pythonhosted.org/packages/28/05/32396bec30fb2263770ee910142f49c1476d08e8ad41abf8403806b520ce/numpy-2.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15716cfef24d3a9762e3acdf87e27f58dc823d1348f765bbea6bef8c639bfa1b", size = 16689272, upload-time = "2026-03-29T13:18:49.223Z" }, + { url = "https://files.pythonhosted.org/packages/c5/f3/a983d28637bfcd763a9c7aafdb6d5c0ebf3d487d1e1459ffdb57e2f01117/numpy-2.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23cbfd4c17357c81021f21540da84ee282b9c8fba38a03b7b9d09ba6b951421e", size = 14699573, upload-time = "2026-03-29T13:18:52.629Z" }, + { url = "https://files.pythonhosted.org/packages/9b/fd/e5ecca1e78c05106d98028114f5c00d3eddb41207686b2b7de3e477b0e22/numpy-2.4.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8b3b60bb7cba2c8c81837661c488637eee696f59a877788a396d33150c35d842", size = 5204782, upload-time = "2026-03-29T13:18:55.579Z" }, + { url = "https://files.pythonhosted.org/packages/de/2f/702a4594413c1a8632092beae8aba00f1d67947389369b3777aed783fdca/numpy-2.4.4-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e4a010c27ff6f210ff4c6ef34394cd61470d01014439b192ec22552ee867f2a8", size = 6552038, upload-time = "2026-03-29T13:18:57.769Z" }, + { url = "https://files.pythonhosted.org/packages/7f/37/eed308a8f56cba4d1fdf467a4fc67ef4ff4bf1c888f5fc980481890104b1/numpy-2.4.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f9e75681b59ddaa5e659898085ae0eaea229d054f2ac0c7e563a62205a700121", size = 15670666, upload-time = "2026-03-29T13:19:00.341Z" }, + { url = "https://files.pythonhosted.org/packages/0a/0d/0e3ecece05b7a7e87ab9fb587855548da437a061326fff64a223b6dcb78a/numpy-2.4.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:81f4a14bee47aec54f883e0cad2d73986640c1590eb9bfaaba7ad17394481e6e", size = 16645480, upload-time = "2026-03-29T13:19:03.63Z" }, + { url = "https://files.pythonhosted.org/packages/34/49/f2312c154b82a286758ee2f1743336d50651f8b5195db18cdb63675ff649/numpy-2.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:62d6b0f03b694173f9fcb1fb317f7222fd0b0b103e784c6549f5e53a27718c44", size = 17020036, upload-time = "2026-03-29T13:19:07.428Z" }, + { url = "https://files.pythonhosted.org/packages/7b/e9/736d17bd77f1b0ec4f9901aaec129c00d59f5d84d5e79bba540ef12c2330/numpy-2.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fbc356aae7adf9e6336d336b9c8111d390a05df88f1805573ebb0807bd06fd1d", size = 18368643, upload-time = "2026-03-29T13:19:10.775Z" }, + { url = "https://files.pythonhosted.org/packages/63/f6/d417977c5f519b17c8a5c3bc9e8304b0908b0e21136fe43bf628a1343914/numpy-2.4.4-cp312-cp312-win32.whl", hash = "sha256:0d35aea54ad1d420c812bfa0385c71cd7cc5bcf7c65fed95fc2cd02fe8c79827", size = 5961117, upload-time = "2026-03-29T13:19:13.464Z" }, + { url = "https://files.pythonhosted.org/packages/2d/5b/e1deebf88ff431b01b7406ca3583ab2bbb90972bbe1c568732e49c844f7e/numpy-2.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:b5f0362dc928a6ecd9db58868fca5e48485205e3855957bdedea308f8672ea4a", size = 12320584, upload-time = "2026-03-29T13:19:16.155Z" }, + { url = "https://files.pythonhosted.org/packages/58/89/e4e856ac82a68c3ed64486a544977d0e7bdd18b8da75b78a577ca31c4395/numpy-2.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:846300f379b5b12cc769334464656bc882e0735d27d9726568bc932fdc49d5ec", size = 10221450, upload-time = "2026-03-29T13:19:18.994Z" }, + { url = "https://files.pythonhosted.org/packages/14/1d/d0a583ce4fefcc3308806a749a536c201ed6b5ad6e1322e227ee4848979d/numpy-2.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:08f2e31ed5e6f04b118e49821397f12767934cfdd12a1ce86a058f91e004ee50", size = 16684933, upload-time = "2026-03-29T13:19:22.47Z" }, + { url = "https://files.pythonhosted.org/packages/c1/62/2b7a48fbb745d344742c0277f01286dead15f3f68e4f359fbfcf7b48f70f/numpy-2.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e823b8b6edc81e747526f70f71a9c0a07ac4e7ad13020aa736bb7c9d67196115", size = 14694532, upload-time = "2026-03-29T13:19:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/e5/87/499737bfba066b4a3bebff24a8f1c5b2dee410b209bc6668c9be692580f0/numpy-2.4.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4a19d9dba1a76618dd86b164d608566f393f8ec6ac7c44f0cc879011c45e65af", size = 5199661, upload-time = "2026-03-29T13:19:28.31Z" }, + { url = "https://files.pythonhosted.org/packages/cd/da/464d551604320d1491bc345efed99b4b7034143a85787aab78d5691d5a0e/numpy-2.4.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d2a8490669bfe99a233298348acc2d824d496dee0e66e31b66a6022c2ad74a5c", size = 6547539, upload-time = "2026-03-29T13:19:30.97Z" }, + { url = "https://files.pythonhosted.org/packages/7d/90/8d23e3b0dafd024bf31bdec225b3bb5c2dbfa6912f8a53b8659f21216cbf/numpy-2.4.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45dbed2ab436a9e826e302fcdcbe9133f9b0006e5af7168afb8963a6520da103", size = 15668806, upload-time = "2026-03-29T13:19:33.887Z" }, + { url = "https://files.pythonhosted.org/packages/d1/73/a9d864e42a01896bb5974475438f16086be9ba1f0d19d0bb7a07427c4a8b/numpy-2.4.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c901b15172510173f5cb310eae652908340f8dede90fff9e3bf6c0d8dfd92f83", size = 16632682, upload-time = "2026-03-29T13:19:37.336Z" }, + { url = "https://files.pythonhosted.org/packages/34/fb/14570d65c3bde4e202a031210475ae9cde9b7686a2e7dc97ee67d2833b35/numpy-2.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:99d838547ace2c4aace6c4f76e879ddfe02bb58a80c1549928477862b7a6d6ed", size = 17019810, upload-time = "2026-03-29T13:19:40.963Z" }, + { url = "https://files.pythonhosted.org/packages/8a/77/2ba9d87081fd41f6d640c83f26fb7351e536b7ce6dd9061b6af5904e8e46/numpy-2.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0aec54fd785890ecca25a6003fd9a5aed47ad607bbac5cd64f836ad8666f4959", size = 18357394, upload-time = "2026-03-29T13:19:44.859Z" }, + { url = "https://files.pythonhosted.org/packages/a2/23/52666c9a41708b0853fa3b1a12c90da38c507a3074883823126d4e9d5b30/numpy-2.4.4-cp313-cp313-win32.whl", hash = "sha256:07077278157d02f65c43b1b26a3886bce886f95d20aabd11f87932750dfb14ed", size = 5959556, upload-time = "2026-03-29T13:19:47.661Z" }, + { url = "https://files.pythonhosted.org/packages/57/fb/48649b4971cde70d817cf97a2a2fdc0b4d8308569f1dd2f2611959d2e0cf/numpy-2.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:5c70f1cc1c4efbe316a572e2d8b9b9cc44e89b95f79ca3331553fbb63716e2bf", size = 12317311, upload-time = "2026-03-29T13:19:50.67Z" }, + { url = "https://files.pythonhosted.org/packages/ba/d8/11490cddd564eb4de97b4579ef6bfe6a736cc07e94c1598590ae25415e01/numpy-2.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:ef4059d6e5152fa1a39f888e344c73fdc926e1b2dd58c771d67b0acfbf2aa67d", size = 10222060, upload-time = "2026-03-29T13:19:54.229Z" }, + { url = "https://files.pythonhosted.org/packages/99/5d/dab4339177a905aad3e2221c915b35202f1ec30d750dd2e5e9d9a72b804b/numpy-2.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4bbc7f303d125971f60ec0aaad5e12c62d0d2c925f0ab1273debd0e4ba37aba5", size = 14822302, upload-time = "2026-03-29T13:19:57.585Z" }, + { url = "https://files.pythonhosted.org/packages/eb/e4/0564a65e7d3d97562ed6f9b0fd0fb0a6f559ee444092f105938b50043876/numpy-2.4.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:4d6d57903571f86180eb98f8f0c839fa9ebbfb031356d87f1361be91e433f5b7", size = 5327407, upload-time = "2026-03-29T13:20:00.601Z" }, + { url = "https://files.pythonhosted.org/packages/29/8d/35a3a6ce5ad371afa58b4700f1c820f8f279948cca32524e0a695b0ded83/numpy-2.4.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:4636de7fd195197b7535f231b5de9e4b36d2c440b6e566d2e4e4746e6af0ca93", size = 6647631, upload-time = "2026-03-29T13:20:02.855Z" }, + { url = "https://files.pythonhosted.org/packages/f4/da/477731acbd5a58a946c736edfdabb2ac5b34c3d08d1ba1a7b437fa0884df/numpy-2.4.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad2e2ef14e0b04e544ea2fa0a36463f847f113d314aa02e5b402fdf910ef309e", size = 15727691, upload-time = "2026-03-29T13:20:06.004Z" }, + { url = "https://files.pythonhosted.org/packages/e6/db/338535d9b152beabeb511579598418ba0212ce77cf9718edd70262cc4370/numpy-2.4.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a285b3b96f951841799528cd1f4f01cd70e7e0204b4abebac9463eecfcf2a40", size = 16681241, upload-time = "2026-03-29T13:20:09.417Z" }, + { url = "https://files.pythonhosted.org/packages/e2/a9/ad248e8f58beb7a0219b413c9c7d8151c5d285f7f946c3e26695bdbbe2df/numpy-2.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f8474c4241bc18b750be2abea9d7a9ec84f46ef861dbacf86a4f6e043401f79e", size = 17085767, upload-time = "2026-03-29T13:20:13.126Z" }, + { url = "https://files.pythonhosted.org/packages/b5/1a/3b88ccd3694681356f70da841630e4725a7264d6a885c8d442a697e1146b/numpy-2.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4e874c976154687c1f71715b034739b45c7711bec81db01914770373d125e392", size = 18403169, upload-time = "2026-03-29T13:20:17.096Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c9/fcfd5d0639222c6eac7f304829b04892ef51c96a75d479214d77e3ce6e33/numpy-2.4.4-cp313-cp313t-win32.whl", hash = "sha256:9c585a1790d5436a5374bac930dad6ed244c046ed91b2b2a3634eb2971d21008", size = 6083477, upload-time = "2026-03-29T13:20:20.195Z" }, + { url = "https://files.pythonhosted.org/packages/d5/e3/3938a61d1c538aaec8ed6fd6323f57b0c2d2d2219512434c5c878db76553/numpy-2.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:93e15038125dc1e5345d9b5b68aa7f996ec33b98118d18c6ca0d0b7d6198b7e8", size = 12457487, upload-time = "2026-03-29T13:20:22.946Z" }, + { url = "https://files.pythonhosted.org/packages/97/6a/7e345032cc60501721ef94e0e30b60f6b0bd601f9174ebd36389a2b86d40/numpy-2.4.4-cp313-cp313t-win_arm64.whl", hash = "sha256:0dfd3f9d3adbe2920b68b5cd3d51444e13a10792ec7154cd0a2f6e74d4ab3233", size = 10292002, upload-time = "2026-03-29T13:20:25.909Z" }, + { url = "https://files.pythonhosted.org/packages/6e/06/c54062f85f673dd5c04cbe2f14c3acb8c8b95e3384869bb8cc9bff8cb9df/numpy-2.4.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f169b9a863d34f5d11b8698ead99febeaa17a13ca044961aa8e2662a6c7766a0", size = 16684353, upload-time = "2026-03-29T13:20:29.504Z" }, + { url = "https://files.pythonhosted.org/packages/4c/39/8a320264a84404c74cc7e79715de85d6130fa07a0898f67fb5cd5bd79908/numpy-2.4.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2483e4584a1cb3092da4470b38866634bafb223cbcd551ee047633fd2584599a", size = 14704914, upload-time = "2026-03-29T13:20:33.547Z" }, + { url = "https://files.pythonhosted.org/packages/91/fb/287076b2614e1d1044235f50f03748f31fa287e3dbe6abeb35cdfa351eca/numpy-2.4.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:2d19e6e2095506d1736b7d80595e0f252d76b89f5e715c35e06e937679ea7d7a", size = 5210005, upload-time = "2026-03-29T13:20:36.45Z" }, + { url = "https://files.pythonhosted.org/packages/63/eb/fcc338595309910de6ecabfcef2419a9ce24399680bfb149421fa2df1280/numpy-2.4.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:6a246d5914aa1c820c9443ddcee9c02bec3e203b0c080349533fae17727dfd1b", size = 6544974, upload-time = "2026-03-29T13:20:39.014Z" }, + { url = "https://files.pythonhosted.org/packages/44/5d/e7e9044032a716cdfaa3fba27a8e874bf1c5f1912a1ddd4ed071bf8a14a6/numpy-2.4.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:989824e9faf85f96ec9c7761cd8d29c531ad857bfa1daa930cba85baaecf1a9a", size = 15684591, upload-time = "2026-03-29T13:20:42.146Z" }, + { url = "https://files.pythonhosted.org/packages/98/7c/21252050676612625449b4807d6b695b9ce8a7c9e1c197ee6216c8a65c7c/numpy-2.4.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:27a8d92cd10f1382a67d7cf4db7ce18341b66438bdd9f691d7b0e48d104c2a9d", size = 16637700, upload-time = "2026-03-29T13:20:46.204Z" }, + { url = "https://files.pythonhosted.org/packages/b1/29/56d2bbef9465db24ef25393383d761a1af4f446a1df9b8cded4fe3a5a5d7/numpy-2.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e44319a2953c738205bf3354537979eaa3998ed673395b964c1176083dd46252", size = 17035781, upload-time = "2026-03-29T13:20:50.242Z" }, + { url = "https://files.pythonhosted.org/packages/e3/2b/a35a6d7589d21f44cea7d0a98de5ddcbb3d421b2622a5c96b1edf18707c3/numpy-2.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e892aff75639bbef0d2a2cfd55535510df26ff92f63c92cd84ef8d4ba5a5557f", size = 18362959, upload-time = "2026-03-29T13:20:54.019Z" }, + { url = "https://files.pythonhosted.org/packages/64/c9/d52ec581f2390e0f5f85cbfd80fb83d965fc15e9f0e1aec2195faa142cde/numpy-2.4.4-cp314-cp314-win32.whl", hash = "sha256:1378871da56ca8943c2ba674530924bb8ca40cd228358a3b5f302ad60cf875fc", size = 6008768, upload-time = "2026-03-29T13:20:56.912Z" }, + { url = "https://files.pythonhosted.org/packages/fa/22/4cc31a62a6c7b74a8730e31a4274c5dc80e005751e277a2ce38e675e4923/numpy-2.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:715d1c092715954784bc79e1174fc2a90093dc4dc84ea15eb14dad8abdcdeb74", size = 12449181, upload-time = "2026-03-29T13:20:59.548Z" }, + { url = "https://files.pythonhosted.org/packages/70/2e/14cda6f4d8e396c612d1bf97f22958e92148801d7e4f110cabebdc0eef4b/numpy-2.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:2c194dd721e54ecad9ad387c1d35e63dce5c4450c6dc7dd5611283dda239aabb", size = 10496035, upload-time = "2026-03-29T13:21:02.524Z" }, + { url = "https://files.pythonhosted.org/packages/b1/e8/8fed8c8d848d7ecea092dc3469643f9d10bc3a134a815a3b033da1d2039b/numpy-2.4.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2aa0613a5177c264ff5921051a5719d20095ea586ca88cc802c5c218d1c67d3e", size = 14824958, upload-time = "2026-03-29T13:21:05.671Z" }, + { url = "https://files.pythonhosted.org/packages/05/1a/d8007a5138c179c2bf33ef44503e83d70434d2642877ee8fbb230e7c0548/numpy-2.4.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:42c16925aa5a02362f986765f9ebabf20de75cdefdca827d14315c568dcab113", size = 5330020, upload-time = "2026-03-29T13:21:08.635Z" }, + { url = "https://files.pythonhosted.org/packages/99/64/ffb99ac6ae93faf117bcbd5c7ba48a7f45364a33e8e458545d3633615dda/numpy-2.4.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:874f200b2a981c647340f841730fc3a2b54c9d940566a3c4149099591e2c4c3d", size = 6650758, upload-time = "2026-03-29T13:21:10.949Z" }, + { url = "https://files.pythonhosted.org/packages/6e/6e/795cc078b78a384052e73b2f6281ff7a700e9bf53bcce2ee579d4f6dd879/numpy-2.4.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9b39d38a9bd2ae1becd7eac1303d031c5c110ad31f2b319c6e7d98b135c934d", size = 15729948, upload-time = "2026-03-29T13:21:14.047Z" }, + { url = "https://files.pythonhosted.org/packages/5f/86/2acbda8cc2af5f3d7bfc791192863b9e3e19674da7b5e533fded124d1299/numpy-2.4.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b268594bccac7d7cf5844c7732e3f20c50921d94e36d7ec9b79e9857694b1b2f", size = 16679325, upload-time = "2026-03-29T13:21:17.561Z" }, + { url = "https://files.pythonhosted.org/packages/bc/59/cafd83018f4aa55e0ac6fa92aa066c0a1877b77a615ceff1711c260ffae8/numpy-2.4.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ac6b31e35612a26483e20750126d30d0941f949426974cace8e6b5c58a3657b0", size = 17084883, upload-time = "2026-03-29T13:21:21.106Z" }, + { url = "https://files.pythonhosted.org/packages/f0/85/a42548db84e65ece46ab2caea3d3f78b416a47af387fcbb47ec28e660dc2/numpy-2.4.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8e3ed142f2728df44263aaf5fb1f5b0b99f4070c553a0d7f033be65338329150", size = 18403474, upload-time = "2026-03-29T13:21:24.828Z" }, + { url = "https://files.pythonhosted.org/packages/ed/ad/483d9e262f4b831000062e5d8a45e342166ec8aaa1195264982bca267e62/numpy-2.4.4-cp314-cp314t-win32.whl", hash = "sha256:dddbbd259598d7240b18c9d87c56a9d2fb3b02fe266f49a7c101532e78c1d871", size = 6155500, upload-time = "2026-03-29T13:21:28.205Z" }, + { url = "https://files.pythonhosted.org/packages/c7/03/2fc4e14c7bd4ff2964b74ba90ecb8552540b6315f201df70f137faa5c589/numpy-2.4.4-cp314-cp314t-win_amd64.whl", hash = "sha256:a7164afb23be6e37ad90b2f10426149fd75aee07ca55653d2aa41e66c4ef697e", size = 12637755, upload-time = "2026-03-29T13:21:31.107Z" }, + { url = "https://files.pythonhosted.org/packages/58/78/548fb8e07b1a341746bfbecb32f2c268470f45fa028aacdbd10d9bc73aab/numpy-2.4.4-cp314-cp314t-win_arm64.whl", hash = "sha256:ba203255017337d39f89bdd58417f03c4426f12beed0440cfd933cb15f8669c7", size = 10566643, upload-time = "2026-03-29T13:21:34.339Z" }, + { url = "https://files.pythonhosted.org/packages/6b/33/8fae8f964a4f63ed528264ddf25d2b683d0b663e3cba26961eb838a7c1bd/numpy-2.4.4-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:58c8b5929fcb8287cbd6f0a3fae19c6e03a5c48402ae792962ac465224a629a4", size = 16854491, upload-time = "2026-03-29T13:21:38.03Z" }, + { url = "https://files.pythonhosted.org/packages/bc/d0/1aabee441380b981cf8cdda3ae7a46aa827d1b5a8cce84d14598bc94d6d9/numpy-2.4.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:eea7ac5d2dce4189771cedb559c738a71512768210dc4e4753b107a2048b3d0e", size = 14895830, upload-time = "2026-03-29T13:21:41.509Z" }, + { url = "https://files.pythonhosted.org/packages/a5/b8/aafb0d1065416894fccf4df6b49ef22b8db045187949545bced89c034b8e/numpy-2.4.4-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:51fc224f7ca4d92656d5a5eb315f12eb5fe2c97a66249aa7b5f562528a3be38c", size = 5400927, upload-time = "2026-03-29T13:21:44.747Z" }, + { url = "https://files.pythonhosted.org/packages/d6/77/063baa20b08b431038c7f9ff5435540c7b7265c78cf56012a483019ca72d/numpy-2.4.4-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:28a650663f7314afc3e6ec620f44f333c386aad9f6fc472030865dc0ebb26ee3", size = 6715557, upload-time = "2026-03-29T13:21:47.406Z" }, + { url = "https://files.pythonhosted.org/packages/c7/a8/379542d45a14f149444c5c4c4e7714707239ce9cc1de8c2803958889da14/numpy-2.4.4-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:19710a9ca9992d7174e9c52f643d4272dcd1558c5f7af7f6f8190f633bd651a7", size = 15804253, upload-time = "2026-03-29T13:21:50.753Z" }, + { url = "https://files.pythonhosted.org/packages/a2/c8/f0a45426d6d21e7ea3310a15cf90c43a14d9232c31a837702dba437f3373/numpy-2.4.4-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b2aec6af35c113b05695ebb5749a787acd63cafc83086a05771d1e1cd1e555f", size = 16753552, upload-time = "2026-03-29T13:21:54.344Z" }, + { url = "https://files.pythonhosted.org/packages/04/74/f4c001f4714c3ad9ce037e18cf2b9c64871a84951eaa0baf683a9ca9301c/numpy-2.4.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f2cf083b324a467e1ab358c105f6cad5ea950f50524668a80c486ff1db24e119", size = 12509075, upload-time = "2026-03-29T13:21:57.644Z" }, +] + +[[package]] +name = "nvidia-cublas" +version = "13.1.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cuda-nvrtc", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/a1/0bd24ee8c8d03adac032fd2909426a00c88f8c57961b1277ded97f91119f/nvidia_cublas-13.1.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b7a210458267ac818974c53038fbec2e969d5c99f305ab15c72522fa9f001dd5", size = 542848918, upload-time = "2026-04-08T18:46:22.985Z" }, + { url = "https://files.pythonhosted.org/packages/3b/cd/154ca20c38269e05eff77c1464e6c1da89f50a6390b565e9d82e06bc11e1/nvidia_cublas-13.1.1.3-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:37936a16db8fe4ac1f065c2139360608a543a09275cb1a1af612e08cfa065436", size = 423138758, upload-time = "2026-04-08T18:46:58.655Z" }, +] + +[[package]] +name = "nvidia-cuda-cupti" +version = "13.0.85" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/2a/80353b103fc20ce05ef51e928daed4b6015db4aaa9162ed0997090fe2250/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_aarch64.whl", hash = "sha256:796bd679890ee55fb14a94629b698b6db54bcfd833d391d5e94017dd9d7d3151", size = 10310827, upload-time = "2025-09-04T08:26:42.012Z" }, + { url = "https://files.pythonhosted.org/packages/33/6d/737d164b4837a9bbd202f5ae3078975f0525a55730fe871d8ed4e3b952b0/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_x86_64.whl", hash = "sha256:4eb01c08e859bf924d222250d2e8f8b8ff6d3db4721288cf35d14252a4d933c8", size = 10715597, upload-time = "2025-09-04T08:26:51.312Z" }, +] + +[[package]] +name = "nvidia-cuda-nvrtc" +version = "13.0.88" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/68/483a78f5e8f31b08fb1bb671559968c0ca3a065ac7acabfc7cee55214fd6/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:ad9b6d2ead2435f11cbb6868809d2adeeee302e9bb94bcf0539c7a40d80e8575", size = 90215200, upload-time = "2025-09-04T08:28:44.204Z" }, + { url = "https://files.pythonhosted.org/packages/b7/dc/6bb80850e0b7edd6588d560758f17e0550893a1feaf436807d64d2da040f/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d27f20a0ca67a4bb34268a5e951033496c5b74870b868bacd046b1b8e0c3267b", size = 43015449, upload-time = "2025-09-04T08:28:20.239Z" }, +] + +[[package]] +name = "nvidia-cuda-runtime" +version = "13.0.96" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/4f/17d7b9b8e285199c58ce28e31b5c5bbaa4d8271af06a89b6405258245de2/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ef9bcbe90493a2b9d810e43d249adb3d02e98dd30200d86607d8d02687c43f55", size = 2261060, upload-time = "2025-10-09T08:55:15.78Z" }, + { url = "https://files.pythonhosted.org/packages/2e/24/d1558f3b68b1d26e706813b1d10aa1d785e4698c425af8db8edc3dced472/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f82250d7782aa23b6cfe765ecc7db554bd3c2870c43f3d1821f1d18aebf0548", size = 2243632, upload-time = "2025-10-09T08:55:36.117Z" }, +] + +[[package]] +name = "nvidia-cudnn-cu13" +version = "9.20.0.48" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/c5/83384d846b2fd17c44bd499b36c75a45ed4f095fbbb2252294e89cea5c5c/nvidia_cudnn_cu13-9.20.0.48-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:e31454ae00094b0c55319d9d15b6fa2fc50a9e1c0f5c8c80fb75258234e731e1", size = 444574296, upload-time = "2026-03-09T19:28:27.751Z" }, + { url = "https://files.pythonhosted.org/packages/6e/5e/edb9c0ae051602c3ccaffe424256463636d639e27d7f302dde9975ef9e7a/nvidia_cudnn_cu13-9.20.0.48-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0c45dd8eeb50b603f07995b1b300c62ffe6a1980482b82b3bcf94a4ca9d49304", size = 366173588, upload-time = "2026-03-09T19:29:34.474Z" }, +] + +[[package]] +name = "nvidia-cufft" +version = "12.0.0.61" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554, upload-time = "2025-09-04T08:31:38.196Z" }, + { url = "https://files.pythonhosted.org/packages/a8/2f/7b57e29836ea8714f81e9898409196f47d772d5ddedddf1592eadb8ab743/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6c44f692dce8fd5ffd3e3df134b6cdb9c2f72d99cf40b62c32dde45eea9ddad3", size = 214085489, upload-time = "2025-09-04T08:31:56.044Z" }, +] + +[[package]] +name = "nvidia-cufile" +version = "1.15.1.6" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/70/4f193de89a48b71714e74602ee14d04e4019ad36a5a9f20c425776e72cd6/nvidia_cufile-1.15.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08a3ecefae5a01c7f5117351c64f17c7c62efa5fffdbe24fc7d298da19cd0b44", size = 1223672, upload-time = "2025-09-04T08:32:22.779Z" }, + { url = "https://files.pythonhosted.org/packages/ab/73/cc4a14c9813a8a0d509417cf5f4bdaba76e924d58beb9864f5a7baceefbf/nvidia_cufile-1.15.1.6-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:bdc0deedc61f548bddf7733bdc216456c2fdb101d020e1ab4b88d232d5e2f6d1", size = 1136992, upload-time = "2025-09-04T08:32:14.119Z" }, +] + +[[package]] +name = "nvidia-curand" +version = "10.4.0.35" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/72/7c2ae24fb6b63a32e6ae5d241cc65263ea18d08802aaae087d9f013335a2/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:133df5a7509c3e292aaa2b477afd0194f06ce4ea24d714d616ff36439cee349a", size = 61962106, upload-time = "2025-08-04T10:21:41.128Z" }, + { url = "https://files.pythonhosted.org/packages/a5/9f/be0a41ca4a4917abf5cb9ae0daff1a6060cc5de950aec0396de9f3b52bc5/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:1aee33a5da6e1db083fe2b90082def8915f30f3248d5896bcec36a579d941bfc", size = 59544258, upload-time = "2025-08-04T10:22:03.992Z" }, +] + +[[package]] +name = "nvidia-cusolver" +version = "12.0.4.66" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { name = "nvidia-cusparse", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { name = "nvidia-nvjitlink", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760, upload-time = "2025-09-04T08:33:04.222Z" }, + { url = "https://files.pythonhosted.org/packages/5f/67/cba3777620cdacb99102da4042883709c41c709f4b6323c10781a9c3aa34/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0a759da5dea5c0ea10fd307de75cdeb59e7ea4fcb8add0924859b944babf1112", size = 200941980, upload-time = "2025-09-04T08:33:22.767Z" }, +] + +[[package]] +name = "nvidia-cusparse" +version = "12.6.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568, upload-time = "2025-09-04T08:33:42.864Z" }, + { url = "https://files.pythonhosted.org/packages/fa/18/623c77619c31d62efd55302939756966f3ecc8d724a14dab2b75f1508850/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b3c89c88d01ee0e477cb7f82ef60a11a4bcd57b6b87c33f789350b59759360b", size = 145942937, upload-time = "2025-09-04T08:33:58.029Z" }, +] + +[[package]] +name = "nvidia-cusparselt-cu13" +version = "0.8.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/e1/cdc1797eadf82d3a9a575a19b33fdc871a97edbec42c00b5b5e914f4aff4/nvidia_cusparselt_cu13-0.8.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:4dca476c50bf4780d46cd0bfbd82e2bc10a08e4fef7950917ce8d7578d22a23f", size = 221051344, upload-time = "2025-09-05T18:49:51.289Z" }, + { url = "https://files.pythonhosted.org/packages/34/7d/2661f2fb3ac4302f3a246f5fc030213ac60c1fe0bce84f9783dbd831dbb7/nvidia_cusparselt_cu13-0.8.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:786ce87568c303fadb5afcc7102d454cd3040d75f6f8626f5db460d1871f4dd0", size = 170148586, upload-time = "2025-09-05T18:50:50.248Z" }, +] + +[[package]] +name = "nvidia-nccl-cu13" +version = "2.29.7" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/72/0d/daf50d44177ee0cbc7ff0a0c91eb5ff676c82be42f9a970bc7597f440c3a/nvidia_nccl_cu13-2.29.7-py3-none-manylinux_2_18_aarch64.whl", hash = "sha256:674a12383e3c38a1bcccae7d4f3633b37852230b6047883cb2f4c2d1b36d9bf5", size = 206014712, upload-time = "2026-03-03T05:34:20.843Z" }, + { url = "https://files.pythonhosted.org/packages/67/f4/58e4e91b6919367c7aafb8e36fce9aad1a3047e536bf7e2fd560927d3a4c/nvidia_nccl_cu13-2.29.7-py3-none-manylinux_2_18_x86_64.whl", hash = "sha256:edd81538446786ec3b73972543e53bb43bcaf0bfc8ef76cb679fcc390ffe136d", size = 205976000, upload-time = "2026-03-03T05:36:24.472Z" }, +] + +[[package]] +name = "nvidia-nvjitlink" +version = "13.0.88" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/7a/123e033aaff487c77107195fa5a2b8686795ca537935a24efae476c41f05/nvidia_nvjitlink-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:13a74f429e23b921c1109976abefacc69835f2f433ebd323d3946e11d804e47b", size = 40713933, upload-time = "2025-09-04T08:35:43.553Z" }, + { url = "https://files.pythonhosted.org/packages/ab/2c/93c5250e64df4f894f1cbb397c6fd71f79813f9fd79d7cd61de3f97b3c2d/nvidia_nvjitlink-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e931536ccc7d467a98ba1d8b89ff7fa7f1fa3b13f2b0069118cd7f47bff07d0c", size = 38768748, upload-time = "2025-09-04T08:35:20.008Z" }, +] + +[[package]] +name = "nvidia-nvshmem-cu13" +version = "3.4.5" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/0f/05cc9c720236dcd2db9c1ab97fff629e96821be2e63103569da0c9b72f19/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dc2a197f38e5d0376ad52cd1a2a3617d3cdc150fd5966f4aee9bcebb1d68fe9", size = 60215947, upload-time = "2025-09-06T00:32:20.022Z" }, + { url = "https://files.pythonhosted.org/packages/3c/35/a9bf80a609e74e3b000fef598933235c908fcefcef9026042b8e6dfde2a9/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:290f0a2ee94c9f3687a02502f3b9299a9f9fe826e6d0287ee18482e78d495b80", size = 60412546, upload-time = "2025-09-06T00:32:41.564Z" }, +] + +[[package]] +name = "nvidia-nvtx" +version = "13.0.85" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/f3/d86c845465a2723ad7e1e5c36dcd75ddb82898b3f53be47ebd429fb2fa5d/nvidia_nvtx-13.0.85-py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4936d1d6780fbe68db454f5e72a42ff64d1fd6397df9f363ae786930fd5c1cd4", size = 148047, upload-time = "2025-09-04T08:29:01.761Z" }, + { url = "https://files.pythonhosted.org/packages/a8/64/3708a90d1ebe202ffdeb7185f878a3c84d15c2b2c31858da2ce0583e2def/nvidia_nvtx-13.0.85-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb7780edb6b14107373c835bf8b72e7a178bac7367e23da7acb108f973f157a6", size = 148878, upload-time = "2025-09-04T08:28:53.627Z" }, +] + +[[package]] +name = "opt-einsum" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/b9/2ac072041e899a52f20cf9510850ff58295003aa75525e58343591b0cbfb/opt_einsum-3.4.0.tar.gz", hash = "sha256:96ca72f1b886d148241348783498194c577fa30a8faac108586b14f1ba4473ac", size = 63004, upload-time = "2024-09-26T14:33:24.483Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/23/cd/066e86230ae37ed0be70aae89aabf03ca8d9f39c8aea0dec8029455b5540/opt_einsum-3.4.0-py3-none-any.whl", hash = "sha256:69bb92469f86a1565195ece4ac0323943e83477171b91d24c35afe028a90d7cd", size = 71932, upload-time = "2024-09-26T14:33:23.039Z" }, +] + +[[package]] +name = "packaging" +version = "26.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, +] + +[[package]] +name = "pandas" +version = "2.3.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "python-dateutil", marker = "python_full_version < '3.11'" }, + { name = "pytz", marker = "python_full_version < '3.11'" }, + { name = "tzdata", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/f7/f425a00df4fcc22b292c6895c6831c0c8ae1d9fac1e024d16f98a9ce8749/pandas-2.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:376c6446ae31770764215a6c937f72d917f214b43560603cd60da6408f183b6c", size = 11555763, upload-time = "2025-09-29T23:16:53.287Z" }, + { url = "https://files.pythonhosted.org/packages/13/4f/66d99628ff8ce7857aca52fed8f0066ce209f96be2fede6cef9f84e8d04f/pandas-2.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e19d192383eab2f4ceb30b412b22ea30690c9e618f78870357ae1d682912015a", size = 10801217, upload-time = "2025-09-29T23:17:04.522Z" }, + { url = "https://files.pythonhosted.org/packages/1d/03/3fc4a529a7710f890a239cc496fc6d50ad4a0995657dccc1d64695adb9f4/pandas-2.3.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5caf26f64126b6c7aec964f74266f435afef1c1b13da3b0636c7518a1fa3e2b1", size = 12148791, upload-time = "2025-09-29T23:17:18.444Z" }, + { url = "https://files.pythonhosted.org/packages/40/a8/4dac1f8f8235e5d25b9955d02ff6f29396191d4e665d71122c3722ca83c5/pandas-2.3.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd7478f1463441ae4ca7308a70e90b33470fa593429f9d4c578dd00d1fa78838", size = 12769373, upload-time = "2025-09-29T23:17:35.846Z" }, + { url = "https://files.pythonhosted.org/packages/df/91/82cc5169b6b25440a7fc0ef3a694582418d875c8e3ebf796a6d6470aa578/pandas-2.3.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4793891684806ae50d1288c9bae9330293ab4e083ccd1c5e383c34549c6e4250", size = 13200444, upload-time = "2025-09-29T23:17:49.341Z" }, + { url = "https://files.pythonhosted.org/packages/10/ae/89b3283800ab58f7af2952704078555fa60c807fff764395bb57ea0b0dbd/pandas-2.3.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28083c648d9a99a5dd035ec125d42439c6c1c525098c58af0fc38dd1a7a1b3d4", size = 13858459, upload-time = "2025-09-29T23:18:03.722Z" }, + { url = "https://files.pythonhosted.org/packages/85/72/530900610650f54a35a19476eca5104f38555afccda1aa11a92ee14cb21d/pandas-2.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:503cf027cf9940d2ceaa1a93cfb5f8c8c7e6e90720a2850378f0b3f3b1e06826", size = 11346086, upload-time = "2025-09-29T23:18:18.505Z" }, + { url = "https://files.pythonhosted.org/packages/c1/fa/7ac648108144a095b4fb6aa3de1954689f7af60a14cf25583f4960ecb878/pandas-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:602b8615ebcc4a0c1751e71840428ddebeb142ec02c786e8ad6b1ce3c8dec523", size = 11578790, upload-time = "2025-09-29T23:18:30.065Z" }, + { url = "https://files.pythonhosted.org/packages/9b/35/74442388c6cf008882d4d4bdfc4109be87e9b8b7ccd097ad1e7f006e2e95/pandas-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8fe25fc7b623b0ef6b5009149627e34d2a4657e880948ec3c840e9402e5c1b45", size = 10833831, upload-time = "2025-09-29T23:38:56.071Z" }, + { url = "https://files.pythonhosted.org/packages/fe/e4/de154cbfeee13383ad58d23017da99390b91d73f8c11856f2095e813201b/pandas-2.3.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b468d3dad6ff947df92dcb32ede5b7bd41a9b3cceef0a30ed925f6d01fb8fa66", size = 12199267, upload-time = "2025-09-29T23:18:41.627Z" }, + { url = "https://files.pythonhosted.org/packages/bf/c9/63f8d545568d9ab91476b1818b4741f521646cbdd151c6efebf40d6de6f7/pandas-2.3.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b98560e98cb334799c0b07ca7967ac361a47326e9b4e5a7dfb5ab2b1c9d35a1b", size = 12789281, upload-time = "2025-09-29T23:18:56.834Z" }, + { url = "https://files.pythonhosted.org/packages/f2/00/a5ac8c7a0e67fd1a6059e40aa08fa1c52cc00709077d2300e210c3ce0322/pandas-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37b5848ba49824e5c30bedb9c830ab9b7751fd049bc7914533e01c65f79791", size = 13240453, upload-time = "2025-09-29T23:19:09.247Z" }, + { url = "https://files.pythonhosted.org/packages/27/4d/5c23a5bc7bd209231618dd9e606ce076272c9bc4f12023a70e03a86b4067/pandas-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db4301b2d1f926ae677a751eb2bd0e8c5f5319c9cb3f88b0becbbb0b07b34151", size = 13890361, upload-time = "2025-09-29T23:19:25.342Z" }, + { url = "https://files.pythonhosted.org/packages/8e/59/712db1d7040520de7a4965df15b774348980e6df45c129b8c64d0dbe74ef/pandas-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:f086f6fe114e19d92014a1966f43a3e62285109afe874f067f5abbdcbb10e59c", size = 11348702, upload-time = "2025-09-29T23:19:38.296Z" }, + { url = "https://files.pythonhosted.org/packages/9c/fb/231d89e8637c808b997d172b18e9d4a4bc7bf31296196c260526055d1ea0/pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53", size = 11597846, upload-time = "2025-09-29T23:19:48.856Z" }, + { url = "https://files.pythonhosted.org/packages/5c/bd/bf8064d9cfa214294356c2d6702b716d3cf3bb24be59287a6a21e24cae6b/pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35", size = 10729618, upload-time = "2025-09-29T23:39:08.659Z" }, + { url = "https://files.pythonhosted.org/packages/57/56/cf2dbe1a3f5271370669475ead12ce77c61726ffd19a35546e31aa8edf4e/pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908", size = 11737212, upload-time = "2025-09-29T23:19:59.765Z" }, + { url = "https://files.pythonhosted.org/packages/e5/63/cd7d615331b328e287d8233ba9fdf191a9c2d11b6af0c7a59cfcec23de68/pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89", size = 12362693, upload-time = "2025-09-29T23:20:14.098Z" }, + { url = "https://files.pythonhosted.org/packages/a6/de/8b1895b107277d52f2b42d3a6806e69cfef0d5cf1d0ba343470b9d8e0a04/pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98", size = 12771002, upload-time = "2025-09-29T23:20:26.76Z" }, + { url = "https://files.pythonhosted.org/packages/87/21/84072af3187a677c5893b170ba2c8fbe450a6ff911234916da889b698220/pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084", size = 13450971, upload-time = "2025-09-29T23:20:41.344Z" }, + { url = "https://files.pythonhosted.org/packages/86/41/585a168330ff063014880a80d744219dbf1dd7a1c706e75ab3425a987384/pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b", size = 10992722, upload-time = "2025-09-29T23:20:54.139Z" }, + { url = "https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713", size = 11544671, upload-time = "2025-09-29T23:21:05.024Z" }, + { url = "https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8", size = 10680807, upload-time = "2025-09-29T23:21:15.979Z" }, + { url = "https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d", size = 11709872, upload-time = "2025-09-29T23:21:27.165Z" }, + { url = "https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac", size = 12306371, upload-time = "2025-09-29T23:21:40.532Z" }, + { url = "https://files.pythonhosted.org/packages/33/81/a3afc88fca4aa925804a27d2676d22dcd2031c2ebe08aabd0ae55b9ff282/pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c", size = 12765333, upload-time = "2025-09-29T23:21:55.77Z" }, + { url = "https://files.pythonhosted.org/packages/8d/0f/b4d4ae743a83742f1153464cf1a8ecfafc3ac59722a0b5c8602310cb7158/pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493", size = 13418120, upload-time = "2025-09-29T23:22:10.109Z" }, + { url = "https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee", size = 10993991, upload-time = "2025-09-29T23:25:04.889Z" }, + { url = "https://files.pythonhosted.org/packages/f9/ca/3f8d4f49740799189e1395812f3bf23b5e8fc7c190827d55a610da72ce55/pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5", size = 12048227, upload-time = "2025-09-29T23:22:24.343Z" }, + { url = "https://files.pythonhosted.org/packages/0e/5a/f43efec3e8c0cc92c4663ccad372dbdff72b60bdb56b2749f04aa1d07d7e/pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21", size = 11411056, upload-time = "2025-09-29T23:22:37.762Z" }, + { url = "https://files.pythonhosted.org/packages/46/b1/85331edfc591208c9d1a63a06baa67b21d332e63b7a591a5ba42a10bb507/pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78", size = 11645189, upload-time = "2025-09-29T23:22:51.688Z" }, + { url = "https://files.pythonhosted.org/packages/44/23/78d645adc35d94d1ac4f2a3c4112ab6f5b8999f4898b8cdf01252f8df4a9/pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110", size = 12121912, upload-time = "2025-09-29T23:23:05.042Z" }, + { url = "https://files.pythonhosted.org/packages/53/da/d10013df5e6aaef6b425aa0c32e1fc1f3e431e4bcabd420517dceadce354/pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86", size = 12712160, upload-time = "2025-09-29T23:23:28.57Z" }, + { url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233, upload-time = "2025-09-29T23:24:24.876Z" }, + { url = "https://files.pythonhosted.org/packages/04/fd/74903979833db8390b73b3a8a7d30d146d710bd32703724dd9083950386f/pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0", size = 11540635, upload-time = "2025-09-29T23:25:52.486Z" }, + { url = "https://files.pythonhosted.org/packages/21/00/266d6b357ad5e6d3ad55093a7e8efc7dd245f5a842b584db9f30b0f0a287/pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593", size = 10759079, upload-time = "2025-09-29T23:26:33.204Z" }, + { url = "https://files.pythonhosted.org/packages/ca/05/d01ef80a7a3a12b2f8bbf16daba1e17c98a2f039cbc8e2f77a2c5a63d382/pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c", size = 11814049, upload-time = "2025-09-29T23:27:15.384Z" }, + { url = "https://files.pythonhosted.org/packages/15/b2/0e62f78c0c5ba7e3d2c5945a82456f4fac76c480940f805e0b97fcbc2f65/pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b", size = 12332638, upload-time = "2025-09-29T23:27:51.625Z" }, + { url = "https://files.pythonhosted.org/packages/c5/33/dd70400631b62b9b29c3c93d2feee1d0964dc2bae2e5ad7a6c73a7f25325/pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6", size = 12886834, upload-time = "2025-09-29T23:28:21.289Z" }, + { url = "https://files.pythonhosted.org/packages/d3/18/b5d48f55821228d0d2692b34fd5034bb185e854bdb592e9c640f6290e012/pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3", size = 13409925, upload-time = "2025-09-29T23:28:58.261Z" }, + { url = "https://files.pythonhosted.org/packages/a6/3d/124ac75fcd0ecc09b8fdccb0246ef65e35b012030defb0e0eba2cbbbe948/pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5", size = 11109071, upload-time = "2025-09-29T23:32:27.484Z" }, + { url = "https://files.pythonhosted.org/packages/89/9c/0e21c895c38a157e0faa1fb64587a9226d6dd46452cac4532d80c3c4a244/pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec", size = 12048504, upload-time = "2025-09-29T23:29:31.47Z" }, + { url = "https://files.pythonhosted.org/packages/d7/82/b69a1c95df796858777b68fbe6a81d37443a33319761d7c652ce77797475/pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7", size = 11410702, upload-time = "2025-09-29T23:29:54.591Z" }, + { url = "https://files.pythonhosted.org/packages/f9/88/702bde3ba0a94b8c73a0181e05144b10f13f29ebfc2150c3a79062a8195d/pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450", size = 11634535, upload-time = "2025-09-29T23:30:21.003Z" }, + { url = "https://files.pythonhosted.org/packages/a4/1e/1bac1a839d12e6a82ec6cb40cda2edde64a2013a66963293696bbf31fbbb/pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5", size = 12121582, upload-time = "2025-09-29T23:30:43.391Z" }, + { url = "https://files.pythonhosted.org/packages/44/91/483de934193e12a3b1d6ae7c8645d083ff88dec75f46e827562f1e4b4da6/pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788", size = 12699963, upload-time = "2025-09-29T23:31:10.009Z" }, + { url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175, upload-time = "2025-09-29T23:31:59.173Z" }, +] + +[[package]] +name = "pandas" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "python-dateutil", marker = "python_full_version >= '3.11'" }, + { name = "tzdata", marker = "(python_full_version >= '3.11' and sys_platform == 'emscripten') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f8/87/4341c6252d1c47b08768c3d25ac487362bf403f0313ddae4a2a26c9b1b4c/pandas-3.0.3.tar.gz", hash = "sha256:696a4a00a2a2a35d4e5deb3fc946641b96c944f02230e4f76137fe35d806c4fc", size = 4651414, upload-time = "2026-05-11T18:54:29.21Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/16/b5c76b838fd9bf6ce84d3a53346b8874ec05c5f0040d75ef2c320100cd2a/pandas-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:455f6f8139d4282188f526868dbc3c828470e88a3d9d59a891bd46a455f21b98", size = 10338495, upload-time = "2026-05-11T18:52:11.558Z" }, + { url = "https://files.pythonhosted.org/packages/5a/b0/a4ffc4ae74d2d822200dcc46898987d8eb6032d1e2b219cae39da6f5cbcc/pandas-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4e15135e2ee5df1063313e2425ceef8ac0f4ae775893815b0923651b806a5639", size = 9938250, upload-time = "2026-05-11T18:52:17.005Z" }, + { url = "https://files.pythonhosted.org/packages/2e/b2/3323601a52caee42c019e370090ca4544b241437240ca04f786cce82b0cf/pandas-3.0.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:05f1f1752b8533ea03f7f39a9c15b1a058d067bb48f4748948e7a8691e0510f2", size = 10770558, upload-time = "2026-05-11T18:52:19.865Z" }, + { url = "https://files.pythonhosted.org/packages/32/f1/bbecd2f867b97abebe0f9b53d750f862251b40337e061b36676ded3d920f/pandas-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a1e45c80cceb3b4a21bc5939d52e8cbd8d9b7305309219d59e9754d9ce09e27", size = 11274611, upload-time = "2026-05-11T18:52:22.622Z" }, + { url = "https://files.pythonhosted.org/packages/7f/4f/eafabf2d5fae5adf143b4d18d3706c5efdc368a7c4eb1ee8a3eddabbd0f6/pandas-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:14da8316da4d0c5a77618425996bfb1248ca87fc2c1486e6fde4652bd18b5824", size = 11784670, upload-time = "2026-05-11T18:52:25.4Z" }, + { url = "https://files.pythonhosted.org/packages/49/44/1eb20389301b57b19cc099a1c2f662501f72f08a65f912d05822613c1532/pandas-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a55066a0505dae0ba2b50a46637db34b46f9094c65c5d4800794ef6335010938", size = 12353708, upload-time = "2026-05-11T18:52:28.139Z" }, + { url = "https://files.pythonhosted.org/packages/eb/62/c321f13b5ba1819fc8dca456c7fce578da2dcfecff1abbf0eaddf8406c0f/pandas-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:6674ab18ad8c57802867264b00e15e7bb904700cdd9046e3b2fa1fce237439ea", size = 9907609, upload-time = "2026-05-11T18:52:30.982Z" }, + { url = "https://files.pythonhosted.org/packages/53/85/1b7f563ebc6357c27233a02a96b589bcce1fa9c6eb89fb4f0e56421d277e/pandas-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:5cc09a68b3120e0f54870dede8287a7bb1fa463907e4fcec1ea77cab6179bf7a", size = 9165596, upload-time = "2026-05-11T18:52:33.334Z" }, + { url = "https://files.pythonhosted.org/packages/24/f1/392f8c5bfc16f66a0d2d41561c01627c228fe7ed2a0d056ef11315042570/pandas-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fed2ff7fd9779120e388e285fc029bd5cf9490cdd2e4166a9ee22c0e49a9ab09", size = 10357846, upload-time = "2026-05-11T18:52:36.143Z" }, + { url = "https://files.pythonhosted.org/packages/cf/3d/b16412745651e855f357e5e66930248688378853a6e2698a214e331fba1f/pandas-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b168fc218fd80a6cbdbdbc1a97ddc7889ed057d7eb45f50d866ceab5f39904c4", size = 9899550, upload-time = "2026-05-11T18:52:38.976Z" }, + { url = "https://files.pythonhosted.org/packages/31/a8/fa2535168fffcedf67f4f6de28d2dd903a747ca7c8ea6989451aaeb3a92f/pandas-3.0.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0383c72c75cdcca61a9e116e611143902dbfd08bff356829c2f6d1cf40a9ca8c", size = 10412965, upload-time = "2026-05-11T18:52:41.915Z" }, + { url = "https://files.pythonhosted.org/packages/65/b6/09b01cdbc15224e2850365192d17b7bdebb8bdbd8780ed221fcdf0d9a515/pandas-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6dc0b3fd2169c9157deed50b4d519553a3655c8c6a96027136d654592be973a9", size = 10894600, upload-time = "2026-05-11T18:52:45.02Z" }, + { url = "https://files.pythonhosted.org/packages/c9/a4/2eb28f2fccb4ced4a2c79ab2a5dee9ade1ebf44922ebad6fea158c9f95d4/pandas-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7e65d5407dc0b394f509699650e4a2ec01c0514f21850f453fa60f3be79a5dbf", size = 11422824, upload-time = "2026-05-11T18:52:48.058Z" }, + { url = "https://files.pythonhosted.org/packages/f8/45/830bb57f533a4604b355e07edcb8ea18cf88b5f94e5fca92f27052d7c597/pandas-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f8894dc474d648fe7b6ff0ca9b0bd73950d19952bc1a6534540762c5d79d305c", size = 11950889, upload-time = "2026-05-11T18:52:50.905Z" }, + { url = "https://files.pythonhosted.org/packages/b9/c5/fc1b368f303087d20e8c9bf3d6ceb186263cfac0ade735cd938538bea839/pandas-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:c7be265b62cef88e253a941e4698604973736dcfe242fdb5198f0f7bc473cdcc", size = 9755463, upload-time = "2026-05-11T18:52:53.386Z" }, + { url = "https://files.pythonhosted.org/packages/86/bd/fda8f9705b1b09c6ebe14bfc0fa0e4ec8584d54ea673628f157ff55131af/pandas-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:557409bc4178e70ee8d9ddb494798e51ebf6ea59330f6be22c51bab2a7db6c49", size = 9066158, upload-time = "2026-05-11T18:52:56.038Z" }, + { url = "https://files.pythonhosted.org/packages/c5/90/62d8302883c44308c477e222c3daf7c813a34c8e96985882fbd53d964352/pandas-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:67b3b64c11910cfa29f4e94a14d3bff9ee693b6fc76055e7cad549cee0aec5fa", size = 10331071, upload-time = "2026-05-11T18:52:58.838Z" }, + { url = "https://files.pythonhosted.org/packages/7f/ae/6a6493c783a101f165e4356953ba3c74d6f77f0042fa7d753da9dfbb640c/pandas-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:39436b377d56d2a2e52d0395bdbee171f01068e99af5250509aceeb929f765c7", size = 9875690, upload-time = "2026-05-11T18:53:01.431Z" }, + { url = "https://files.pythonhosted.org/packages/62/7c/5df8e9f56c69a2769fbe9382a5ef8f2658c007e376434e1e2cbb57ad895f/pandas-3.0.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4be06d68f9ddcfc645b87534911da79a8fbffc7573c80e0edcf42a5020624d8", size = 10381634, upload-time = "2026-05-11T18:53:04.393Z" }, + { url = "https://files.pythonhosted.org/packages/99/68/1237369725aa617bb358263d535803e3053fdbc593513ec5ed9c9896b5b6/pandas-3.0.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a4eeb6830daf35a71cc09649bd823e2b542dac246cdee9614c6e4bd65028cd6a", size = 10891243, upload-time = "2026-05-11T18:53:07.643Z" }, + { url = "https://files.pythonhosted.org/packages/25/93/77d108e8af7222b4a503ebde0e30215b1c2e4f8e53a526431890f22d5586/pandas-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1928e07221f82db493cd4af1e23c1bfca524a19a4699887975bff68f49a72bfb", size = 11388659, upload-time = "2026-05-11T18:53:10.634Z" }, + { url = "https://files.pythonhosted.org/packages/d0/bd/eff5b4399f332ac386c853f6cd2bd3fa2ca0061b9f36ecd9c4d7c4265649/pandas-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51b1fe551acb77dac643c6fda86084d8d446c10fe64b06a9cc29c4cc8540e7f2", size = 11942880, upload-time = "2026-05-11T18:53:13.536Z" }, + { url = "https://files.pythonhosted.org/packages/2c/20/559ace4200982c3887d0b86bfd0d856a2143ef8ddab63cc07934951a964c/pandas-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:a82d532a3351d435432cd913edbccaf8b8e01d4dd0e5ced5a8d2e8ecd94c7e44", size = 9757091, upload-time = "2026-05-11T18:53:16.306Z" }, + { url = "https://files.pythonhosted.org/packages/3a/66/69055a09fe200f29f922a3eeec4804611900b95f52d932ece3393c3c0c19/pandas-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:275c14e0fce14a2ec20eee474aecd305478ea3c1e6f6a9d8fe219a165542717e", size = 9057282, upload-time = "2026-05-11T18:53:18.768Z" }, + { url = "https://files.pythonhosted.org/packages/57/0e/efe801b0e6811e8e650cd21b7f2608e30f08a7067e2bf6e8752b0d56ee3c/pandas-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:46997386d528eb40376ecd6b033cf4a8a1e5282580f68f43de875b78cba2199d", size = 10767016, upload-time = "2026-05-11T18:53:21.227Z" }, + { url = "https://files.pythonhosted.org/packages/ea/dc/eb55135a1d5f0f0519f28da1f609a206d2cad1f9c35c32d51e38dd7261ae/pandas-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:261e308dfb22448384b7580cf719d2f998fe2966c92893c3e77d14008af1f066", size = 10420210, upload-time = "2026-05-11T18:53:23.982Z" }, + { url = "https://files.pythonhosted.org/packages/c6/3e/b1d5d955ce33ffecb407465a60bc32769d74fcf68224b7ae67ae11d4dea4/pandas-3.0.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dd1a5d1def6a46002e964510bdc67c368aa0951df5d1d9f8365336f5a1f490cd", size = 10336126, upload-time = "2026-05-11T18:53:26.731Z" }, + { url = "https://files.pythonhosted.org/packages/f5/76/a01261711ab60a22d71b862f0de20e4c504bf80457270ad8cb42110f6abc/pandas-3.0.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d72828c20c6d6e83e1e22a6a3b47b326b71664112fa9705dcbccfd7a39b62085", size = 10728051, upload-time = "2026-05-11T18:53:29.125Z" }, + { url = "https://files.pythonhosted.org/packages/e9/21/ea191195e587b18cf682e97f433f81b2d0fbe341380e80a3e0d6e4403c8e/pandas-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d26cbe1fcfc12e8fd900e2454163e466b2d3af84f7c75481df7683ffc073d870", size = 11350796, upload-time = "2026-05-11T18:53:32.056Z" }, + { url = "https://files.pythonhosted.org/packages/64/69/f0eaaf54939f0e8c6768fd06be9af2cef9b36048b96dfb9e1b2c685a807e/pandas-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3e91cec1879ada0624fc3dc9953c5cbd60208e59c0db28f540c5d6d47502422f", size = 11799741, upload-time = "2026-05-11T18:53:34.985Z" }, + { url = "https://files.pythonhosted.org/packages/45/a4/865e0e510cae5fc2194de4db28be638952de942571ba9125934fd9c01d47/pandas-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:08d789b41f87e0905880e293cedf6197ce71fe67cc081358b1e148a491b9bd13", size = 10499958, upload-time = "2026-05-11T18:53:37.857Z" }, + { url = "https://files.pythonhosted.org/packages/86/54/effdcc3c0ff7a08037889200e148ebe94c16c4f653be078c7b3675955df1/pandas-3.0.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3650109c0f22879df8bd6179ab9ee3d7f1d1d4e7e0094a3f0032d9f51e2e64ac", size = 10336065, upload-time = "2026-05-11T18:53:41.099Z" }, + { url = "https://files.pythonhosted.org/packages/68/10/bf2d6738d72748b961a3751ab89522d58c54efc36a8e1a12161216cd45cf/pandas-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:bab900348131a7db1f69a7309ef141fd5680f1487094193bcbbb61791573bf8f", size = 9926101, upload-time = "2026-05-11T18:53:43.515Z" }, + { url = "https://files.pythonhosted.org/packages/ae/e9/e35cf11c8a136e757b956f5f0efdcaa50aecde85ea055f1898dfc68262f3/pandas-3.0.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba7e08b9ac1d54569cd1e256e3668975ed624d6826f7b68df0342b012007bddb", size = 10457553, upload-time = "2026-05-11T18:53:46.394Z" }, + { url = "https://files.pythonhosted.org/packages/58/3b/1cdec6772bdbaf7b25dab360c59f03cadf05492dd724c6540af905389b07/pandas-3.0.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d71c63ae4ebdbf70209742096f1fc46a83a0613c99d4b23766cced9ff8cd62a", size = 10914065, upload-time = "2026-05-11T18:53:49.134Z" }, + { url = "https://files.pythonhosted.org/packages/c4/c2/1ef644445fcd72e3627bceec77e3560636f87ddce4ed841afe76b83b5bf9/pandas-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e3a2ec42c98ffa2565a67e08e218d06d72576d758d90facb7c00805194d8f360", size = 11459188, upload-time = "2026-05-11T18:53:52.527Z" }, + { url = "https://files.pythonhosted.org/packages/7e/49/4d8d4f42cbc9c4adc7a1870f269c02cbd6cd40d059622c06fb298addcbad/pandas-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:335f62418ed562cfc3c49e9e196375c28b729dcef8543abf4f9438e381bf3c76", size = 11982966, upload-time = "2026-05-11T18:53:55.043Z" }, + { url = "https://files.pythonhosted.org/packages/38/55/792619469bab9882d8bbd5865d45a72f6478762d04a9af4bf0d08c503e95/pandas-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:3c20a521bbb85902f79f7270c80a59e1b5452d96d170c034f207181870f97ac5", size = 9876755, upload-time = "2026-05-11T18:53:58.067Z" }, + { url = "https://files.pythonhosted.org/packages/2a/af/33c469653b0ba03b50c3a98192d4c07f0c75c66b263ceb097fce0ee97d31/pandas-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:a2d2dff8a04f3917b55ab3910c32990f8ddf7eceba114947838cefa976a68977", size = 9198658, upload-time = "2026-05-11T18:54:00.733Z" }, + { url = "https://files.pythonhosted.org/packages/a2/fa/b8c257bd76b8bd060c3a9151c1fca05e9b9c5e3af5d0f549c0356f6d143d/pandas-3.0.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:0d589105b3c14645af1738ff279b2995102d8f7a03b0a66dc8d95550eb513e04", size = 10787242, upload-time = "2026-05-11T18:54:03.564Z" }, + { url = "https://files.pythonhosted.org/packages/54/eb/f19206ffb0bf1919002969aa448b4702c6594845156a6f8050674855aac3/pandas-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:13fc1e853d9e04743d11ba75a985ccbc2a317fe07d8af61e445a6fd24dacd6a6", size = 10436369, upload-time = "2026-05-11T18:54:06.311Z" }, + { url = "https://files.pythonhosted.org/packages/fd/24/c7c39fb4fe22b71a0c2d78bf0c585c600092d85f94f086d2b3b2f6ca27e2/pandas-3.0.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:819959dab7bbd0049c15623fbac4e29a191b9528160a61fb1032242d8ced2d9c", size = 10358306, upload-time = "2026-05-11T18:54:09.085Z" }, + { url = "https://files.pythonhosted.org/packages/16/ec/dd2a9eb7fa1204df88c0864164e35b228ac581062ac612ba0a67fd812e4c/pandas-3.0.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:60ae316d3fd75d1858d450d0db0103ea2be3e7d4a95ec2f064f7e2ae63f7b028", size = 10758394, upload-time = "2026-05-11T18:54:11.956Z" }, + { url = "https://files.pythonhosted.org/packages/95/6e/00c61ea8e85b4f6d8d35e11852a1a4998fc7fafc91c6a602d1cc9c972d64/pandas-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bd3a518890b400d32f9023722dc9a9a5c969f00b415419a3c06c043f09bb5d7d", size = 11375717, upload-time = "2026-05-11T18:54:14.539Z" }, + { url = "https://files.pythonhosted.org/packages/31/89/8fc1c268969fac43688d65fd92e67df24bd128d53cb4d2eee534cd307399/pandas-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9c39be2d709d01fa972a0cabc522389fceca4f3969332ba25a7d6c5802cf976a", size = 11828897, upload-time = "2026-05-11T18:54:17.146Z" }, + { url = "https://files.pythonhosted.org/packages/56/3b/e7d20dea247a3e6dc0bd8a6953854afbedc03951def4e7371e05e7263e25/pandas-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4db8c527972a821cf5286b40ccc57642a39bc62e62022b42f99f8a67fca8c3a1", size = 10900855, upload-time = "2026-05-11T18:54:19.72Z" }, + { url = "https://files.pythonhosted.org/packages/0f/54/68a0978d1ef8502b8492099beaa6e7a0c1b32e3b5d4f677f5810cb08711c/pandas-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b2c95f8bfc1ee412bf482605d7bfd30c12d1d26bd59fdd91efeef1d4718decb1", size = 9466464, upload-time = "2026-05-11T18:54:22.754Z" }, +] + +[[package]] +name = "parameterized" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ea/49/00c0c0cc24ff4266025a53e41336b79adaa5a4ebfad214f433d623f9865e/parameterized-0.9.0.tar.gz", hash = "sha256:7fc905272cefa4f364c1a3429cbbe9c0f98b793988efb5bf90aac80f08db09b1", size = 24351, upload-time = "2023-03-27T02:01:11.592Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/2f/804f58f0b856ab3bf21617cccf5b39206e6c4c94c2cd227bde125ea6105f/parameterized-0.9.0-py2.py3-none-any.whl", hash = "sha256:4e0758e3d41bea3bbd05ec14fc2c24736723f243b28d702081aef438c9372b1b", size = 20475, upload-time = "2023-03-27T02:01:09.31Z" }, +] + +[[package]] +name = "pillow" +version = "12.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819, upload-time = "2026-04-01T14:46:17.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/aa/d0b28e1c811cd4d5f5c2bfe2e022292bd255ae5744a3b9ac7d6c8f72dd75/pillow-12.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:a4e8f36e677d3336f35089648c8955c51c6d386a13cf6ee9c189c5f5bd713a9f", size = 5354355, upload-time = "2026-04-01T14:42:15.402Z" }, + { url = "https://files.pythonhosted.org/packages/27/8e/1d5b39b8ae2bd7650d0c7b6abb9602d16043ead9ebbfef4bc4047454da2a/pillow-12.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e589959f10d9824d39b350472b92f0ce3b443c0a3442ebf41c40cb8361c5b97", size = 4695871, upload-time = "2026-04-01T14:42:18.234Z" }, + { url = "https://files.pythonhosted.org/packages/f0/c5/dcb7a6ca6b7d3be41a76958e90018d56c8462166b3ef223150360850c8da/pillow-12.2.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a52edc8bfff4429aaabdf4d9ee0daadbbf8562364f940937b941f87a4290f5ff", size = 6269734, upload-time = "2026-04-01T14:42:20.608Z" }, + { url = "https://files.pythonhosted.org/packages/ea/f1/aa1bb13b2f4eba914e9637893c73f2af8e48d7d4023b9d3750d4c5eb2d0c/pillow-12.2.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:975385f4776fafde056abb318f612ef6285b10a1f12b8570f3647ad0d74b48ec", size = 8076080, upload-time = "2026-04-01T14:42:23.095Z" }, + { url = "https://files.pythonhosted.org/packages/a1/2a/8c79d6a53169937784604a8ae8d77e45888c41537f7f6f65ed1f407fe66d/pillow-12.2.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd9c0c7a0c681a347b3194c500cb1e6ca9cab053ea4d82a5cf45b6b754560136", size = 6382236, upload-time = "2026-04-01T14:42:25.82Z" }, + { url = "https://files.pythonhosted.org/packages/b5/42/bbcb6051030e1e421d103ce7a8ecadf837aa2f39b8f82ef1a8d37c3d4ebc/pillow-12.2.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:88d387ff40b3ff7c274947ed3125dedf5262ec6919d83946753b5f3d7c67ea4c", size = 7070220, upload-time = "2026-04-01T14:42:28.68Z" }, + { url = "https://files.pythonhosted.org/packages/3f/e1/c2a7d6dd8cfa6b231227da096fd2d58754bab3603b9d73bf609d3c18b64f/pillow-12.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:51c4167c34b0d8ba05b547a3bb23578d0ba17b80a5593f93bd8ecb123dd336a3", size = 6493124, upload-time = "2026-04-01T14:42:31.579Z" }, + { url = "https://files.pythonhosted.org/packages/5f/41/7c8617da5d32e1d2f026e509484fdb6f3ad7efaef1749a0c1928adbb099e/pillow-12.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:34c0d99ecccea270c04882cb3b86e7b57296079c9a4aff88cb3b33563d95afaa", size = 7194324, upload-time = "2026-04-01T14:42:34.615Z" }, + { url = "https://files.pythonhosted.org/packages/2d/de/a777627e19fd6d62f84070ee1521adde5eeda4855b5cf60fe0b149118bca/pillow-12.2.0-cp310-cp310-win32.whl", hash = "sha256:b85f66ae9eb53e860a873b858b789217ba505e5e405a24b85c0464822fe88032", size = 6376363, upload-time = "2026-04-01T14:42:37.19Z" }, + { url = "https://files.pythonhosted.org/packages/e7/34/fc4cb5204896465842767b96d250c08410f01f2f28afc43b257de842eed5/pillow-12.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:673aa32138f3e7531ccdbca7b3901dba9b70940a19ccecc6a37c77d5fdeb05b5", size = 7083523, upload-time = "2026-04-01T14:42:39.62Z" }, + { url = "https://files.pythonhosted.org/packages/2d/a0/32852d36bc7709f14dc3f64f929a275e958ad8c19a6deba9610d458e28b3/pillow-12.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:3e080565d8d7c671db5802eedfb438e5565ffa40115216eabb8cd52d0ecce024", size = 2463318, upload-time = "2026-04-01T14:42:42.063Z" }, + { url = "https://files.pythonhosted.org/packages/68/e1/748f5663efe6edcfc4e74b2b93edfb9b8b99b67f21a854c3ae416500a2d9/pillow-12.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:8be29e59487a79f173507c30ddf57e733a357f67881430449bb32614075a40ab", size = 5354347, upload-time = "2026-04-01T14:42:44.255Z" }, + { url = "https://files.pythonhosted.org/packages/47/a1/d5ff69e747374c33a3b53b9f98cca7889fce1fd03d79cdc4e1bccc6c5a87/pillow-12.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:71cde9a1e1551df7d34a25462fc60325e8a11a82cc2e2f54578e5e9a1e153d65", size = 4695873, upload-time = "2026-04-01T14:42:46.452Z" }, + { url = "https://files.pythonhosted.org/packages/df/21/e3fbdf54408a973c7f7f89a23b2cb97a7ef30c61ab4142af31eee6aebc88/pillow-12.2.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f490f9368b6fc026f021db16d7ec2fbf7d89e2edb42e8ec09d2c60505f5729c7", size = 6280168, upload-time = "2026-04-01T14:42:49.228Z" }, + { url = "https://files.pythonhosted.org/packages/d3/f1/00b7278c7dd52b17ad4329153748f87b6756ec195ff786c2bdf12518337d/pillow-12.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8bd7903a5f2a4545f6fd5935c90058b89d30045568985a71c79f5fd6edf9b91e", size = 8088188, upload-time = "2026-04-01T14:42:51.735Z" }, + { url = "https://files.pythonhosted.org/packages/ad/cf/220a5994ef1b10e70e85748b75649d77d506499352be135a4989c957b701/pillow-12.2.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3997232e10d2920a68d25191392e3a4487d8183039e1c74c2297f00ed1c50705", size = 6394401, upload-time = "2026-04-01T14:42:54.343Z" }, + { url = "https://files.pythonhosted.org/packages/e9/bd/e51a61b1054f09437acfbc2ff9106c30d1eb76bc1453d428399946781253/pillow-12.2.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e74473c875d78b8e9d5da2a70f7099549f9eb37ded4e2f6a463e60125bccd176", size = 7079655, upload-time = "2026-04-01T14:42:56.954Z" }, + { url = "https://files.pythonhosted.org/packages/6b/3d/45132c57d5fb4b5744567c3817026480ac7fc3ce5d4c47902bc0e7f6f853/pillow-12.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:56a3f9c60a13133a98ecff6197af34d7824de9b7b38c3654861a725c970c197b", size = 6503105, upload-time = "2026-04-01T14:42:59.847Z" }, + { url = "https://files.pythonhosted.org/packages/7d/2e/9df2fc1e82097b1df3dce58dc43286aa01068e918c07574711fcc53e6fb4/pillow-12.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:90e6f81de50ad6b534cab6e5aef77ff6e37722b2f5d908686f4a5c9eba17a909", size = 7203402, upload-time = "2026-04-01T14:43:02.664Z" }, + { url = "https://files.pythonhosted.org/packages/bd/2e/2941e42858ebb67e50ae741473de81c2984e6eff7b397017623c676e2e8d/pillow-12.2.0-cp311-cp311-win32.whl", hash = "sha256:8c984051042858021a54926eb597d6ee3012393ce9c181814115df4c60b9a808", size = 6378149, upload-time = "2026-04-01T14:43:05.274Z" }, + { url = "https://files.pythonhosted.org/packages/69/42/836b6f3cd7f3e5fa10a1f1a5420447c17966044c8fbf589cc0452d5502db/pillow-12.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e6b2a0c538fc200b38ff9eb6628228b77908c319a005815f2dde585a0664b60", size = 7082626, upload-time = "2026-04-01T14:43:08.557Z" }, + { url = "https://files.pythonhosted.org/packages/c2/88/549194b5d6f1f494b485e493edc6693c0a16f4ada488e5bd974ed1f42fad/pillow-12.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:9a8a34cc89c67a65ea7437ce257cea81a9dad65b29805f3ecee8c8fe8ff25ffe", size = 2463531, upload-time = "2026-04-01T14:43:10.743Z" }, + { url = "https://files.pythonhosted.org/packages/58/be/7482c8a5ebebbc6470b3eb791812fff7d5e0216c2be3827b30b8bb6603ed/pillow-12.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2d192a155bbcec180f8564f693e6fd9bccff5a7af9b32e2e4bf8c9c69dbad6b5", size = 5308279, upload-time = "2026-04-01T14:43:13.246Z" }, + { url = "https://files.pythonhosted.org/packages/d8/95/0a351b9289c2b5cbde0bacd4a83ebc44023e835490a727b2a3bd60ddc0f4/pillow-12.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421", size = 4695490, upload-time = "2026-04-01T14:43:15.584Z" }, + { url = "https://files.pythonhosted.org/packages/de/af/4e8e6869cbed569d43c416fad3dc4ecb944cb5d9492defaed89ddd6fe871/pillow-12.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:03e7e372d5240cc23e9f07deca4d775c0817bffc641b01e9c3af208dbd300987", size = 6284462, upload-time = "2026-04-01T14:43:18.268Z" }, + { url = "https://files.pythonhosted.org/packages/e9/9e/c05e19657fd57841e476be1ab46c4d501bffbadbafdc31a6d665f8b737b6/pillow-12.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b86024e52a1b269467a802258c25521e6d742349d760728092e1bc2d135b4d76", size = 8094744, upload-time = "2026-04-01T14:43:20.716Z" }, + { url = "https://files.pythonhosted.org/packages/2b/54/1789c455ed10176066b6e7e6da1b01e50e36f94ba584dc68d9eebfe9156d/pillow-12.2.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7371b48c4fa448d20d2714c9a1f775a81155050d383333e0a6c15b1123dda005", size = 6398371, upload-time = "2026-04-01T14:43:23.443Z" }, + { url = "https://files.pythonhosted.org/packages/43/e3/fdc657359e919462369869f1c9f0e973f353f9a9ee295a39b1fea8ee1a77/pillow-12.2.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62f5409336adb0663b7caa0da5c7d9e7bdbaae9ce761d34669420c2a801b2780", size = 7087215, upload-time = "2026-04-01T14:43:26.758Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f8/2f6825e441d5b1959d2ca5adec984210f1ec086435b0ed5f52c19b3b8a6e/pillow-12.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:01afa7cf67f74f09523699b4e88c73fb55c13346d212a59a2db1f86b0a63e8c5", size = 6509783, upload-time = "2026-04-01T14:43:29.56Z" }, + { url = "https://files.pythonhosted.org/packages/67/f9/029a27095ad20f854f9dba026b3ea6428548316e057e6fc3545409e86651/pillow-12.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5", size = 7212112, upload-time = "2026-04-01T14:43:32.091Z" }, + { url = "https://files.pythonhosted.org/packages/be/42/025cfe05d1be22dbfdb4f264fe9de1ccda83f66e4fc3aac94748e784af04/pillow-12.2.0-cp312-cp312-win32.whl", hash = "sha256:58f62cc0f00fd29e64b29f4fd923ffdb3859c9f9e6105bfc37ba1d08994e8940", size = 6378489, upload-time = "2026-04-01T14:43:34.601Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7b/25a221d2c761c6a8ae21bfa3874988ff2583e19cf8a27bf2fee358df7942/pillow-12.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:7f84204dee22a783350679a0333981df803dac21a0190d706a50475e361c93f5", size = 7084129, upload-time = "2026-04-01T14:43:37.213Z" }, + { url = "https://files.pythonhosted.org/packages/10/e1/542a474affab20fd4a0f1836cb234e8493519da6b76899e30bcc5d990b8b/pillow-12.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:af73337013e0b3b46f175e79492d96845b16126ddf79c438d7ea7ff27783a414", size = 2463612, upload-time = "2026-04-01T14:43:39.421Z" }, + { url = "https://files.pythonhosted.org/packages/4a/01/53d10cf0dbad820a8db274d259a37ba50b88b24768ddccec07355382d5ad/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c", size = 4100837, upload-time = "2026-04-01T14:43:41.506Z" }, + { url = "https://files.pythonhosted.org/packages/0f/98/f3a6657ecb698c937f6c76ee564882945f29b79bad496abcba0e84659ec5/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2", size = 4176528, upload-time = "2026-04-01T14:43:43.773Z" }, + { url = "https://files.pythonhosted.org/packages/69/bc/8986948f05e3ea490b8442ea1c1d4d990b24a7e43d8a51b2c7d8b1dced36/pillow-12.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c", size = 3640401, upload-time = "2026-04-01T14:43:45.87Z" }, + { url = "https://files.pythonhosted.org/packages/34/46/6c717baadcd62bc8ed51d238d521ab651eaa74838291bda1f86fe1f864c9/pillow-12.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795", size = 5308094, upload-time = "2026-04-01T14:43:48.438Z" }, + { url = "https://files.pythonhosted.org/packages/71/43/905a14a8b17fdb1ccb58d282454490662d2cb89a6bfec26af6d3520da5ec/pillow-12.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f", size = 4695402, upload-time = "2026-04-01T14:43:51.292Z" }, + { url = "https://files.pythonhosted.org/packages/73/dd/42107efcb777b16fa0393317eac58f5b5cf30e8392e266e76e51cff28c3d/pillow-12.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed", size = 6280005, upload-time = "2026-04-01T14:43:54.242Z" }, + { url = "https://files.pythonhosted.org/packages/a8/68/b93e09e5e8549019e61acf49f65b1a8530765a7f812c77a7461bca7e4494/pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9", size = 8090669, upload-time = "2026-04-01T14:43:57.335Z" }, + { url = "https://files.pythonhosted.org/packages/4b/6e/3ccb54ce8ec4ddd1accd2d89004308b7b0b21c4ac3d20fa70af4760a4330/pillow-12.2.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed", size = 6395194, upload-time = "2026-04-01T14:43:59.864Z" }, + { url = "https://files.pythonhosted.org/packages/67/ee/21d4e8536afd1a328f01b359b4d3997b291ffd35a237c877b331c1c3b71c/pillow-12.2.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3", size = 7082423, upload-time = "2026-04-01T14:44:02.74Z" }, + { url = "https://files.pythonhosted.org/packages/78/5f/e9f86ab0146464e8c133fe85df987ed9e77e08b29d8d35f9f9f4d6f917ba/pillow-12.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9", size = 6505667, upload-time = "2026-04-01T14:44:05.381Z" }, + { url = "https://files.pythonhosted.org/packages/ed/1e/409007f56a2fdce61584fd3acbc2bbc259857d555196cedcadc68c015c82/pillow-12.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795", size = 7208580, upload-time = "2026-04-01T14:44:08.39Z" }, + { url = "https://files.pythonhosted.org/packages/23/c4/7349421080b12fb35414607b8871e9534546c128a11965fd4a7002ccfbee/pillow-12.2.0-cp313-cp313-win32.whl", hash = "sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e", size = 6375896, upload-time = "2026-04-01T14:44:11.197Z" }, + { url = "https://files.pythonhosted.org/packages/3f/82/8a3739a5e470b3c6cbb1d21d315800d8e16bff503d1f16b03a4ec3212786/pillow-12.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b", size = 7081266, upload-time = "2026-04-01T14:44:13.947Z" }, + { url = "https://files.pythonhosted.org/packages/c3/25/f968f618a062574294592f668218f8af564830ccebdd1fa6200f598e65c5/pillow-12.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06", size = 2463508, upload-time = "2026-04-01T14:44:16.312Z" }, + { url = "https://files.pythonhosted.org/packages/4d/a4/b342930964e3cb4dce5038ae34b0eab4653334995336cd486c5a8c25a00c/pillow-12.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b", size = 5309927, upload-time = "2026-04-01T14:44:18.89Z" }, + { url = "https://files.pythonhosted.org/packages/9f/de/23198e0a65a9cf06123f5435a5d95cea62a635697f8f03d134d3f3a96151/pillow-12.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f", size = 4698624, upload-time = "2026-04-01T14:44:21.115Z" }, + { url = "https://files.pythonhosted.org/packages/01/a6/1265e977f17d93ea37aa28aa81bad4fa597933879fac2520d24e021c8da3/pillow-12.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612", size = 6321252, upload-time = "2026-04-01T14:44:23.663Z" }, + { url = "https://files.pythonhosted.org/packages/3c/83/5982eb4a285967baa70340320be9f88e57665a387e3a53a7f0db8231a0cd/pillow-12.2.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c", size = 8126550, upload-time = "2026-04-01T14:44:26.772Z" }, + { url = "https://files.pythonhosted.org/packages/4e/48/6ffc514adce69f6050d0753b1a18fd920fce8cac87620d5a31231b04bfc5/pillow-12.2.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea", size = 6433114, upload-time = "2026-04-01T14:44:29.615Z" }, + { url = "https://files.pythonhosted.org/packages/36/a3/f9a77144231fb8d40ee27107b4463e205fa4677e2ca2548e14da5cf18dce/pillow-12.2.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4", size = 7115667, upload-time = "2026-04-01T14:44:32.773Z" }, + { url = "https://files.pythonhosted.org/packages/c1/fc/ac4ee3041e7d5a565e1c4fd72a113f03b6394cc72ab7089d27608f8aaccb/pillow-12.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4", size = 6538966, upload-time = "2026-04-01T14:44:35.252Z" }, + { url = "https://files.pythonhosted.org/packages/c0/a8/27fb307055087f3668f6d0a8ccb636e7431d56ed0750e07a60547b1e083e/pillow-12.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea", size = 7238241, upload-time = "2026-04-01T14:44:37.875Z" }, + { url = "https://files.pythonhosted.org/packages/ad/4b/926ab182c07fccae9fcb120043464e1ff1564775ec8864f21a0ebce6ac25/pillow-12.2.0-cp313-cp313t-win32.whl", hash = "sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24", size = 6379592, upload-time = "2026-04-01T14:44:40.336Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c4/f9e476451a098181b30050cc4c9a3556b64c02cf6497ea421ac047e89e4b/pillow-12.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98", size = 7085542, upload-time = "2026-04-01T14:44:43.251Z" }, + { url = "https://files.pythonhosted.org/packages/00/a4/285f12aeacbe2d6dc36c407dfbbe9e96d4a80b0fb710a337f6d2ad978c75/pillow-12.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453", size = 2465765, upload-time = "2026-04-01T14:44:45.996Z" }, + { url = "https://files.pythonhosted.org/packages/bf/98/4595daa2365416a86cb0d495248a393dfc84e96d62ad080c8546256cb9c0/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8", size = 4100848, upload-time = "2026-04-01T14:44:48.48Z" }, + { url = "https://files.pythonhosted.org/packages/0b/79/40184d464cf89f6663e18dfcf7ca21aae2491fff1a16127681bf1fa9b8cf/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b", size = 4176515, upload-time = "2026-04-01T14:44:51.353Z" }, + { url = "https://files.pythonhosted.org/packages/b0/63/703f86fd4c422a9cf722833670f4f71418fb116b2853ff7da722ea43f184/pillow-12.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295", size = 3640159, upload-time = "2026-04-01T14:44:53.588Z" }, + { url = "https://files.pythonhosted.org/packages/71/e0/fb22f797187d0be2270f83500aab851536101b254bfa1eae10795709d283/pillow-12.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed", size = 5312185, upload-time = "2026-04-01T14:44:56.039Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8c/1a9e46228571de18f8e28f16fabdfc20212a5d019f3e3303452b3f0a580d/pillow-12.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae", size = 4695386, upload-time = "2026-04-01T14:44:58.663Z" }, + { url = "https://files.pythonhosted.org/packages/70/62/98f6b7f0c88b9addd0e87c217ded307b36be024d4ff8869a812b241d1345/pillow-12.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601", size = 6280384, upload-time = "2026-04-01T14:45:01.5Z" }, + { url = "https://files.pythonhosted.org/packages/5e/03/688747d2e91cfbe0e64f316cd2e8005698f76ada3130d0194664174fa5de/pillow-12.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be", size = 8091599, upload-time = "2026-04-01T14:45:04.5Z" }, + { url = "https://files.pythonhosted.org/packages/f6/35/577e22b936fcdd66537329b33af0b4ccfefaeabd8aec04b266528cddb33c/pillow-12.2.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f", size = 6396021, upload-time = "2026-04-01T14:45:07.117Z" }, + { url = "https://files.pythonhosted.org/packages/11/8d/d2532ad2a603ca2b93ad9f5135732124e57811d0168155852f37fbce2458/pillow-12.2.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286", size = 7083360, upload-time = "2026-04-01T14:45:09.763Z" }, + { url = "https://files.pythonhosted.org/packages/5e/26/d325f9f56c7e039034897e7380e9cc202b1e368bfd04d4cbe6a441f02885/pillow-12.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50", size = 6507628, upload-time = "2026-04-01T14:45:12.378Z" }, + { url = "https://files.pythonhosted.org/packages/5f/f7/769d5632ffb0988f1c5e7660b3e731e30f7f8ec4318e94d0a5d674eb65a4/pillow-12.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104", size = 7209321, upload-time = "2026-04-01T14:45:15.122Z" }, + { url = "https://files.pythonhosted.org/packages/6a/7a/c253e3c645cd47f1aceea6a8bacdba9991bf45bb7dfe927f7c893e89c93c/pillow-12.2.0-cp314-cp314-win32.whl", hash = "sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7", size = 6479723, upload-time = "2026-04-01T14:45:17.797Z" }, + { url = "https://files.pythonhosted.org/packages/cd/8b/601e6566b957ca50e28725cb6c355c59c2c8609751efbecd980db44e0349/pillow-12.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150", size = 7217400, upload-time = "2026-04-01T14:45:20.529Z" }, + { url = "https://files.pythonhosted.org/packages/d6/94/220e46c73065c3e2951bb91c11a1fb636c8c9ad427ac3ce7d7f3359b9b2f/pillow-12.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1", size = 2554835, upload-time = "2026-04-01T14:45:23.162Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ab/1b426a3974cb0e7da5c29ccff4807871d48110933a57207b5a676cccc155/pillow-12.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463", size = 5314225, upload-time = "2026-04-01T14:45:25.637Z" }, + { url = "https://files.pythonhosted.org/packages/19/1e/dce46f371be2438eecfee2a1960ee2a243bbe5e961890146d2dee1ff0f12/pillow-12.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3", size = 4698541, upload-time = "2026-04-01T14:45:28.355Z" }, + { url = "https://files.pythonhosted.org/packages/55/c3/7fbecf70adb3a0c33b77a300dc52e424dc22ad8cdc06557a2e49523b703d/pillow-12.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166", size = 6322251, upload-time = "2026-04-01T14:45:30.924Z" }, + { url = "https://files.pythonhosted.org/packages/1c/3c/7fbc17cfb7e4fe0ef1642e0abc17fc6c94c9f7a16be41498e12e2ba60408/pillow-12.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe", size = 8127807, upload-time = "2026-04-01T14:45:33.908Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c3/a8ae14d6defd2e448493ff512fae903b1e9bd40b72efb6ec55ce0048c8ce/pillow-12.2.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd", size = 6433935, upload-time = "2026-04-01T14:45:36.623Z" }, + { url = "https://files.pythonhosted.org/packages/6e/32/2880fb3a074847ac159d8f902cb43278a61e85f681661e7419e6596803ed/pillow-12.2.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e", size = 7116720, upload-time = "2026-04-01T14:45:39.258Z" }, + { url = "https://files.pythonhosted.org/packages/46/87/495cc9c30e0129501643f24d320076f4cc54f718341df18cc70ec94c44e1/pillow-12.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06", size = 6540498, upload-time = "2026-04-01T14:45:41.879Z" }, + { url = "https://files.pythonhosted.org/packages/18/53/773f5edca692009d883a72211b60fdaf8871cbef075eaa9d577f0a2f989e/pillow-12.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43", size = 7239413, upload-time = "2026-04-01T14:45:44.705Z" }, + { url = "https://files.pythonhosted.org/packages/c9/e4/4b64a97d71b2a83158134abbb2f5bd3f8a2ea691361282f010998f339ec7/pillow-12.2.0-cp314-cp314t-win32.whl", hash = "sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354", size = 6482084, upload-time = "2026-04-01T14:45:47.568Z" }, + { url = "https://files.pythonhosted.org/packages/ba/13/306d275efd3a3453f72114b7431c877d10b1154014c1ebbedd067770d629/pillow-12.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1", size = 7225152, upload-time = "2026-04-01T14:45:50.032Z" }, + { url = "https://files.pythonhosted.org/packages/ff/6e/cf826fae916b8658848d7b9f38d88da6396895c676e8086fc0988073aaf8/pillow-12.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb", size = 2556579, upload-time = "2026-04-01T14:45:52.529Z" }, + { url = "https://files.pythonhosted.org/packages/4e/b7/2437044fb910f499610356d1352e3423753c98e34f915252aafecc64889f/pillow-12.2.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0538bd5e05efec03ae613fd89c4ce0368ecd2ba239cc25b9f9be7ed426b0af1f", size = 5273969, upload-time = "2026-04-01T14:45:55.538Z" }, + { url = "https://files.pythonhosted.org/packages/f6/f4/8316e31de11b780f4ac08ef3654a75555e624a98db1056ecb2122d008d5a/pillow-12.2.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:394167b21da716608eac917c60aa9b969421b5dcbbe02ae7f013e7b85811c69d", size = 4659674, upload-time = "2026-04-01T14:45:58.093Z" }, + { url = "https://files.pythonhosted.org/packages/d4/37/664fca7201f8bb2aa1d20e2c3d5564a62e6ae5111741966c8319ca802361/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5d04bfa02cc2d23b497d1e90a0f927070043f6cbf303e738300532379a4b4e0f", size = 5288479, upload-time = "2026-04-01T14:46:01.141Z" }, + { url = "https://files.pythonhosted.org/packages/49/62/5b0ed78fce87346be7a5cfcfaaad91f6a1f98c26f86bdbafa2066c647ef6/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0c838a5125cee37e68edec915651521191cef1e6aa336b855f495766e77a366e", size = 7032230, upload-time = "2026-04-01T14:46:03.874Z" }, + { url = "https://files.pythonhosted.org/packages/c3/28/ec0fc38107fc32536908034e990c47914c57cd7c5a3ece4d8d8f7ffd7e27/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a6c9fa44005fa37a91ebfc95d081e8079757d2e904b27103f4f5fa6f0bf78c0", size = 5355404, upload-time = "2026-04-01T14:46:06.33Z" }, + { url = "https://files.pythonhosted.org/packages/5e/8b/51b0eddcfa2180d60e41f06bd6d0a62202b20b59c68f5a132e615b75aecf/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:25373b66e0dd5905ed63fa3cae13c82fbddf3079f2c8bf15c6fb6a35586324c1", size = 6002215, upload-time = "2026-04-01T14:46:08.83Z" }, + { url = "https://files.pythonhosted.org/packages/bc/60/5382c03e1970de634027cee8e1b7d39776b778b81812aaf45b694dfe9e28/pillow-12.2.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:bfa9c230d2fe991bed5318a5f119bd6780cda2915cca595393649fc118ab895e", size = 7080946, upload-time = "2026-04-01T14:46:11.734Z" }, +] + +[[package]] +name = "plotly" +version = "6.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "narwhals" }, + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3a/7f/0f100df1172aadf88a929a9dbb902656b0880ba4b960fe5224867159d8f4/plotly-6.7.0.tar.gz", hash = "sha256:45eea0ff27e2a23ccd62776f77eb43aa1ca03df4192b76036e380bb479b892c6", size = 6911286, upload-time = "2026-04-09T20:36:45.738Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/ad/cba91b3bcf04073e4d1655a5c1710ef3f457f56f7d1b79dcc3d72f4dd912/plotly-6.7.0-py3-none-any.whl", hash = "sha256:ac8aca1c25c663a59b5b9140a549264a5badde2e057d79b8c772ae2920e32ff0", size = 9898444, upload-time = "2026-04-09T20:36:39.812Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "plum-dispatch" +version = "2.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "beartype" }, + { name = "rich" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1b/b7/84146ae5ff6c40d11357acdb36aafe3db7e104de01c1026d8e1b0ce3e7f1/plum_dispatch-2.9.0.tar.gz", hash = "sha256:fb45c5b2dd4dadd57def51bcf321dfa3a258df5c725f43adea7e7f6db3b79b52", size = 244502, upload-time = "2026-04-28T08:38:34.442Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/80/e1/8fa7fffff5699fa9d7aae2c57de27f2b21a242189f8c102c80a0961dcc89/plum_dispatch-2.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6c5f5dfd1afd42dfd738c1b7701b9e03279c52b4952d6bb56e169564b1d48eb9", size = 174089, upload-time = "2026-04-28T08:38:15.801Z" }, + { url = "https://files.pythonhosted.org/packages/33/91/a2879973fd40c985aeb55ffe522d7b91c10062f82b251b9ad202f063a3e8/plum_dispatch-2.9.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7cec06a2f2545a2b09e8dde81be6f1512e1df850b3ab2fbf582ef79a10956b23", size = 206109, upload-time = "2026-04-28T08:38:17.219Z" }, + { url = "https://files.pythonhosted.org/packages/e2/3a/578642ace42b78507a98e8b27ad7d8554d197b7ec67d28eab6806b0a0fc0/plum_dispatch-2.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:7e767a7e52f7d4cb3f10010024047d043c29d7ed0ab7df63bceef5371c44a2fa", size = 152556, upload-time = "2026-04-28T08:38:18.51Z" }, + { url = "https://files.pythonhosted.org/packages/e0/20/7aa36a01b1689d427a9fc20c80b1c0bc8340d1413c8071f08fb1e9a01939/plum_dispatch-2.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:25f62c2209b7bff00c6cdc25fe153ac09df304d7d833fab2852fc46fffbb7e87", size = 172039, upload-time = "2026-04-28T08:38:19.985Z" }, + { url = "https://files.pythonhosted.org/packages/63/01/eee83f936500e3e0100ac421483fa78768fec70214e4ce0241187ec3a2f1/plum_dispatch-2.9.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4d34cab84908abbdff501e356296eba960ff5d5fc04668b71ebc6f9ff84fa2f", size = 203364, upload-time = "2026-04-28T08:38:21.431Z" }, + { url = "https://files.pythonhosted.org/packages/d8/05/78c229ccce36fbd6dbc1cde698baae57313515ff1bff6ebad28be46c9386/plum_dispatch-2.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:1050150b6ae3600ac19a406b23cb7d0707825b227f608a54b8be9994d1f4b710", size = 152110, upload-time = "2026-04-28T08:38:22.975Z" }, + { url = "https://files.pythonhosted.org/packages/82/d6/fc6591336731b5e291319ec3c43d81cd6cc7940c9079183b333b85ed4d77/plum_dispatch-2.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:39a325b0b041fad687271d71abc11b5271d5a3b62af2c6b1271f4ececc3b59de", size = 175166, upload-time = "2026-04-28T08:38:24.467Z" }, + { url = "https://files.pythonhosted.org/packages/4c/4d/5418649397b477ae08839564c97596ede5ef36523147899bb913bbe959d1/plum_dispatch-2.9.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d39b20a1af776a39e4a0833e97c6258d938e5d874c7f3537bb7d772c39718c1e", size = 205988, upload-time = "2026-04-28T08:38:25.838Z" }, + { url = "https://files.pythonhosted.org/packages/e1/a5/28a6c4e45c4465a6e1a30d65206e913c217690752dc3c9261d78a9187aed/plum_dispatch-2.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:beb9b92c6994404a0d2dc83c857263f5da49519575e5cbb8a13060652746c44c", size = 152563, upload-time = "2026-04-28T08:38:27.37Z" }, + { url = "https://files.pythonhosted.org/packages/cb/18/19e01dbbaececb75815abb37bc71e10d87a7561829b4eae47ec3b342c94f/plum_dispatch-2.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:78987caf558fbdd675a7a4a5c144069f1f9524610195123fb7d0f0234d0af01a", size = 174490, upload-time = "2026-04-28T08:38:28.881Z" }, + { url = "https://files.pythonhosted.org/packages/fc/29/7b3de57cd86430fd00d71bc5a8a078bf2c63d4ada7fdfb30853fe976a19f/plum_dispatch-2.9.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:36662d76ae6d87aaa041fade036d2242f0b88b68c9fb4c96b7ba0026320065d3", size = 204734, upload-time = "2026-04-28T08:38:30.398Z" }, + { url = "https://files.pythonhosted.org/packages/ba/98/23d19326af0da251bb98a39f517ab6da983e5aad5eb9cc398b60933df2cc/plum_dispatch-2.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:a0d16daf09482a6588025e9f133a338d99474e8dd3c3dbbc4c5282a4fffe5a0e", size = 152624, upload-time = "2026-04-28T08:38:31.886Z" }, + { url = "https://files.pythonhosted.org/packages/64/a7/ee4d01d26032b060d379f44a29124180f756d907e3840d3bc450f8a0d2a7/plum_dispatch-2.9.0-py3-none-any.whl", hash = "sha256:5a516cdac5460343937a2b813562ac00b0eeac973ac023916e538610bdf34397", size = 45328, upload-time = "2026-04-28T08:38:33.022Z" }, +] + +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + +[[package]] +name = "pyerfa" +version = "2.0.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/71/39/63cc8291b0cf324ae710df41527faf7d331bce573899199d926b3e492260/pyerfa-2.0.1.5.tar.gz", hash = "sha256:17d6b24fe4846c65d5e7d8c362dcb08199dc63b30a236aedd73875cc83e1f6c0", size = 818430, upload-time = "2024-11-11T15:22:30.852Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/d9/3448a57cb5bd19950de6d6ab08bd8fbb3df60baa71726de91d73d76c481b/pyerfa-2.0.1.5-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:b282d7c60c4c47cf629c484c17ac504fcb04abd7b3f4dfcf53ee042afc3a5944", size = 341818, upload-time = "2024-11-11T15:22:16.467Z" }, + { url = "https://files.pythonhosted.org/packages/11/4a/31a363370478b63c6289a34743f2ba2d3ae1bd8223e004d18ab28fb92385/pyerfa-2.0.1.5-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:be1aeb70390dd03a34faf96749d5cabc58437410b4aab7213c512323932427df", size = 329370, upload-time = "2024-11-11T15:22:17.829Z" }, + { url = "https://files.pythonhosted.org/packages/cb/96/b6210fc624123c8ae13e1eecb68fb75e3f3adff216d95eee1c7b05843e3e/pyerfa-2.0.1.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0603e8e1b839327d586c8a627cdc634b795e18b007d84f0cda5500a0908254e", size = 692794, upload-time = "2024-11-11T15:22:19.429Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e0/050018d855d26d3c0b4a7d1b2ed692be758ce276d8289e2a2b44ba1014a5/pyerfa-2.0.1.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e43c7194e3242083f2350b46c09fd4bf8ba1bcc0ebd1460b98fc47fe2389906", size = 738711, upload-time = "2024-11-11T15:22:20.661Z" }, + { url = "https://files.pythonhosted.org/packages/b9/f5/ff91ee77308793ae32fa1e1de95e9edd4551456dd888b4e87c5938657ca5/pyerfa-2.0.1.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:07b80cd70701f5d066b1ac8cce406682cfcd667a1186ec7d7ade597239a6021d", size = 722966, upload-time = "2024-11-11T15:22:21.905Z" }, + { url = "https://files.pythonhosted.org/packages/2c/56/b22b35c8551d2228ff8d445e63787112927ca13f6dc9e2c04f69d742c95b/pyerfa-2.0.1.5-cp39-abi3-win32.whl", hash = "sha256:d30b9b0df588ed5467e529d851ea324a67239096dd44703125072fd11b351ea2", size = 339955, upload-time = "2024-11-11T15:22:23.087Z" }, + { url = "https://files.pythonhosted.org/packages/b4/11/97233cf23ad5411ac6f13b1d6ee3888f90ace4f974d9bf9db887aa428912/pyerfa-2.0.1.5-cp39-abi3-win_amd64.whl", hash = "sha256:66292d437dcf75925b694977aa06eb697126e7b86553e620371ed3e48b5e0ad0", size = 349410, upload-time = "2024-11-11T15:22:24.817Z" }, +] + +[[package]] +name = "pyfftw" +version = "0.15.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "setuptools", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4b/3f/ee1bc44b080fc1e81d293cd07bed563d254bc1997d63a3b8053804a87dfd/pyfftw-0.15.0.tar.gz", hash = "sha256:2f16b9854a40c8fdd10aa5803b24ddc6ab49f9cd559dbd7f07e7d61aa205c1ca", size = 164003, upload-time = "2024-11-06T16:01:19.293Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/66/99e0497a2dc8ff67aca6c23734895662b1885fce150a836de60ba9644ec6/pyFFTW-0.15.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:2c35205209dde186669b90eeda92ca6837dfff4814edc5aebb01536b32760eb0", size = 2835727, upload-time = "2024-11-06T16:00:34.678Z" }, + { url = "https://files.pythonhosted.org/packages/b3/2a/9eb5c7aa582bdb0ba25d0ef522281ffa5ff0e34a1cc9d0fb564ee019239a/pyFFTW-0.15.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:28bb5888342114950b2d7df268264863c5644f34130a7fdc0acabd05c885d486", size = 1272455, upload-time = "2024-11-06T16:00:36.636Z" }, + { url = "https://files.pythonhosted.org/packages/af/13/e1c59f3a659ce6712d999b5315c22e547a75f1cc5f171f3cdb9ceae13474/pyFFTW-0.15.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:5748f2d85d790684efd895ce8f3611cceedb75fde944c4788753e0ba1a88637a", size = 2449453, upload-time = "2024-11-06T16:00:38.491Z" }, + { url = "https://files.pythonhosted.org/packages/b6/b6/55a5033fd41d7b69d3ff9991d7f170f1f90a4463953a0c85011c7c87d739/pyFFTW-0.15.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:f8fe9c361bb69f58c9488d4d267047f94cfcea89ba7cd1ce6a148f807be88c6c", size = 3054268, upload-time = "2024-11-06T16:00:39.891Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f9/32cc147de14e30fd674a53097fc11e12a161bf88d120fbc4b5a3013e3ef1/pyFFTW-0.15.0-cp310-cp310-win32.whl", hash = "sha256:79a90799d4a4fc8d2c06b545e29a5e0575df81d0b9d131b20476a287f18817ee", size = 2234523, upload-time = "2024-11-06T16:00:41.222Z" }, + { url = "https://files.pythonhosted.org/packages/e0/dc/c469ccb4a06fa530cb34d7c61885cede184c3c0f39c873fb14bbc6c0450c/pyFFTW-0.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:80ed625c8e8b6c1ba3ba6741ca0c7584c62ac0d99d9d33d7b4617c5087977aa0", size = 2642503, upload-time = "2024-11-06T16:00:42.924Z" }, + { url = "https://files.pythonhosted.org/packages/17/6e/325833415acbb267b1533ea8c5dcd243df8b5bb3ecbc6e42778514859dfe/pyFFTW-0.15.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:facabac06cb2300fdfcb1cfab9af70b24380d24a725a9b4dbf8cdadf04c1a6c0", size = 2836851, upload-time = "2024-11-06T16:00:45.177Z" }, + { url = "https://files.pythonhosted.org/packages/11/cf/3e6e1fbb4dda9718682b38109018240a631f9078f659eea4add2189a6e6f/pyFFTW-0.15.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:730e86d23ea52a511fd3b0a0b8206dbe9f9f017522dabb838a7ca876e442ea8a", size = 1273108, upload-time = "2024-11-06T16:00:46.607Z" }, + { url = "https://files.pythonhosted.org/packages/41/b8/69fa1a05a9c1830fefa3805c0b67f43a5a6fa3d83950f2ab088b80826378/pyFFTW-0.15.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:b8ab3ad5b6fadf64e34be2fdc34c9cc2b767b33c7a1b0f3e778f4b3fac0b2566", size = 2506868, upload-time = "2024-11-06T16:00:48.197Z" }, + { url = "https://files.pythonhosted.org/packages/29/ca/f9035218cbf7986f214bbe57bfb1f2951d344a0baf601d8ee065d0422e07/pyFFTW-0.15.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:076c42a2b8841fc06fc592d812bfa41a85355dc7f79b6f51016ce981a35efc97", size = 3111543, upload-time = "2024-11-06T16:00:50.044Z" }, + { url = "https://files.pythonhosted.org/packages/1e/68/8c56e66ab9903440a41c3859409c2ec06b218c187c859c432d954002e778/pyFFTW-0.15.0-cp311-cp311-win32.whl", hash = "sha256:5c50580ac16173fb547d18ad089148f030ef48a0f1f3b2faf3bb522b7f9fd8b6", size = 2234192, upload-time = "2024-11-06T16:00:52.23Z" }, + { url = "https://files.pythonhosted.org/packages/91/cf/1b8ba1f0c8fb91f2caaf0ff30011a8289d7732b21cfc812b826222cafbaa/pyFFTW-0.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:553d62715e8e9ad20c6a6aa556846d785488f16fa2a518abcac58ae3c7087654", size = 2643265, upload-time = "2024-11-06T16:00:53.542Z" }, + { url = "https://files.pythonhosted.org/packages/08/5d/5d30a3a39bbe1e1d083d8efe23a62f1deacc445a82e3a55289b820f0ad9d/pyFFTW-0.15.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:27410a9ac0e4ad7821a564549be031d5422d9a96bb35ea63d2d324774f709b65", size = 2833540, upload-time = "2024-11-06T16:00:54.985Z" }, + { url = "https://files.pythonhosted.org/packages/4b/dc/ff9969669fcbff0972c1a96a116fa7efad763d136fc9a58f6112bf770688/pyFFTW-0.15.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:cd8bfb4a9e844693f8dd92ba97bc91d27404716b70580f846e80d74f6b092ffe", size = 1270715, upload-time = "2024-11-06T16:00:56.743Z" }, + { url = "https://files.pythonhosted.org/packages/c7/1c/30bc73495966285fdd3e79a4a7482dbdd863e505bf3021a455bf7b923423/pyFFTW-0.15.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:7d08f75ed35c0af097333b2de368a0b30460a3e7e7bed47780aac3c27f9ec77b", size = 2499664, upload-time = "2024-11-06T16:00:58.166Z" }, + { url = "https://files.pythonhosted.org/packages/6d/81/e5f5769f08aa3efa9527b328a9ebac7a2817f81a2b0f2266e79276686f31/pyFFTW-0.15.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:7ccae636ad163133bc5335d05f56afdb0c756ee013692c667b822a11e188a446", size = 3100310, upload-time = "2024-11-06T16:00:59.548Z" }, + { url = "https://files.pythonhosted.org/packages/60/5b/18c12dfb07b82a93b9b459380e2f118afd139cc564e1575624794b15e68d/pyFFTW-0.15.0-cp312-cp312-win32.whl", hash = "sha256:b4963a55b442c859ede2d247d84daa71c6c01b64e0d9b2730f5635b962777fb2", size = 2229927, upload-time = "2024-11-06T16:01:01.315Z" }, + { url = "https://files.pythonhosted.org/packages/dd/d2/cb3fc49bffb2f7cb49da6dbc67888c9c55df1aca6d11c6d1d000e37fc64f/pyFFTW-0.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:6ace61f34f0dd7973207c31ce16fd7bedf9a178bb956f157559f13384f219713", size = 2641096, upload-time = "2024-11-06T16:01:02.928Z" }, + { url = "https://files.pythonhosted.org/packages/a1/0d/90821a720638cbef79319463e3ce5d98f24153cee53316d4ecc1d8c0400d/pyFFTW-0.15.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:cfbeb106877db1b6bf527735647b861c86ac846ff47671d0d855a9be2de83368", size = 2832851, upload-time = "2024-11-06T16:01:04.301Z" }, + { url = "https://files.pythonhosted.org/packages/12/b7/e625f0cdb2bd65aa43ee35ce8cac1b0abc6c12871cd87fa83e404d82fd1d/pyFFTW-0.15.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:6d8913036a48ebcc9e3e1a315a6607e5cc31af4aee395aae180ff644c4658bbb", size = 1269651, upload-time = "2024-11-06T16:01:06.419Z" }, + { url = "https://files.pythonhosted.org/packages/0f/66/d12a9629908008ed97446c44ca2177c8f59e570a445da4136fdbb9e8db1c/pyFFTW-0.15.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:05324c8c092aa43868554daf6ddd7fd23eee18cd284ab68e749c62427c662737", size = 2496935, upload-time = "2024-11-06T16:01:07.82Z" }, + { url = "https://files.pythonhosted.org/packages/ad/c4/adcf010b151564401e3d401602b0e0cacae1e7cb816f0207397c3ddec626/pyFFTW-0.15.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:474f4a0ccfddfdfbe3e4a610b1c8f5968b58edf44434bf9d08ea52108a72df54", size = 3096170, upload-time = "2024-11-06T16:01:09.258Z" }, + { url = "https://files.pythonhosted.org/packages/b2/96/b1eaad07d5eaf026e2ed0c6f2afdf825628706162071d2665b3824727346/pyFFTW-0.15.0-cp313-cp313-win32.whl", hash = "sha256:40f8f3341546264178a8d9e8736e91554884595a683b71f8db8399907330f47d", size = 2229713, upload-time = "2024-11-06T16:01:10.902Z" }, + { url = "https://files.pythonhosted.org/packages/e9/23/d06a3e5b549f7537ada2b1fc2e5f7c05c38df70c843d463bc469812b1ebd/pyFFTW-0.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:bf04458c5d2fbfe5da270a4b667c59c5792cbe39300379d886acd8ea97fc55cc", size = 2640821, upload-time = "2024-11-06T16:01:12.234Z" }, + { url = "https://files.pythonhosted.org/packages/f2/5b/060fb5f48773dc1be3abd4d495a7b0fd02aeb6be57cb6bac214d1fd75447/pyFFTW-0.15.0-pp310-pypy310_pp73-macosx_12_0_x86_64.whl", hash = "sha256:e94576643406ffd4dc29851e4d493c8bfb3bde8390661b7fced050c1e6ff99ac", size = 2810336, upload-time = "2024-11-06T16:01:14.05Z" }, + { url = "https://files.pythonhosted.org/packages/46/5f/ee1692303b7cc15cdaf56861de6da8e12d001f4c45883832ffd26827b777/pyFFTW-0.15.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ca1dbb5e86121f24b637413584224dda129774d661be1640f092156f4088b3bf", size = 2570190, upload-time = "2024-11-06T16:01:15.496Z" }, + { url = "https://files.pythonhosted.org/packages/e1/19/b66be24aec24a1e59c8c7a04d58b446af15b809a53d70626acba7e865853/pyFFTW-0.15.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9ff6b656058f4099db30a5b5d01c7c1c6503c02a68f07398008a094c374d53c5", size = 2627837, upload-time = "2024-11-06T16:01:16.917Z" }, +] + +[[package]] +name = "pyfftw" +version = "0.15.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f2/2d/e38439b7f937e8bf91a9ff2b8d9713d0d8e64e980fc00e8d1945b8a5b74b/pyfftw-0.15.1.tar.gz", hash = "sha256:bbcde6d40d165e1cbaf12dde062ebfebe9e43394cac8c166e699ba2c9a4b0461", size = 192838, upload-time = "2025-10-22T19:58:56.683Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/c0/4f1a586eed5eba86c27d6fab4ebed44bbfeedd5209e835c19c6406f96a41/pyfftw-0.15.1-cp311-cp311-macosx_13_0_x86_64.whl", hash = "sha256:fb5283238be3c797b7b91cbb1d549787b9d0495a7d48b828832a3c94fa557f48", size = 3297881, upload-time = "2025-10-22T19:58:20.945Z" }, + { url = "https://files.pythonhosted.org/packages/b2/c1/02eaaf7a2f7ca26d7b56b6195617979b44b9213f3dcc1a514663a3bbc264/pyfftw-0.15.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:f4b42864e95f128a676e27a94025cce074d807a81d48ed423c65aa7403a7729a", size = 1682075, upload-time = "2025-10-22T19:58:22.236Z" }, + { url = "https://files.pythonhosted.org/packages/c6/23/e3692116f6e903d1d9705d1f948f94ee133512a0b388d05ea0f8adcb0c07/pyfftw-0.15.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e728dbe262182089a6606b7feb5c67bc55b8b385b0c1b60f71d6a891b5f142a", size = 2557646, upload-time = "2025-10-22T19:58:23.345Z" }, + { url = "https://files.pythonhosted.org/packages/7a/49/affa553caa93928d2f7d814f26939487d37d4068a0ba4d8238c6ba3000b1/pyfftw-0.15.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:294eecf05577b0198e6d041376e016254c4b54040b8a7a330d9d6321b8b30472", size = 3158494, upload-time = "2025-10-22T19:58:24.63Z" }, + { url = "https://files.pythonhosted.org/packages/88/92/bde713b499fa2062f30f396603bbf09acd0c9c974249638202c931433498/pyfftw-0.15.1-cp311-cp311-win32.whl", hash = "sha256:ed42033aa729520c66500027157525981613a139c2fd4671dd7d52d58cb4b820", size = 2226331, upload-time = "2025-10-22T19:58:26.345Z" }, + { url = "https://files.pythonhosted.org/packages/25/f4/e0583a68e1114b108354207ce0680b898a4b37c48bd3a8cfc1fd0ec1a476/pyfftw-0.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:e32a30c34f27c24ee602b89b22d9cd453570bf851e7d8f98506185cb5e99ccfc", size = 2634855, upload-time = "2025-10-22T19:58:27.839Z" }, + { url = "https://files.pythonhosted.org/packages/4f/76/5673561606f45c60ab3da497deead308e2c4b59c2b705334a01b880f0421/pyfftw-0.15.1-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:074926b5fa6a2193771cb4dc5beaa52ea1d629dc40da363d7de7918df5f3e951", size = 3315083, upload-time = "2025-10-22T19:58:28.981Z" }, + { url = "https://files.pythonhosted.org/packages/2d/fb/5042ad5c4ddc2db89844fc853b6358d36497e44f9d32f3aef9c3fa393182/pyfftw-0.15.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:2156d7bc72a2e2a1c747767909b1a7ba2bcfde14a51fca3171d7c1f8de6c2f02", size = 1698218, upload-time = "2025-10-22T19:58:30.536Z" }, + { url = "https://files.pythonhosted.org/packages/df/1c/7b76808da67a3775480f64e93acc0a623aad7682ba83428311de05ebc67a/pyfftw-0.15.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:313340683d5e99b0a36e45b8c2bd92cd86dfbb18917a62e89890927a66bed9e9", size = 2562434, upload-time = "2025-10-22T19:58:31.531Z" }, + { url = "https://files.pythonhosted.org/packages/fb/43/43fbc63ec6790ce6346e6441e82b54356334641d9d6f099f1ed75d302f2a/pyfftw-0.15.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900d79c7fa1b27b58ec4240f7cb6d512492a9db9bc8cbd18076ed28c84a63b62", size = 3167162, upload-time = "2025-10-22T19:58:32.636Z" }, + { url = "https://files.pythonhosted.org/packages/e4/e1/82d4967ef4ccd8c17e58a9a34fa283436de98c32c08aac4191ef56bf9989/pyfftw-0.15.1-cp312-cp312-win32.whl", hash = "sha256:9c94a6d251f2ceb9d6bb86964c43f9eb9cbd8612a60f41b10c8a64e816f6a2ed", size = 2222298, upload-time = "2025-10-22T19:58:33.795Z" }, + { url = "https://files.pythonhosted.org/packages/ab/c0/3f4513921fd3cb40986bc6fe4a33e87c5aef671edff483db6f7c4a5f8309/pyfftw-0.15.1-cp312-cp312-win_amd64.whl", hash = "sha256:74fe153d6bec682cd85b5601df09cb84ce6e3cc901172a3ce86da7544e457e4c", size = 2633571, upload-time = "2025-10-22T19:58:35.185Z" }, + { url = "https://files.pythonhosted.org/packages/c2/f5/5dca09b863e71db438069eb1990526a4409deebc190228177dec3b7cc636/pyfftw-0.15.1-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:a16c4eb45ff5277f8bcb979d43a6a0d7f2e7405dcad984dc45a30064ed487da9", size = 3315212, upload-time = "2025-10-22T19:58:36.273Z" }, + { url = "https://files.pythonhosted.org/packages/56/c1/1ff9aa930f98c77bbff9cae122e496f1fd7201abac309f1dbcc9fbe5c7ee/pyfftw-0.15.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:2b4bad0184546e3129eeda9d07541eca71232f6e431d57c734930d25f06386e8", size = 1698270, upload-time = "2025-10-22T19:58:37.684Z" }, + { url = "https://files.pythonhosted.org/packages/9a/3f/e94d5773429dc076ec4b47d029603de1728a5037024ed3dbcc853cfa72e9/pyfftw-0.15.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1716378d1d102527917b872a5baf15e2e3de52d5200ccc22128816cd71c33148", size = 2559574, upload-time = "2025-10-22T19:58:38.662Z" }, + { url = "https://files.pythonhosted.org/packages/cb/64/5f773d61cca91a1a741e2403d7191610513d0cca3fc73a41a6941a31fe89/pyfftw-0.15.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:16209de9a244ab7b3337e8ce8d528420ffd05881e7d19e5be21d2546a7e5b2c1", size = 3166906, upload-time = "2025-10-22T19:58:39.986Z" }, + { url = "https://files.pythonhosted.org/packages/c4/8a/dda252557803fde20be41396425a85faf6680eaeed3996e4b765745e9206/pyfftw-0.15.1-cp313-cp313-win32.whl", hash = "sha256:db2a65c59d7a707c55a8f0f38be3916b907429ceed316c6875201b202e22ac99", size = 2222209, upload-time = "2025-10-22T19:58:41.237Z" }, + { url = "https://files.pythonhosted.org/packages/20/6b/9236a2fb77b01b00b957304d53694de2bf034caac89c1d35ab7fa3421fd9/pyfftw-0.15.1-cp313-cp313-win_amd64.whl", hash = "sha256:dcac51a5a4b8c6600bb2c7edb86d9739860a61bb0b076e20fbf0340919da307d", size = 2633673, upload-time = "2025-10-22T19:58:42.709Z" }, + { url = "https://files.pythonhosted.org/packages/3f/5c/ec4cef958e936ac61528644825d052c1c486192aeffadf071071a17cd86a/pyfftw-0.15.1-cp314-cp314-macosx_13_0_x86_64.whl", hash = "sha256:c4e49219794bfe99c7db94d0b392c86ff979db4da2d27d444edbc6de1519ddae", size = 3313756, upload-time = "2025-10-22T19:58:44.002Z" }, + { url = "https://files.pythonhosted.org/packages/d3/68/39e06bab39edf6473653670c6c25fe971a7b369a2059ddc971ac5f872653/pyfftw-0.15.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:489a6364a613935736d43da67791ab4dfcf4875e968a8b46e99983afde7ec960", size = 1697163, upload-time = "2025-10-22T19:58:45.592Z" }, + { url = "https://files.pythonhosted.org/packages/dc/4d/9ae0dfaf5174a13714cfc5377165de5703230c1df96715cfee9d19fb0b4c/pyfftw-0.15.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:379844360c03402f4d2a738fa19924153d5467cdc88aa3752b12bbc1403512b0", size = 2561755, upload-time = "2025-10-22T19:58:46.773Z" }, + { url = "https://files.pythonhosted.org/packages/d5/3b/bc991e3abf4f6ad9907658e3b282530d345027fa294b510a9eb1fce882c4/pyfftw-0.15.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:398a342a689061bfc4bfe7271a973e093f78176a632fb4e523b3ed5b72dba4c6", size = 3156174, upload-time = "2025-10-22T19:58:47.999Z" }, + { url = "https://files.pythonhosted.org/packages/5c/5d/c0a9782c333e8b5de334285e3adc0542f2265107d0c9b720f5730bc43471/pyfftw-0.15.1-cp314-cp314-win32.whl", hash = "sha256:375ec8b11140eb30262bf9ecfa34d043a42d036c33c66e24bfe734f5aae7ddf0", size = 2245736, upload-time = "2025-10-22T19:58:49.422Z" }, + { url = "https://files.pythonhosted.org/packages/ef/c7/ba88f0b5d81ae0732a2cb2900c52924ddfa6b1623e511533c022a558e058/pyfftw-0.15.1-cp314-cp314-win_amd64.whl", hash = "sha256:558c318a1aea81ce2083309c927fd881ccf9c285c5571ce965bb7b18dc4291fd", size = 2660474, upload-time = "2025-10-22T19:58:50.93Z" }, + { url = "https://files.pythonhosted.org/packages/88/45/a34cd015d16002e748de0e218d24ecc2f30eb9ad073f1c7941a51a796a2e/pyfftw-0.15.1-pp311-pypy311_pp73-macosx_13_0_x86_64.whl", hash = "sha256:41a25b721e79378a3ea29d21d2e6cf25e43c1f44bc0bbf554beccdcdb2b8064e", size = 3278795, upload-time = "2025-10-22T19:58:52.212Z" }, + { url = "https://files.pythonhosted.org/packages/15/27/0e6195c9c5eeef73a7499772621057aff5ffd04ebb52a71d49cc049a3dad/pyfftw-0.15.1-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e957f5caa7ccc032d17831ce336cb61b93f516ed7da25ea6c9e1086ed2e323f", size = 2570541, upload-time = "2025-10-22T19:58:53.357Z" }, + { url = "https://files.pythonhosted.org/packages/2e/a9/f1d00acce882645198576a94c4b7fff5465f0bb6c4b7bb1005314527c2d0/pyfftw-0.15.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:763cd58e858133493db371ed3a63e9e41463cd40965e2375a079947c2d96805f", size = 2625398, upload-time = "2025-10-22T19:58:54.893Z" }, +] + +[[package]] +name = "pygments" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, +] + +[[package]] +name = "pyjwt" +version = "2.12.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c2/27/a3b6e5bf6ff856d2509292e95c8f57f0df7017cf5394921fc4e4ef40308a/pyjwt-2.12.1.tar.gz", hash = "sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b", size = 102564, upload-time = "2026-03-13T19:27:37.25Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/7a/8dd906bd22e79e47397a61742927f6747fe93242ef86645ee9092e610244/pyjwt-2.12.1-py3-none-any.whl", hash = "sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c", size = 29726, upload-time = "2026-03-13T19:27:35.677Z" }, +] + +[[package]] +name = "pyparsing" +version = "3.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" }, +] + +[[package]] +name = "pytest" +version = "9.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, +] + +[[package]] +name = "pytest-requires" +version = "0.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ab/fc/2fc8d97d40b3bb74d93ba7baf06644f2e53daf31345721b5548acb2123e8/pytest-requires-0.1.0.tar.gz", hash = "sha256:1d7ee62cffcd4d6ba16d21e5f96fc6d9006ef94f923d64e278d8b5fddbec924e", size = 4718, upload-time = "2021-12-21T16:55:26.774Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/bf/551afa01bc600ba472489a2269f87a8396424d965b864415a41494c541bd/pytest_requires-0.1.0-py3-none-any.whl", hash = "sha256:f13f3a7dd2d1f0df4affd2873719d0836f27012cea1a04efe6c7c117d4ec53cf", size = 4355, upload-time = "2021-12-21T16:55:24.761Z" }, +] + +[[package]] +name = "pytest-rerunfailures" +version = "16.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6b/27/fd0209642f3a1069da3e0be3c7e339f942d052d81ccb1fb4eb9b187d3633/pytest_rerunfailures-16.2.tar.gz", hash = "sha256:5f5a32f15674a3d54f7598388fcd3cc1bc5c37284731a4704a44485dcdda5e23", size = 32121, upload-time = "2026-05-13T08:13:26.998Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/a5/d8c1ad74529b483044b787ead2d24ecc624bca4084a509002102e4bab8cc/pytest_rerunfailures-16.2-py3-none-any.whl", hash = "sha256:c22a53d2827becc76f057d4ded123c0e726523f2f0e5f0bb4efb31fd59e1f14e", size = 14505, upload-time = "2026-05-13T08:13:25.485Z" }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "pytz" +version = "2026.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/46/dd499ec9038423421951e4fad73051febaa13d2df82b4064f87af8b8c0c3/pytz-2026.2.tar.gz", hash = "sha256:0e60b47b29f21574376f218fe21abc009894a2321ea16c6754f3cad6eb7cdd6a", size = 320861, upload-time = "2026-05-04T01:35:29.667Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/dd/96da98f892250475bdf2328112d7468abdd4acc7b902b6af23f4ed958ea0/pytz-2026.2-py2.py3-none-any.whl", hash = "sha256:04156e608bee23d3792fd45c94ae47fae1036688e75032eea2e3bf0323d1f126", size = 510141, upload-time = "2026-05-04T01:35:27.408Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" }, + { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" }, + { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" }, + { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" }, + { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" }, + { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" }, + { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" }, + { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, + { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, + { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, + { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, + { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, + { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, + { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, + { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, + { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, +] + +[[package]] +name = "regex" +version = "2026.5.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/0e/49aee608ad09480e7fd276898c99ec6192985fa331abe4eb3a986094490b/regex-2026.5.9.tar.gz", hash = "sha256:a8234aa23ec39894bfe4a3f1b85616a7032481964a13ac6fc9f10de4f6fca270", size = 416074, upload-time = "2026-05-09T23:15:19.37Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/ed/0ad2c8edf634918eb4484365d3819fa7bd7f58daf807fe7fb21812c316e5/regex-2026.5.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a9e1328e17c84c1a5d22ec9f785ecef4a967fab9a42b6a8dc3bcbebd0a0c9e44", size = 489438, upload-time = "2026-05-09T23:11:29.374Z" }, + { url = "https://files.pythonhosted.org/packages/89/a9/4ed972ad263963b860b7c3e86e0e1bcc791def47b43b8c8efe57e710f139/regex-2026.5.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bfe1ce50cbfb569d74e1e4337da6468961f31dbea55fd85aa5de59c0947a805a", size = 291270, upload-time = "2026-05-09T23:11:33.254Z" }, + { url = "https://files.pythonhosted.org/packages/16/81/075930d9fa28c4ea1f53398dd015ee7c882f623539759113cda1257f4b82/regex-2026.5.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15ee42209947f4ca045412eae98416317238163618ace2a8e54f99586a466733", size = 289198, upload-time = "2026-05-09T23:11:35.769Z" }, + { url = "https://files.pythonhosted.org/packages/d4/c8/5cdfbf0b5dc6599e1b6131eff43262e5275d4ec3469ce10216061659aadb/regex-2026.5.9-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4bb445ff3f725f59df8f6014edb547ee928ec7023a774f6a39a3f953038cbb2", size = 784765, upload-time = "2026-05-09T23:11:37.689Z" }, + { url = "https://files.pythonhosted.org/packages/cd/ca/ae5fd6edc59b7f84b904b31d6ec39a860cbcecd10f64bd5a062ca83a4864/regex-2026.5.9-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:446ddd671e43ab535810c4b21cff7104945c701d4a14d1e6d1cd6f4e445a8bea", size = 852115, upload-time = "2026-05-09T23:11:39.973Z" }, + { url = "https://files.pythonhosted.org/packages/f6/ce/a91cf555afb51f3b74a182e24ba073b91ea7bb64592fc4b315c111bb19fd/regex-2026.5.9-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7b92817338591505f282cf3864c145244b1edcf5381d237038df955001091538", size = 899503, upload-time = "2026-05-09T23:11:42.48Z" }, + { url = "https://files.pythonhosted.org/packages/55/7f/725a0a2b245a4cf0c4bab29d0e97c74285d94136a65d1b55a6459a583502/regex-2026.5.9-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6b8a143aca6c39b446ea8092cde25cc8fe9304d4f5fecfbc1a9dbb0282703c2", size = 794093, upload-time = "2026-05-09T23:11:44.681Z" }, + { url = "https://files.pythonhosted.org/packages/e3/2a/996efbd59ce6b5d4a09e3af6180ceb62af171f4a9a6fb557d2f0ae0d462b/regex-2026.5.9-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0f03aa6898aaaac4592479821df16e68e8d0e29e903e65d8f2dfb2f19028a989", size = 786234, upload-time = "2026-05-09T23:11:46.882Z" }, + { url = "https://files.pythonhosted.org/packages/4b/0a/8731e8b8806174c9cdd5903f80a14990331c1f42fc4209b540952e9e010d/regex-2026.5.9-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ed457d8e98ae812ed7732bef7bf78de78e834eae0372a74e23ca90ef21d910f9", size = 769895, upload-time = "2026-05-09T23:11:49.324Z" }, + { url = "https://files.pythonhosted.org/packages/9a/0b/932473194bd563f342a412ae2ffbbd6da608306a2bc4e99249a41c2b0b92/regex-2026.5.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:71b61c5bfe1c806332defc42ad6c780b3c55f661986d7f40283a3a88274b4c00", size = 774991, upload-time = "2026-05-09T23:11:51.261Z" }, + { url = "https://files.pythonhosted.org/packages/98/80/9523d196010031df25f7177ee0a467efbee436324038e5d99def17a57515/regex-2026.5.9-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:3b1e39888c5e0c7d92cea4fc777396c4a90363b05de75d02eb459a4752200808", size = 848790, upload-time = "2026-05-09T23:11:53.232Z" }, + { url = "https://files.pythonhosted.org/packages/3c/07/56987b35e89edf47e4a38cf2845aeee476bfa688a6bdbd3e820cda461dc1/regex-2026.5.9-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:6ba42b2e7e7f46cf68cc6a5ca36fa07959f9bbd9c6bdcc47b6ee76549a590248", size = 757679, upload-time = "2026-05-09T23:11:55.82Z" }, + { url = "https://files.pythonhosted.org/packages/04/2a/ff713fff0c566507c06a4ce2dc0ae8e7eeebc88811a95fc81cf1e7d534dd/regex-2026.5.9-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:c010eb8caca74bdb40c07498d7ece26b4428fd3f04aa8a72c9ac6f79e8faaac6", size = 837116, upload-time = "2026-05-09T23:11:57.934Z" }, + { url = "https://files.pythonhosted.org/packages/77/90/df6d982b03e3614785c6937ba51b57f6733d97d2ee1c9bc7531dbfab3a54/regex-2026.5.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a6a563446a41adc451393dc6b8e6ad87979efaee3c8738690a8d1b08ebead1b4", size = 782081, upload-time = "2026-05-09T23:11:59.607Z" }, + { url = "https://files.pythonhosted.org/packages/c7/8a/4e88a5f7c3e98489aac4dd23142723d907b2a595b4a6abcbacabefeded09/regex-2026.5.9-cp310-cp310-win32.whl", hash = "sha256:954cc214c04663ee6d266fc61739cad83054683048de65c5bd1d640ad28098ac", size = 266247, upload-time = "2026-05-09T23:12:01.116Z" }, + { url = "https://files.pythonhosted.org/packages/6a/40/4b224cb0582b2dca1786726e6cdabe26abbf757d7f6718332f186da155d2/regex-2026.5.9-cp310-cp310-win_amd64.whl", hash = "sha256:b310768746dd314ea6e2ff4cc89ef215426813396ff4e94ee8e6f7096c8b6e03", size = 278416, upload-time = "2026-05-09T23:12:03.2Z" }, + { url = "https://files.pythonhosted.org/packages/12/4d/014fbe803204cab0947ee428f09f658a29632053dde1d3c6176bb4f0fd4c/regex-2026.5.9-cp310-cp310-win_arm64.whl", hash = "sha256:19c16ceb4a267a8789e25733e583983eeab9f0f8664e66b0bd1c5d21f14c2d4b", size = 270413, upload-time = "2026-05-09T23:12:04.649Z" }, + { url = "https://files.pythonhosted.org/packages/c2/dc/c1f2df4027e82fc54b5a473e4b250f5139faca49a0fbe29a48668d228f34/regex-2026.5.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ccf5249114cc3e772ecdd88a98a86eca0fd74c61ce32a94743758c083fc05d48", size = 489445, upload-time = "2026-05-09T23:12:06.111Z" }, + { url = "https://files.pythonhosted.org/packages/03/d2/59f01110660081cce9c0bc30ebd0b5ee250dacf658e3248ed92f01e0e8ee/regex-2026.5.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46f1326ca6e65b0879d23ca302c0f2415aad42ff0309b9c818e7949fe19a41d8", size = 291271, upload-time = "2026-05-09T23:12:07.731Z" }, + { url = "https://files.pythonhosted.org/packages/58/b6/14b2c84ff90ddb370c81d27503f4a0fcf071496416f4855f6cc8c5d81c35/regex-2026.5.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ef31cbfe458e21c6122ba8150ff060e0c7789ed0d26eb423f25472584920b555", size = 289212, upload-time = "2026-05-09T23:12:09.266Z" }, + { url = "https://files.pythonhosted.org/packages/03/d0/4db86529117320de0c84afd90e70bb47434625875e34fcef9d8c127c5b16/regex-2026.5.9-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:992604d02e6d9c6d786c24a706a71ecffe1020fc1ef264044474cd81fa2c3919", size = 792310, upload-time = "2026-05-09T23:12:11.416Z" }, + { url = "https://files.pythonhosted.org/packages/07/78/fe4800cd322f862ecffd2d553409b20d80650e5ed71b9d178f853d020b82/regex-2026.5.9-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c9411dd64ca95477225734a93dfc8583b51916b8d5942f99d6cac21e09965451", size = 861721, upload-time = "2026-05-09T23:12:13.681Z" }, + { url = "https://files.pythonhosted.org/packages/b5/d0/b3618a895dd8feb897c61bb2954edd265e1767d82a01d53065d5871127a3/regex-2026.5.9-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3dd4a3ff360dfb836fecdb93a4598f9d6e2ac81e3e397125145c6221bf58cf4c", size = 906460, upload-time = "2026-05-09T23:12:15.443Z" }, + { url = "https://files.pythonhosted.org/packages/33/6f/1481597e859ef19508b345eec4afd1416ed6e6b459c75a64026ef193aecf/regex-2026.5.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2a661a7d270a61f7cf460caee8b9fa2d5ef9e5c681234bcb9e0fe14f488e7dfc", size = 799843, upload-time = "2026-05-09T23:12:16.892Z" }, + { url = "https://files.pythonhosted.org/packages/73/59/955734c803f59108deccba3597ae440c76b62a652733c0006e6243758420/regex-2026.5.9-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f079e50a0d3cc3cd5091fa9ff45869a2e6b2cd35895731edafb0327901a8d86d", size = 773610, upload-time = "2026-05-09T23:12:19.127Z" }, + { url = "https://files.pythonhosted.org/packages/68/8f/70c04a236d651c81881dac42ef8538bddda6121434509d0a22d9e601503b/regex-2026.5.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4ebe8f0b5ec5a5024dc4a4c59f444c4e9afc5f2abdbb8962065b75d27fb971f9", size = 781645, upload-time = "2026-05-09T23:12:20.806Z" }, + { url = "https://files.pythonhosted.org/packages/1d/96/05c7434d88185e5d27fe54aeb74df86bd77cd79f52f0b4eae54faa8fea70/regex-2026.5.9-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:97cf3bc1b7d7d2306772ec07366c80d9df00ff79e79cea32898883a646d2fae2", size = 854473, upload-time = "2026-05-09T23:12:22.465Z" }, + { url = "https://files.pythonhosted.org/packages/4e/c1/6e3d8202d981f3117004bf341ee74893ba4ba8a9fbaf4b94615846550a08/regex-2026.5.9-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0f9eede6a5cbdc02d4978090186390936e1776a7d1359b21e41014c609880bcf", size = 763311, upload-time = "2026-05-09T23:12:24.351Z" }, + { url = "https://files.pythonhosted.org/packages/93/c7/e7737f1526b3fb32bd4c337fd6c71c3ebb5c8296fc34d11197e0955d2e35/regex-2026.5.9-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:01f0f5f55f4b64dacec85dc116d3c05fd23ad3ff037bbc73a2085775953c2611", size = 844593, upload-time = "2026-05-09T23:12:26.341Z" }, + { url = "https://files.pythonhosted.org/packages/a5/27/0daffb1a535bb39f422c3d200f4ab023c71110ad66a32b366bee708baba0/regex-2026.5.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1268eddd8486dc561d08eee1156e40aa3a8fe10f4bdec8fa653b455fcbffd12c", size = 789167, upload-time = "2026-05-09T23:12:27.975Z" }, + { url = "https://files.pythonhosted.org/packages/ce/fc/294fe4fac4f2ed67207b17471815870c1c45b3a489e08e0ac96daea16ef6/regex-2026.5.9-cp311-cp311-win32.whl", hash = "sha256:8676474c07469d6f33dd1085ca2cd45f65785f32518f2b20e36d9953ca07f994", size = 266249, upload-time = "2026-05-09T23:12:30.141Z" }, + { url = "https://files.pythonhosted.org/packages/d0/b0/8dce459f6245bcf8f6e9f23ac9569f1a0f15c131cc0745e82b43226204cf/regex-2026.5.9-cp311-cp311-win_amd64.whl", hash = "sha256:246de9d60aa3f8538b519834dd95cbf276ea263d6a7bd5a3666dc3fa0230505b", size = 278423, upload-time = "2026-05-09T23:12:31.676Z" }, + { url = "https://files.pythonhosted.org/packages/db/8d/f9aeff6ad63a3ef720386f2907e6d34a35a510a6e498ebad28b0fb3f6ab6/regex-2026.5.9-cp311-cp311-win_arm64.whl", hash = "sha256:d726ca3f0d76969bf1e8e477d160d3d666bbf999f6860bd314889e5345782046", size = 270420, upload-time = "2026-05-09T23:12:33.194Z" }, + { url = "https://files.pythonhosted.org/packages/50/9b/6550044bc44e17c84d312c031c2ec42fbdb6a4ec4e29093be3a172d08772/regex-2026.5.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:57eeeb05db7979413dec5438f2db21d7ecbba787cde7a711df1a6f6df672aa06", size = 490451, upload-time = "2026-05-09T23:12:34.72Z" }, + { url = "https://files.pythonhosted.org/packages/1e/95/fc7ba4303b5a0f92446a12ee6778ef2c6c799233f5060042a31bf390cfe9/regex-2026.5.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:398c521292f4c7fb807001dcd54694d3a1fcafc179a36ad9cc56f98df85930b6", size = 292112, upload-time = "2026-05-09T23:12:36.285Z" }, + { url = "https://files.pythonhosted.org/packages/54/4b/ee27938d1b2c443e89a9a10e00d2d19aa5ee300cd3d61140644e93bb083e/regex-2026.5.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f7a7c26137296beba7784de6eba69c6a93a63ccebc385e4962fe67e267a91225", size = 289599, upload-time = "2026-05-09T23:12:38.089Z" }, + { url = "https://files.pythonhosted.org/packages/d8/dd/ba103dc19614e25f3880800ca67ce093d6e21b325d72b8383c7bf906e9fa/regex-2026.5.9-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6441cc660d76107934a09c22167200839a0e89604a6297f78a974e66e931d2c0", size = 796732, upload-time = "2026-05-09T23:12:40.062Z" }, + { url = "https://files.pythonhosted.org/packages/cf/e7/f035b4fd858b050b0080bf302968dc0f59ba34e391872d54936758e6844e/regex-2026.5.9-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:91328f1c23d47595ca3ef0a7557fa129c5a23404b775c770697d2f35b33e0107", size = 865440, upload-time = "2026-05-09T23:12:42.059Z" }, + { url = "https://files.pythonhosted.org/packages/0a/51/8cd301ecc899aea28124357f729f4272f44de7806fc7ca02490bfbe253e8/regex-2026.5.9-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:93a7860539414dddaefba2b40f8771765ae17949d4c7182b876ce429e11a8309", size = 912329, upload-time = "2026-05-09T23:12:44.373Z" }, + { url = "https://files.pythonhosted.org/packages/cc/1e/3fbe2fa1e8cebd62f3bb7d3321cff1640aca2e240b51d9bd624aad949260/regex-2026.5.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd2810d22146b6d838acc5ec15602cb6b47920aa4e33015df3868eedfd20bab8", size = 801239, upload-time = "2026-05-09T23:12:46.268Z" }, + { url = "https://files.pythonhosted.org/packages/17/2f/6f6008682bf2cf98040a0d3153a8e557b6ab728d7713d045cee4ce544ab8/regex-2026.5.9-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:daff2bdbaf1d23e52fdff7c0b7bc2048b68f978df6a4d107ac981f94caef2e66", size = 777054, upload-time = "2026-05-09T23:12:48.051Z" }, + { url = "https://files.pythonhosted.org/packages/19/2b/eee0d20a6842ba04df4b8847a920b57ef56853f14ef85405473e586b605a/regex-2026.5.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4eeb011098fcb77af513dcef521a3dbecbf8849b1e38940759d293b7a93f5026", size = 785098, upload-time = "2026-05-09T23:12:49.851Z" }, + { url = "https://files.pythonhosted.org/packages/4a/98/6fc1e6410feefb92159edaed5041992bfe390e8d26c721865434acbca558/regex-2026.5.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ea9c8ecfa1b73c73b626534d6626e5340d429630943672b8480724f44e84b962", size = 860095, upload-time = "2026-05-09T23:12:51.666Z" }, + { url = "https://files.pythonhosted.org/packages/18/a3/bd855e0f2cb1a978ecf6fa6bb69632dd9c3f6ea3b81cde62fde14c9daec7/regex-2026.5.9-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:cd2846168eb9ee3c513902bc8225409cb1caab31d04728b145171fa1625d9621", size = 765762, upload-time = "2026-05-09T23:12:53.413Z" }, + { url = "https://files.pythonhosted.org/packages/dc/66/0ae8c092e60b14c79d24f8e0b7f0aea5bfbffdcab00b5483d13404d3c3a5/regex-2026.5.9-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:39617fb0cde9c0e6306dc70e3bfc096f3da793219879f7ae7aa341a69fbdcf6d", size = 852100, upload-time = "2026-05-09T23:12:55.256Z" }, + { url = "https://files.pythonhosted.org/packages/21/de/8dfde60fc1b21c946a893ba273403b72617edb261370cb1087099a83f088/regex-2026.5.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fd03c4f0e33280d15cae17159b899245d6b7c53d21def19b263b39655061f5ce", size = 789479, upload-time = "2026-05-09T23:12:57.573Z" }, + { url = "https://files.pythonhosted.org/packages/c3/1c/bdcc98f9a4af4fdd166c74941174619ccff4726d3ce32faa8e9a2ecd38dd/regex-2026.5.9-cp312-cp312-win32.whl", hash = "sha256:164eba9b755ea6f244b0d881196fbc1fac09714e9782c9e2732b813142033c8e", size = 266699, upload-time = "2026-05-09T23:12:59.14Z" }, + { url = "https://files.pythonhosted.org/packages/78/87/240d36864f9e48ace85f72e79ced97ceb7f27ce87739a947dcb834b4e6bc/regex-2026.5.9-cp312-cp312-win_amd64.whl", hash = "sha256:86f40a5d6444db30a125c9c9177e6b25dad981cbc37451fd838f145e6edac92e", size = 277783, upload-time = "2026-05-09T23:13:00.789Z" }, + { url = "https://files.pythonhosted.org/packages/4f/b5/7b30f312b0669dff5beebe5b0989dc2d1a312b1a44fab852199c387a5b96/regex-2026.5.9-cp312-cp312-win_arm64.whl", hash = "sha256:96f5f58b54a063d7ea9dca08e1cf57bfe10499c4d579ee672da284f57f5f0070", size = 270513, upload-time = "2026-05-09T23:13:02.426Z" }, + { url = "https://files.pythonhosted.org/packages/aa/da/797e91ecec6f84135da778ddce78c20e0af5d2a15c26f87a81bc3eadb6db/regex-2026.5.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d626b84406444b165fc0ba981604edea39f0588ff1f92baa23fe50799ea9afdb", size = 490303, upload-time = "2026-05-09T23:13:04.382Z" }, + { url = "https://files.pythonhosted.org/packages/44/da/bf30abaaa737b58f4a4b8c4a03659e02fd92092c822e0197ed9e0daab917/regex-2026.5.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d7bdc0ab8f3dd7e1b4f9ab88634e13374669db86bb3c72e8292f07ae313f539f", size = 292019, upload-time = "2026-05-09T23:13:06.022Z" }, + { url = "https://files.pythonhosted.org/packages/2d/e7/d0eaf5713828417b9e5648cf81fa9bacd4961f6ab98c380c2034f8716e35/regex-2026.5.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a8820737949116ffff55fe18f9fc644530063ba6ebfcb8314239416e78f1347c", size = 289468, upload-time = "2026-05-09T23:13:08.214Z" }, + { url = "https://files.pythonhosted.org/packages/d3/9b/b3fdd62b003baa1a9b593cd8c8699c9651c2e80cc21a5c715707983c42d7/regex-2026.5.9-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa0fbdbac82cb3e4450d0ccde7d7a35607f4cb2dd9fba4b8b69bfaf8c9fa6aed", size = 796749, upload-time = "2026-05-09T23:13:10.573Z" }, + { url = "https://files.pythonhosted.org/packages/d4/30/66ab84588765f5b4b271a9ca09ef7ce2b87caa95176ec3d2ad65d7bc4902/regex-2026.5.9-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:57e8915c7986aa33d25e4d3629cef711cd2863f2961b10409f0c04cb8b7d9020", size = 865445, upload-time = "2026-05-09T23:13:12.523Z" }, + { url = "https://files.pythonhosted.org/packages/1a/89/f05169e8588aac365f35ffc7f3bc3184f095ef4cfded7cfaa3c7fd5dbd89/regex-2026.5.9-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:508f56a89ba9cb26e4168cbc37dbd60a28d82430a9e18ad1d25fe0883c314ca2", size = 912322, upload-time = "2026-05-09T23:13:14.281Z" }, + { url = "https://files.pythonhosted.org/packages/30/e1/c93444052cf41581f3c884ab3fb5823daf0992f11cd4388d4275ca610558/regex-2026.5.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6d189041f15691cfa2b6c4290448ec221244d225b3f5fe9e7771b34ffcdf6e2", size = 801269, upload-time = "2026-05-09T23:13:16.569Z" }, + { url = "https://files.pythonhosted.org/packages/50/fe/0cf96b882f540e62e8b9956599798203d599c44cf4c77917ca27400ff69b/regex-2026.5.9-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e82db382b44d0111b22601c509c89f64434816c9e0eef9d1989cda8cc6ff1c04", size = 777085, upload-time = "2026-05-09T23:13:18.675Z" }, + { url = "https://files.pythonhosted.org/packages/23/5c/d78d4924e7fc875557b9e9b768423925fdfaac5549d06da7810019a9bd26/regex-2026.5.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2acfb48634f64996b57f90f39afa692ff362162722581921fe92239a59960f3c", size = 785153, upload-time = "2026-05-09T23:13:20.525Z" }, + { url = "https://files.pythonhosted.org/packages/bf/e0/5214774090e7b4524dcea3e3c4aa74141d43043f8beb49c1599db1c8b53a/regex-2026.5.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d29eebfc9525db68cad3c97eedd7f754fa265aa5cd0cf4f863b2421e1b48fc9f", size = 860164, upload-time = "2026-05-09T23:13:22.263Z" }, + { url = "https://files.pythonhosted.org/packages/6e/e1/4a57a83350319b1271f0d7a249b8672513ed928b237a741631270de6caea/regex-2026.5.9-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:debb893095e944091c16e641a6e33c1b0f4cb61ab945ec5afbf53ce7068834d8", size = 765731, upload-time = "2026-05-09T23:13:24.277Z" }, + { url = "https://files.pythonhosted.org/packages/12/f4/499e74a20c156fc75836ee04a72a38d1a063978f600937f9760467beb1b0/regex-2026.5.9-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d659eee77986549c9ea45b861c7567e44d6287c3dc9a4565478853f7b9fe2ff6", size = 852062, upload-time = "2026-05-09T23:13:26.125Z" }, + { url = "https://files.pythonhosted.org/packages/5b/92/7eebc0d0a01e78629695f342ba17e0deaff8fb45e79cc0d7b98287da6e3e/regex-2026.5.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2efa205e6d98b24d1f3ab395c11aa15cdf10935bca283d0285e0499c284fba21", size = 789577, upload-time = "2026-05-09T23:13:27.814Z" }, + { url = "https://files.pythonhosted.org/packages/05/a4/018e71f7d2ad48c1ebe6d3ae0026f9b7cb4802fd15c7cc02fdf724355102/regex-2026.5.9-cp313-cp313-win32.whl", hash = "sha256:f3844f134e834076677dd369976e9f5068679fcb8e50102fdf6b7ac96a3ec127", size = 266691, upload-time = "2026-05-09T23:13:29.549Z" }, + { url = "https://files.pythonhosted.org/packages/e6/1d/861a93719fb9ee7dbfc3761b3797b7a3e112a5d42c6129459d2d741be9b5/regex-2026.5.9-cp313-cp313-win_amd64.whl", hash = "sha256:3527bb4942d2c14552155406cdedd906567456821848aed1cb4933a391bf5eca", size = 277747, upload-time = "2026-05-09T23:13:31.859Z" }, + { url = "https://files.pythonhosted.org/packages/d9/c6/0a2436ae4da1ba76e51cb98943c6838a9a721faa40ebe2dce07694ae34e3/regex-2026.5.9-cp313-cp313-win_arm64.whl", hash = "sha256:56a33f191f17d8c417f99945ebdc1e691d3af9605d86ec68c7e54a57e3e17af6", size = 270500, upload-time = "2026-05-09T23:13:33.525Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e9/d21346f7b60ed58789371358ed66b09d00f832e1bd7c06e55d9da5679882/regex-2026.5.9-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:01f28d868834624c934b8d2e0aa1c8341337e37831f4a012f18a5afcba4cbaf3", size = 494172, upload-time = "2026-05-09T23:13:35.935Z" }, + { url = "https://files.pythonhosted.org/packages/c4/43/fd1177a2032037c681baecdb3422ee4e1424aec4e4f470ef47793d325274/regex-2026.5.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:48036f6374aaa79eb3b754ec29c61d1c6b1606749d705a13f8854fa2539671f6", size = 293952, upload-time = "2026-05-09T23:13:38.307Z" }, + { url = "https://files.pythonhosted.org/packages/f2/7d/9fbf919768368d3f8a4f6c692cf2aa61e482b2b81ec6a298ace4cbf02480/regex-2026.5.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b96350aa424e79d4fd6b567b344dcbe2b2d6bfc48dfe7717587e1fa6d43da6ff", size = 292314, upload-time = "2026-05-09T23:13:40.353Z" }, + { url = "https://files.pythonhosted.org/packages/e2/6c/e41bfeecb589716843e7c4df09ba46ff2a42961457afece19059d85caeef/regex-2026.5.9-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8f3af7a4903c5c04a11a196a5aa75cdd7dd3f8508132f9fb3259d9f5908e3b88", size = 811681, upload-time = "2026-05-09T23:13:42.543Z" }, + { url = "https://files.pythonhosted.org/packages/87/83/a5c1c525fba0aa656e88ad0face0b1829788ef4c2fb6b26df58aa1151b84/regex-2026.5.9-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7e87577720152d2caae19fe2baaf1f8d5ca12091e9e229f03915c37d1e4b9178", size = 871135, upload-time = "2026-05-09T23:13:44.326Z" }, + { url = "https://files.pythonhosted.org/packages/18/d4/80882e799e440dd878b0979cbebf8fa4d54624a332c83037c7a701649e3f/regex-2026.5.9-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c8b9b9d294cfea3cd19c718ade7cc93492b2c4991abd9a68d0b3477ae6d8e100", size = 917265, upload-time = "2026-05-09T23:13:47.295Z" }, + { url = "https://files.pythonhosted.org/packages/ae/ff/8db60211e2286e396aad7dc7725356c502bff0901ea05bd6cdc2e1a042b9/regex-2026.5.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:728d8bfd28a8845c8b6bc5dc7ce010453d206396786c0765c2740cb65f37791e", size = 816311, upload-time = "2026-05-09T23:13:49.885Z" }, + { url = "https://files.pythonhosted.org/packages/4c/47/742ef579c61730f8d268e5cf1f9ce0e37e2ea041ad0f5644724f2378e463/regex-2026.5.9-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7e30b874d341fac767d7df5a0870540541c2c054b80cfaac116e8d367a8a7ff2", size = 785498, upload-time = "2026-05-09T23:13:52.25Z" }, + { url = "https://files.pythonhosted.org/packages/7f/ab/cb0999802dcb0fb95b1ab005e8d4163d8afdd67efc2cb6b6630ac13f8cb1/regex-2026.5.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:fd190e88a895a8901325fad284a3f74ea52b1da8525b76cc811fa9b1edf0ce2b", size = 801348, upload-time = "2026-05-09T23:13:54.127Z" }, + { url = "https://files.pythonhosted.org/packages/7d/62/8ca59a24c55bc34d166eefaf3717bd77772f329fdbf984d86581e0a3571c/regex-2026.5.9-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:8e76e8161ad00694cfce6767d5dea860c6391ac5b83e5c3a39661e696f11fc7e", size = 866493, upload-time = "2026-05-09T23:13:56.067Z" }, + { url = "https://files.pythonhosted.org/packages/8d/3d/30f2ae62cef3278bb5bb821f467277a55fb73f01032cf85997e15e8289a8/regex-2026.5.9-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ddda5340e6c01a293027dd46232fa79eaff1b48058ce7a98f572b6445b088041", size = 772811, upload-time = "2026-05-09T23:13:57.867Z" }, + { url = "https://files.pythonhosted.org/packages/d8/ae/7d2089bcd78ad0c0161bc684339df50032acb438a7bd3305e7ddb1193cec/regex-2026.5.9-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:205109e96b3cf5adf8f4cd62bedde9487feb282b9497a3535451e5a24cd706a0", size = 856584, upload-time = "2026-05-09T23:13:59.679Z" }, + { url = "https://files.pythonhosted.org/packages/a9/29/92ff47f75990131ea4f24ba17819e5a9d141e10819807e09addd73409af6/regex-2026.5.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dfbe4579b9f08036aa7d101d1835437a20783574ac66327e6b29b4018a138081", size = 803453, upload-time = "2026-05-09T23:14:01.978Z" }, + { url = "https://files.pythonhosted.org/packages/04/99/eff29f1037dcab36702c9ee5d6858cf1ce2336ea8ea2987f64245b99ea5e/regex-2026.5.9-cp313-cp313t-win32.whl", hash = "sha256:ed2c9e8068b614c574d8d30e543d617cf5379b0535d46f97ef00e904745a08b5", size = 269951, upload-time = "2026-05-09T23:14:03.661Z" }, + { url = "https://files.pythonhosted.org/packages/0e/9d/8870b8981d27b22cda77bb26a5ac7ebfa9c7d9e0dea195a834a82380e748/regex-2026.5.9-cp313-cp313t-win_amd64.whl", hash = "sha256:b46b0f094dc1d3b90356c85a0bd2c9bafc4a6a190b9d6f8ddd5a033b6e088ed4", size = 281240, upload-time = "2026-05-09T23:14:05.56Z" }, + { url = "https://files.pythonhosted.org/packages/72/b1/3379415e8f135c13ac551353397cc4fe97b4978f3cac73c5fcbcded548b8/regex-2026.5.9-cp313-cp313t-win_arm64.whl", hash = "sha256:872acc074bd29ffc9913ecdfedf6ea77502312ca44a4aa0d3779089c6069d8de", size = 272383, upload-time = "2026-05-09T23:14:07.843Z" }, + { url = "https://files.pythonhosted.org/packages/13/3e/9c3cd292d8808b3645a2ce517e200179b6d0e903f176300bd8b542e14de5/regex-2026.5.9-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:1bd7587a2948b4085195d5a3374eaf4a425dc3e55784c038175355ecf3bbbf8a", size = 490376, upload-time = "2026-05-09T23:14:09.64Z" }, + { url = "https://files.pythonhosted.org/packages/60/70/d43ee8a2ca0a8b68d167f21658b85520ac0574617c7f320367c5047f7556/regex-2026.5.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:dea2e88e1cce4522496cce630e11e67b98b7076620bc4336c3f674bc21a375f4", size = 291964, upload-time = "2026-05-09T23:14:11.424Z" }, + { url = "https://files.pythonhosted.org/packages/21/91/9d50b433828d8e74196904e168a43abf1e6e88b2a15d47ed742456720c37/regex-2026.5.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2099f7e7ff7b6aa3192312650a56e91cc091e49d50b04e4f6f8b6e28b3b27f1c", size = 289682, upload-time = "2026-05-09T23:14:13.123Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d2/b835e3cafbb9d977736912436259ff551d60919f7d7b3d37d46659c63564/regex-2026.5.9-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecd353045824e4477562a2ac718c25799cdaaa41f7aa925a806a8a3e6848a5b9", size = 796996, upload-time = "2026-05-09T23:14:14.923Z" }, + { url = "https://files.pythonhosted.org/packages/2c/a6/9f992d00019166b9de01c546dd4549bc679f2a68df11b877740b0760b7c2/regex-2026.5.9-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:65c8c8c37377794bd5b2f3ebe51919042bf17aec802e23c833d89782ed0c78af", size = 866089, upload-time = "2026-05-09T23:14:17.757Z" }, + { url = "https://files.pythonhosted.org/packages/e0/08/4d32af657e049b19cb62b02e46e38fe1518797bfb2203ee93a510b21b0dc/regex-2026.5.9-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5b73ab8afcf66c622db143d1c6fda4e58e4d537ee4f125229ad47b1ab80f34c0", size = 911530, upload-time = "2026-05-09T23:14:20.353Z" }, + { url = "https://files.pythonhosted.org/packages/d9/27/2af43dd1dc201d1fecefda64a45f4ad0995855b92724f795a777b402ee69/regex-2026.5.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0de5cf193997384ed2ca6f1cd4f78055b255d93d82d5a8cd6ba0d11c10b167e4", size = 800643, upload-time = "2026-05-09T23:14:22.265Z" }, + { url = "https://files.pythonhosted.org/packages/a4/dd/23a249047013b5321d4a60c4d2437462086f601b061776a525e5fba2a59f/regex-2026.5.9-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d641a8c9a61618047796d572a39a79b26167b0411d2c3031937b2fe2d081e2cf", size = 777223, upload-time = "2026-05-09T23:14:24.179Z" }, + { url = "https://files.pythonhosted.org/packages/94/6a/e85ed9538cd19586d0465076a4578a12e093ce776d15f3f8ce92733a8dd6/regex-2026.5.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:24b2355ef5cc9aa5b8f07d17704face1c166fdcc2290fa7bd6e6c925655a8346", size = 785760, upload-time = "2026-05-09T23:14:26.065Z" }, + { url = "https://files.pythonhosted.org/packages/2a/c4/f25473209438638e947c55f9156fd8f236f74169229028cc99116380868e/regex-2026.5.9-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:a24852d3c29ad9e47593593d8a247c44ccc3d0548ef12c822d6ed0810affe676", size = 860891, upload-time = "2026-05-09T23:14:28.17Z" }, + { url = "https://files.pythonhosted.org/packages/f9/f7/f4f86e3c74419c37370e91f150ae0c2ef7d34b2e0e4cdd5da046a02e4022/regex-2026.5.9-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:916714069da19329ef7de197dcbc77bb3104145c7c2c864dbfbe318f46b88b14", size = 765891, upload-time = "2026-05-09T23:14:30.06Z" }, + { url = "https://files.pythonhosted.org/packages/26/70/704d8e13765939146b1cd0ef4e2feb71d7929727d2290f026eed10095955/regex-2026.5.9-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:fa411799ca8da32a8d38d020a88faa5b6f91657d284761352940ecf9f7c3bbdd", size = 851380, upload-time = "2026-05-09T23:14:32.123Z" }, + { url = "https://files.pythonhosted.org/packages/26/29/1a13582a8460038edc38e49f64ceb0dd7c60f5caba77571f4bf6601965d9/regex-2026.5.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1e6da47d679b7010ef27556b6e0f99771b744936db1792a10ceac6547ae1503e", size = 789350, upload-time = "2026-05-09T23:14:34.799Z" }, + { url = "https://files.pythonhosted.org/packages/73/56/3dcafe34fc72e271d62ad9a291801e88a1457bb251c132f15fcc2e5aad1a/regex-2026.5.9-cp314-cp314-win32.whl", hash = "sha256:98bd73080e8756255137e1bd3f3f00295bbc5aa383c0e0f973920e9134d7c4ad", size = 272130, upload-time = "2026-05-09T23:14:36.729Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9c/02eebf0be95efe416c664db7fb8b6b05b7a0b06a7544f2884f2558b0526f/regex-2026.5.9-cp314-cp314-win_amd64.whl", hash = "sha256:ff8d372ac2acdc048d1c19916f27ee61bc5722728458ba6ca5052f2c72d51763", size = 280999, upload-time = "2026-05-09T23:14:39.126Z" }, + { url = "https://files.pythonhosted.org/packages/70/5a/1dd1abee76cb7a846a0bcf42fdc87e5720c3c33c24f3e37814310a513d9f/regex-2026.5.9-cp314-cp314-win_arm64.whl", hash = "sha256:e1d93bf647916292e8edcec150c07ddf3dc50179ccaf770c04a7f9e452155372", size = 273500, upload-time = "2026-05-09T23:14:41.059Z" }, + { url = "https://files.pythonhosted.org/packages/86/c1/c5f619b0057a7965cb78ec559c1d7a45ce8c99a35bea95483d64959a93d9/regex-2026.5.9-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:83d0ee4a57d1c87cb549e195ec300b8f0ec3a82eba66d835e4e2ed8634fe4499", size = 494269, upload-time = "2026-05-09T23:14:42.869Z" }, + { url = "https://files.pythonhosted.org/packages/05/2c/5d01f1aee33de4bbe60c8452945bfc8477ca7c5ae4450f6bfe711036cb36/regex-2026.5.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d3d7eb5c9a7f6df82ed3cfac9beb93882a5cbcb5b8b157b56cb2b3b276574ac1", size = 293954, upload-time = "2026-05-09T23:14:44.822Z" }, + { url = "https://files.pythonhosted.org/packages/7a/fe/e8988b2ae2108c6ef71bd4aa8d87fbe257976dd0810e826cd75f701c68b6/regex-2026.5.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:075160bf16658e16d35233300b8453aac25de4cbea808d22348b6979668e924d", size = 292405, upload-time = "2026-05-09T23:14:47.211Z" }, + { url = "https://files.pythonhosted.org/packages/79/34/d2b0937faa7859263f7f0a3c6b103a1296306be6952dc173d0154e9a2f49/regex-2026.5.9-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45375819235558a4ff1c4971dc32881f022613abdb180128f5cb4768c1765a1c", size = 811855, upload-time = "2026-05-09T23:14:49.21Z" }, + { url = "https://files.pythonhosted.org/packages/80/fe/daf53a47457a8486db66c66c01ceb9c2303eecee3f87197f1e77eb1a736d/regex-2026.5.9-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ead4b163ac30a29574510cd4b3e2e985ac5290c05fc7095557d6a5f403fc31b5", size = 871189, upload-time = "2026-05-09T23:14:51.555Z" }, + { url = "https://files.pythonhosted.org/packages/1c/75/058fc4470cbfbf57d800aff1a0022b929a3f9fa553ee10a0cdf2070eb31f/regex-2026.5.9-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8c6e4218fbdfbcd4f6c19efca40930d24a621bf4b48cb76bc6640543bd28ef20", size = 917485, upload-time = "2026-05-09T23:14:53.633Z" }, + { url = "https://files.pythonhosted.org/packages/88/e7/179cfda3a28bc843b5c6cfe7f79f23489c791ed95f151083803660878432/regex-2026.5.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6351571c8a42b505eb555c0dc47d740d0fb66977dc142919eea6f4325b7c56a0", size = 816369, upload-time = "2026-05-09T23:14:56.198Z" }, + { url = "https://files.pythonhosted.org/packages/41/90/6f0cc422071688266d344fca8462d787cba0a2c144acb25721f9a61ec265/regex-2026.5.9-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:002205cafd2a9e78c6290c7d1df277bf3277b3b7a30e0b4bb0dac2e2e3f7cb2d", size = 785869, upload-time = "2026-05-09T23:14:58.602Z" }, + { url = "https://files.pythonhosted.org/packages/02/67/a31f1760f09c27b251ef39e9beb541f462cf977381d067faa764c2c0e393/regex-2026.5.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8abd33fef90b2a9efac5557d6033ca82d1195ed3a15fea5af15ba7b463c6a63b", size = 801427, upload-time = "2026-05-09T23:15:00.642Z" }, + { url = "https://files.pythonhosted.org/packages/e3/c4/1a80654597b6bc1e1ea0494824c31200e8a956abe290afae9b19a166a148/regex-2026.5.9-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:31037c82eccb44b7ea2e9e221d7c01429430e989a1f4b91ea5a855f6017b509a", size = 866482, upload-time = "2026-05-09T23:15:03.384Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/960724e06482c08466ff5611e242e86f80062949cdf6b4b9cc317b9dd93d/regex-2026.5.9-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:5604dfd046dc37eca90250fc3be938b076c8059fa772ac0ed6f499b0f0fb0415", size = 773022, upload-time = "2026-05-09T23:15:05.625Z" }, + { url = "https://files.pythonhosted.org/packages/50/a8/a9979c3e7918280e93159ebcab5ef1a65116dd4f3bd6091be0eae4a126e8/regex-2026.5.9-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0e1b1b4e496afbb24f4a62aba855ee4f88f25578927697b340702e48c9ee6bc2", size = 856642, upload-time = "2026-05-09T23:15:07.966Z" }, + { url = "https://files.pythonhosted.org/packages/fe/d4/a9b732f2f0072c0ab12227483abb24fffcb9f73f8a2b203df0a6d0434735/regex-2026.5.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:be3372b9df6ddecff6486d37e19095a7b4973137caf5512407a89f4455361f41", size = 803552, upload-time = "2026-05-09T23:15:10.215Z" }, + { url = "https://files.pythonhosted.org/packages/d5/fe/1b3113817447a1d4155e4ac76d2e072f42c0bcba2f43fa8a0e756ea2cd91/regex-2026.5.9-cp314-cp314t-win32.whl", hash = "sha256:3ddd90103f9e5c471c49c7852ecc1fe27c7e45eb99e977aefe7caa4e779f4f58", size = 275746, upload-time = "2026-05-09T23:15:12.609Z" }, + { url = "https://files.pythonhosted.org/packages/92/73/93d42045302636c91f2e5ef588b65b84b01428f28ec77de256b1dfdfbe5c/regex-2026.5.9-cp314-cp314t-win_amd64.whl", hash = "sha256:ca518ed29c46eecba6010b15f1b9a479314d2de409536e71b6a13aa04e3b8a77", size = 285685, upload-time = "2026-05-09T23:15:15.086Z" }, + { url = "https://files.pythonhosted.org/packages/da/80/35b4c33c804a165a7f55289afda3ea9e3eb6d15800341a2d66455c0f1f30/regex-2026.5.9-cp314-cp314t-win_arm64.whl", hash = "sha256:5e41809d2683fcde7d5a8c87a6567ba1fb1ce0de9f31bff578de00a4b2d76daa", size = 275713, upload-time = "2026-05-09T23:15:16.98Z" }, +] + +[[package]] +name = "requests" +version = "2.34.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, +] + +[[package]] +name = "rich" +version = "15.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, +] + +[[package]] +name = "safe-netrc" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/f1/fb3c8ccd4a8963d7400cfd80100d5ae7fd95d49ace3a6722736bb4c1c1a6/safe-netrc-1.0.1.tar.gz", hash = "sha256:1dcc7263b4d9ce72e0109d8e2bc9ba89c8056ccc618d26c8c94802c6fd753720", size = 10846, upload-time = "2022-11-29T13:24:35.252Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/2a/b5b09ff2781c7ea694b337f65f52a00319396578c049af8890ff9b4b8232/safe_netrc-1.0.1-py3-none-any.whl", hash = "sha256:5f0dd6a5e304b1da3be220f15efedbf09e50779fe90462143c228c781b9d8218", size = 10891, upload-time = "2022-11-29T13:24:33.626Z" }, +] + +[[package]] +name = "scikit-learn" +version = "1.7.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "joblib", marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "threadpoolctl", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda", size = 7193136, upload-time = "2025-09-09T08:21:29.075Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/3e/daed796fd69cce768b8788401cc464ea90b306fb196ae1ffed0b98182859/scikit_learn-1.7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b33579c10a3081d076ab403df4a4190da4f4432d443521674637677dc91e61f", size = 9336221, upload-time = "2025-09-09T08:20:19.328Z" }, + { url = "https://files.pythonhosted.org/packages/1c/ce/af9d99533b24c55ff4e18d9b7b4d9919bbc6cd8f22fe7a7be01519a347d5/scikit_learn-1.7.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:36749fb62b3d961b1ce4fedf08fa57a1986cd409eff2d783bca5d4b9b5fce51c", size = 8653834, upload-time = "2025-09-09T08:20:22.073Z" }, + { url = "https://files.pythonhosted.org/packages/58/0e/8c2a03d518fb6bd0b6b0d4b114c63d5f1db01ff0f9925d8eb10960d01c01/scikit_learn-1.7.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7a58814265dfc52b3295b1900cfb5701589d30a8bb026c7540f1e9d3499d5ec8", size = 9660938, upload-time = "2025-09-09T08:20:24.327Z" }, + { url = "https://files.pythonhosted.org/packages/2b/75/4311605069b5d220e7cf5adabb38535bd96f0079313cdbb04b291479b22a/scikit_learn-1.7.2-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a847fea807e278f821a0406ca01e387f97653e284ecbd9750e3ee7c90347f18", size = 9477818, upload-time = "2025-09-09T08:20:26.845Z" }, + { url = "https://files.pythonhosted.org/packages/7f/9b/87961813c34adbca21a6b3f6b2bea344c43b30217a6d24cc437c6147f3e8/scikit_learn-1.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:ca250e6836d10e6f402436d6463d6c0e4d8e0234cfb6a9a47835bd392b852ce5", size = 8886969, upload-time = "2025-09-09T08:20:29.329Z" }, + { url = "https://files.pythonhosted.org/packages/43/83/564e141eef908a5863a54da8ca342a137f45a0bfb71d1d79704c9894c9d1/scikit_learn-1.7.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7509693451651cd7361d30ce4e86a1347493554f172b1c72a39300fa2aea79e", size = 9331967, upload-time = "2025-09-09T08:20:32.421Z" }, + { url = "https://files.pythonhosted.org/packages/18/d6/ba863a4171ac9d7314c4d3fc251f015704a2caeee41ced89f321c049ed83/scikit_learn-1.7.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:0486c8f827c2e7b64837c731c8feff72c0bd2b998067a8a9cbc10643c31f0fe1", size = 8648645, upload-time = "2025-09-09T08:20:34.436Z" }, + { url = "https://files.pythonhosted.org/packages/ef/0e/97dbca66347b8cf0ea8b529e6bb9367e337ba2e8be0ef5c1a545232abfde/scikit_learn-1.7.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:89877e19a80c7b11a2891a27c21c4894fb18e2c2e077815bcade10d34287b20d", size = 9715424, upload-time = "2025-09-09T08:20:36.776Z" }, + { url = "https://files.pythonhosted.org/packages/f7/32/1f3b22e3207e1d2c883a7e09abb956362e7d1bd2f14458c7de258a26ac15/scikit_learn-1.7.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8da8bf89d4d79aaec192d2bda62f9b56ae4e5b4ef93b6a56b5de4977e375c1f1", size = 9509234, upload-time = "2025-09-09T08:20:38.957Z" }, + { url = "https://files.pythonhosted.org/packages/9f/71/34ddbd21f1da67c7a768146968b4d0220ee6831e4bcbad3e03dd3eae88b6/scikit_learn-1.7.2-cp311-cp311-win_amd64.whl", hash = "sha256:9b7ed8d58725030568523e937c43e56bc01cadb478fc43c042a9aca1dacb3ba1", size = 8894244, upload-time = "2025-09-09T08:20:41.166Z" }, + { url = "https://files.pythonhosted.org/packages/a7/aa/3996e2196075689afb9fce0410ebdb4a09099d7964d061d7213700204409/scikit_learn-1.7.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8d91a97fa2b706943822398ab943cde71858a50245e31bc71dba62aab1d60a96", size = 9259818, upload-time = "2025-09-09T08:20:43.19Z" }, + { url = "https://files.pythonhosted.org/packages/43/5d/779320063e88af9c4a7c2cf463ff11c21ac9c8bd730c4a294b0000b666c9/scikit_learn-1.7.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:acbc0f5fd2edd3432a22c69bed78e837c70cf896cd7993d71d51ba6708507476", size = 8636997, upload-time = "2025-09-09T08:20:45.468Z" }, + { url = "https://files.pythonhosted.org/packages/5c/d0/0c577d9325b05594fdd33aa970bf53fb673f051a45496842caee13cfd7fe/scikit_learn-1.7.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e5bf3d930aee75a65478df91ac1225ff89cd28e9ac7bd1196853a9229b6adb0b", size = 9478381, upload-time = "2025-09-09T08:20:47.982Z" }, + { url = "https://files.pythonhosted.org/packages/82/70/8bf44b933837ba8494ca0fc9a9ab60f1c13b062ad0197f60a56e2fc4c43e/scikit_learn-1.7.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4d6e9deed1a47aca9fe2f267ab8e8fe82ee20b4526b2c0cd9e135cea10feb44", size = 9300296, upload-time = "2025-09-09T08:20:50.366Z" }, + { url = "https://files.pythonhosted.org/packages/c6/99/ed35197a158f1fdc2fe7c3680e9c70d0128f662e1fee4ed495f4b5e13db0/scikit_learn-1.7.2-cp312-cp312-win_amd64.whl", hash = "sha256:6088aa475f0785e01bcf8529f55280a3d7d298679f50c0bb70a2364a82d0b290", size = 8731256, upload-time = "2025-09-09T08:20:52.627Z" }, + { url = "https://files.pythonhosted.org/packages/ae/93/a3038cb0293037fd335f77f31fe053b89c72f17b1c8908c576c29d953e84/scikit_learn-1.7.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0b7dacaa05e5d76759fb071558a8b5130f4845166d88654a0f9bdf3eb57851b7", size = 9212382, upload-time = "2025-09-09T08:20:54.731Z" }, + { url = "https://files.pythonhosted.org/packages/40/dd/9a88879b0c1104259136146e4742026b52df8540c39fec21a6383f8292c7/scikit_learn-1.7.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:abebbd61ad9e1deed54cca45caea8ad5f79e1b93173dece40bb8e0c658dbe6fe", size = 8592042, upload-time = "2025-09-09T08:20:57.313Z" }, + { url = "https://files.pythonhosted.org/packages/46/af/c5e286471b7d10871b811b72ae794ac5fe2989c0a2df07f0ec723030f5f5/scikit_learn-1.7.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:502c18e39849c0ea1a5d681af1dbcf15f6cce601aebb657aabbfe84133c1907f", size = 9434180, upload-time = "2025-09-09T08:20:59.671Z" }, + { url = "https://files.pythonhosted.org/packages/f1/fd/df59faa53312d585023b2da27e866524ffb8faf87a68516c23896c718320/scikit_learn-1.7.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a4c328a71785382fe3fe676a9ecf2c86189249beff90bf85e22bdb7efaf9ae0", size = 9283660, upload-time = "2025-09-09T08:21:01.71Z" }, + { url = "https://files.pythonhosted.org/packages/a7/c7/03000262759d7b6f38c836ff9d512f438a70d8a8ddae68ee80de72dcfb63/scikit_learn-1.7.2-cp313-cp313-win_amd64.whl", hash = "sha256:63a9afd6f7b229aad94618c01c252ce9e6fa97918c5ca19c9a17a087d819440c", size = 8702057, upload-time = "2025-09-09T08:21:04.234Z" }, + { url = "https://files.pythonhosted.org/packages/55/87/ef5eb1f267084532c8e4aef98a28b6ffe7425acbfd64b5e2f2e066bc29b3/scikit_learn-1.7.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:9acb6c5e867447b4e1390930e3944a005e2cb115922e693c08a323421a6966e8", size = 9558731, upload-time = "2025-09-09T08:21:06.381Z" }, + { url = "https://files.pythonhosted.org/packages/93/f8/6c1e3fc14b10118068d7938878a9f3f4e6d7b74a8ddb1e5bed65159ccda8/scikit_learn-1.7.2-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:2a41e2a0ef45063e654152ec9d8bcfc39f7afce35b08902bfe290c2498a67a6a", size = 9038852, upload-time = "2025-09-09T08:21:08.628Z" }, + { url = "https://files.pythonhosted.org/packages/83/87/066cafc896ee540c34becf95d30375fe5cbe93c3b75a0ee9aa852cd60021/scikit_learn-1.7.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:98335fb98509b73385b3ab2bd0639b1f610541d3988ee675c670371d6a87aa7c", size = 9527094, upload-time = "2025-09-09T08:21:11.486Z" }, + { url = "https://files.pythonhosted.org/packages/9c/2b/4903e1ccafa1f6453b1ab78413938c8800633988c838aa0be386cbb33072/scikit_learn-1.7.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:191e5550980d45449126e23ed1d5e9e24b2c68329ee1f691a3987476e115e09c", size = 9367436, upload-time = "2025-09-09T08:21:13.602Z" }, + { url = "https://files.pythonhosted.org/packages/b5/aa/8444be3cfb10451617ff9d177b3c190288f4563e6c50ff02728be67ad094/scikit_learn-1.7.2-cp313-cp313t-win_amd64.whl", hash = "sha256:57dc4deb1d3762c75d685507fbd0bc17160144b2f2ba4ccea5dc285ab0d0e973", size = 9275749, upload-time = "2025-09-09T08:21:15.96Z" }, + { url = "https://files.pythonhosted.org/packages/d9/82/dee5acf66837852e8e68df6d8d3a6cb22d3df997b733b032f513d95205b7/scikit_learn-1.7.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fa8f63940e29c82d1e67a45d5297bdebbcb585f5a5a50c4914cc2e852ab77f33", size = 9208906, upload-time = "2025-09-09T08:21:18.557Z" }, + { url = "https://files.pythonhosted.org/packages/3c/30/9029e54e17b87cb7d50d51a5926429c683d5b4c1732f0507a6c3bed9bf65/scikit_learn-1.7.2-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:f95dc55b7902b91331fa4e5845dd5bde0580c9cd9612b1b2791b7e80c3d32615", size = 8627836, upload-time = "2025-09-09T08:21:20.695Z" }, + { url = "https://files.pythonhosted.org/packages/60/18/4a52c635c71b536879f4b971c2cedf32c35ee78f48367885ed8025d1f7ee/scikit_learn-1.7.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9656e4a53e54578ad10a434dc1f993330568cfee176dff07112b8785fb413106", size = 9426236, upload-time = "2025-09-09T08:21:22.645Z" }, + { url = "https://files.pythonhosted.org/packages/99/7e/290362f6ab582128c53445458a5befd471ed1ea37953d5bcf80604619250/scikit_learn-1.7.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96dc05a854add0e50d3f47a1ef21a10a595016da5b007c7d9cd9d0bffd1fcc61", size = 9312593, upload-time = "2025-09-09T08:21:24.65Z" }, + { url = "https://files.pythonhosted.org/packages/8e/87/24f541b6d62b1794939ae6422f8023703bbf6900378b2b34e0b4384dfefd/scikit_learn-1.7.2-cp314-cp314-win_amd64.whl", hash = "sha256:bb24510ed3f9f61476181e4db51ce801e2ba37541def12dc9333b946fc7a9cf8", size = 8820007, upload-time = "2025-09-09T08:21:26.713Z" }, +] + +[[package]] +name = "scikit-learn" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "joblib", marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "threadpoolctl", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0e/d4/40988bf3b8e34feec1d0e6a051446b1f66225f8529b9309becaeef62b6c4/scikit_learn-1.8.0.tar.gz", hash = "sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd", size = 7335585, upload-time = "2025-12-10T07:08:53.618Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/92/53ea2181da8ac6bf27170191028aee7251f8f841f8d3edbfdcaf2008fde9/scikit_learn-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:146b4d36f800c013d267b29168813f7a03a43ecd2895d04861f1240b564421da", size = 8595835, upload-time = "2025-12-10T07:07:39.385Z" }, + { url = "https://files.pythonhosted.org/packages/01/18/d154dc1638803adf987910cdd07097d9c526663a55666a97c124d09fb96a/scikit_learn-1.8.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:f984ca4b14914e6b4094c5d52a32ea16b49832c03bd17a110f004db3c223e8e1", size = 8080381, upload-time = "2025-12-10T07:07:41.93Z" }, + { url = "https://files.pythonhosted.org/packages/8a/44/226142fcb7b7101e64fdee5f49dbe6288d4c7af8abf593237b70fca080a4/scikit_learn-1.8.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e30adb87f0cc81c7690a84f7932dd66be5bac57cfe16b91cb9151683a4a2d3b", size = 8799632, upload-time = "2025-12-10T07:07:43.899Z" }, + { url = "https://files.pythonhosted.org/packages/36/4d/4a67f30778a45d542bbea5db2dbfa1e9e100bf9ba64aefe34215ba9f11f6/scikit_learn-1.8.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ada8121bcb4dac28d930febc791a69f7cb1673c8495e5eee274190b73a4559c1", size = 9103788, upload-time = "2025-12-10T07:07:45.982Z" }, + { url = "https://files.pythonhosted.org/packages/89/3c/45c352094cfa60050bcbb967b1faf246b22e93cb459f2f907b600f2ceda5/scikit_learn-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:c57b1b610bd1f40ba43970e11ce62821c2e6569e4d74023db19c6b26f246cb3b", size = 8081706, upload-time = "2025-12-10T07:07:48.111Z" }, + { url = "https://files.pythonhosted.org/packages/3d/46/5416595bb395757f754feb20c3d776553a386b661658fb21b7c814e89efe/scikit_learn-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:2838551e011a64e3053ad7618dda9310175f7515f1742fa2d756f7c874c05961", size = 7688451, upload-time = "2025-12-10T07:07:49.873Z" }, + { url = "https://files.pythonhosted.org/packages/90/74/e6a7cc4b820e95cc38cf36cd74d5aa2b42e8ffc2d21fe5a9a9c45c1c7630/scikit_learn-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5fb63362b5a7ddab88e52b6dbb47dac3fd7dafeee740dc6c8d8a446ddedade8e", size = 8548242, upload-time = "2025-12-10T07:07:51.568Z" }, + { url = "https://files.pythonhosted.org/packages/49/d8/9be608c6024d021041c7f0b3928d4749a706f4e2c3832bbede4fb4f58c95/scikit_learn-1.8.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:5025ce924beccb28298246e589c691fe1b8c1c96507e6d27d12c5fadd85bfd76", size = 8079075, upload-time = "2025-12-10T07:07:53.697Z" }, + { url = "https://files.pythonhosted.org/packages/dd/47/f187b4636ff80cc63f21cd40b7b2d177134acaa10f6bb73746130ee8c2e5/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4496bb2cf7a43ce1a2d7524a79e40bc5da45cf598dbf9545b7e8316ccba47bb4", size = 8660492, upload-time = "2025-12-10T07:07:55.574Z" }, + { url = "https://files.pythonhosted.org/packages/97/74/b7a304feb2b49df9fafa9382d4d09061a96ee9a9449a7cbea7988dda0828/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0bcfe4d0d14aec44921545fd2af2338c7471de9cb701f1da4c9d85906ab847a", size = 8931904, upload-time = "2025-12-10T07:07:57.666Z" }, + { url = "https://files.pythonhosted.org/packages/9f/c4/0ab22726a04ede56f689476b760f98f8f46607caecff993017ac1b64aa5d/scikit_learn-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:35c007dedb2ffe38fe3ee7d201ebac4a2deccd2408e8621d53067733e3c74809", size = 8019359, upload-time = "2025-12-10T07:07:59.838Z" }, + { url = "https://files.pythonhosted.org/packages/24/90/344a67811cfd561d7335c1b96ca21455e7e472d281c3c279c4d3f2300236/scikit_learn-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:8c497fff237d7b4e07e9ef1a640887fa4fb765647f86fbe00f969ff6280ce2bb", size = 7641898, upload-time = "2025-12-10T07:08:01.36Z" }, + { url = "https://files.pythonhosted.org/packages/03/aa/e22e0768512ce9255eba34775be2e85c2048da73da1193e841707f8f039c/scikit_learn-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0d6ae97234d5d7079dc0040990a6f7aeb97cb7fa7e8945f1999a429b23569e0a", size = 8513770, upload-time = "2025-12-10T07:08:03.251Z" }, + { url = "https://files.pythonhosted.org/packages/58/37/31b83b2594105f61a381fc74ca19e8780ee923be2d496fcd8d2e1147bd99/scikit_learn-1.8.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:edec98c5e7c128328124a029bceb09eda2d526997780fef8d65e9a69eead963e", size = 8044458, upload-time = "2025-12-10T07:08:05.336Z" }, + { url = "https://files.pythonhosted.org/packages/2d/5a/3f1caed8765f33eabb723596666da4ebbf43d11e96550fb18bdec42b467b/scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:74b66d8689d52ed04c271e1329f0c61635bcaf5b926db9b12d58914cdc01fe57", size = 8610341, upload-time = "2025-12-10T07:08:07.732Z" }, + { url = "https://files.pythonhosted.org/packages/38/cf/06896db3f71c75902a8e9943b444a56e727418f6b4b4a90c98c934f51ed4/scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8fdf95767f989b0cfedb85f7ed8ca215d4be728031f56ff5a519ee1e3276dc2e", size = 8900022, upload-time = "2025-12-10T07:08:09.862Z" }, + { url = "https://files.pythonhosted.org/packages/1c/f9/9b7563caf3ec8873e17a31401858efab6b39a882daf6c1bfa88879c0aa11/scikit_learn-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:2de443b9373b3b615aec1bb57f9baa6bb3a9bd093f1269ba95c17d870422b271", size = 7989409, upload-time = "2025-12-10T07:08:12.028Z" }, + { url = "https://files.pythonhosted.org/packages/49/bd/1f4001503650e72c4f6009ac0c4413cb17d2d601cef6f71c0453da2732fc/scikit_learn-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:eddde82a035681427cbedded4e6eff5e57fa59216c2e3e90b10b19ab1d0a65c3", size = 7619760, upload-time = "2025-12-10T07:08:13.688Z" }, + { url = "https://files.pythonhosted.org/packages/d2/7d/a630359fc9dcc95496588c8d8e3245cc8fd81980251079bc09c70d41d951/scikit_learn-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7cc267b6108f0a1499a734167282c00c4ebf61328566b55ef262d48e9849c735", size = 8826045, upload-time = "2025-12-10T07:08:15.215Z" }, + { url = "https://files.pythonhosted.org/packages/cc/56/a0c86f6930cfcd1c7054a2bc417e26960bb88d32444fe7f71d5c2cfae891/scikit_learn-1.8.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:fe1c011a640a9f0791146011dfd3c7d9669785f9fed2b2a5f9e207536cf5c2fd", size = 8420324, upload-time = "2025-12-10T07:08:17.561Z" }, + { url = "https://files.pythonhosted.org/packages/46/1e/05962ea1cebc1cf3876667ecb14c283ef755bf409993c5946ade3b77e303/scikit_learn-1.8.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72358cce49465d140cc4e7792015bb1f0296a9742d5622c67e31399b75468b9e", size = 8680651, upload-time = "2025-12-10T07:08:19.952Z" }, + { url = "https://files.pythonhosted.org/packages/fe/56/a85473cd75f200c9759e3a5f0bcab2d116c92a8a02ee08ccd73b870f8bb4/scikit_learn-1.8.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:80832434a6cc114f5219211eec13dcbc16c2bac0e31ef64c6d346cde3cf054cb", size = 8925045, upload-time = "2025-12-10T07:08:22.11Z" }, + { url = "https://files.pythonhosted.org/packages/cc/b7/64d8cfa896c64435ae57f4917a548d7ac7a44762ff9802f75a79b77cb633/scikit_learn-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ee787491dbfe082d9c3013f01f5991658b0f38aa8177e4cd4bf434c58f551702", size = 8507994, upload-time = "2025-12-10T07:08:23.943Z" }, + { url = "https://files.pythonhosted.org/packages/5e/37/e192ea709551799379958b4c4771ec507347027bb7c942662c7fbeba31cb/scikit_learn-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf97c10a3f5a7543f9b88cbf488d33d175e9146115a451ae34568597ba33dcde", size = 7869518, upload-time = "2025-12-10T07:08:25.71Z" }, + { url = "https://files.pythonhosted.org/packages/24/05/1af2c186174cc92dcab2233f327336058c077d38f6fe2aceb08e6ab4d509/scikit_learn-1.8.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c22a2da7a198c28dd1a6e1136f19c830beab7fdca5b3e5c8bba8394f8a5c45b3", size = 8528667, upload-time = "2025-12-10T07:08:27.541Z" }, + { url = "https://files.pythonhosted.org/packages/a8/25/01c0af38fe969473fb292bba9dc2b8f9b451f3112ff242c647fee3d0dfe7/scikit_learn-1.8.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:6b595b07a03069a2b1740dc08c2299993850ea81cce4fe19b2421e0c970de6b7", size = 8066524, upload-time = "2025-12-10T07:08:29.822Z" }, + { url = "https://files.pythonhosted.org/packages/be/ce/a0623350aa0b68647333940ee46fe45086c6060ec604874e38e9ab7d8e6c/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:29ffc74089f3d5e87dfca4c2c8450f88bdc61b0fc6ed5d267f3988f19a1309f6", size = 8657133, upload-time = "2025-12-10T07:08:31.865Z" }, + { url = "https://files.pythonhosted.org/packages/b8/cb/861b41341d6f1245e6ca80b1c1a8c4dfce43255b03df034429089ca2a2c5/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fb65db5d7531bccf3a4f6bec3462223bea71384e2cda41da0f10b7c292b9e7c4", size = 8923223, upload-time = "2025-12-10T07:08:34.166Z" }, + { url = "https://files.pythonhosted.org/packages/76/18/a8def8f91b18cd1ba6e05dbe02540168cb24d47e8dcf69e8d00b7da42a08/scikit_learn-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:56079a99c20d230e873ea40753102102734c5953366972a71d5cb39a32bc40c6", size = 8096518, upload-time = "2025-12-10T07:08:36.339Z" }, + { url = "https://files.pythonhosted.org/packages/d1/77/482076a678458307f0deb44e29891d6022617b2a64c840c725495bee343f/scikit_learn-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:3bad7565bc9cf37ce19a7c0d107742b320c1285df7aab1a6e2d28780df167242", size = 7754546, upload-time = "2025-12-10T07:08:38.128Z" }, + { url = "https://files.pythonhosted.org/packages/2d/d1/ef294ca754826daa043b2a104e59960abfab4cf653891037d19dd5b6f3cf/scikit_learn-1.8.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:4511be56637e46c25721e83d1a9cea9614e7badc7040c4d573d75fbe257d6fd7", size = 8848305, upload-time = "2025-12-10T07:08:41.013Z" }, + { url = "https://files.pythonhosted.org/packages/5b/e2/b1f8b05138ee813b8e1a4149f2f0d289547e60851fd1bb268886915adbda/scikit_learn-1.8.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:a69525355a641bf8ef136a7fa447672fb54fe8d60cab5538d9eb7c6438543fb9", size = 8432257, upload-time = "2025-12-10T07:08:42.873Z" }, + { url = "https://files.pythonhosted.org/packages/26/11/c32b2138a85dcb0c99f6afd13a70a951bfdff8a6ab42d8160522542fb647/scikit_learn-1.8.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c2656924ec73e5939c76ac4c8b026fc203b83d8900362eb2599d8aee80e4880f", size = 8678673, upload-time = "2025-12-10T07:08:45.362Z" }, + { url = "https://files.pythonhosted.org/packages/c7/57/51f2384575bdec454f4fe4e7a919d696c9ebce914590abf3e52d47607ab8/scikit_learn-1.8.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15fc3b5d19cc2be65404786857f2e13c70c83dd4782676dd6814e3b89dc8f5b9", size = 8922467, upload-time = "2025-12-10T07:08:47.408Z" }, + { url = "https://files.pythonhosted.org/packages/35/4d/748c9e2872637a57981a04adc038dacaa16ba8ca887b23e34953f0b3f742/scikit_learn-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:00d6f1d66fbcf4eba6e356e1420d33cc06c70a45bb1363cd6f6a8e4ebbbdece2", size = 8774395, upload-time = "2025-12-10T07:08:49.337Z" }, + { url = "https://files.pythonhosted.org/packages/60/22/d7b2ebe4704a5e50790ba089d5c2ae308ab6bb852719e6c3bd4f04c3a363/scikit_learn-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:f28dd15c6bb0b66ba09728cf09fd8736c304be29409bd8445a080c1280619e8c", size = 8002647, upload-time = "2025-12-10T07:08:51.601Z" }, +] + +[[package]] +name = "scipy" +version = "1.15.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214, upload-time = "2025-05-08T16:13:05.955Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/2f/4966032c5f8cc7e6a60f1b2e0ad686293b9474b65246b0c642e3ef3badd0/scipy-1.15.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a345928c86d535060c9c2b25e71e87c39ab2f22fc96e9636bd74d1dbf9de448c", size = 38702770, upload-time = "2025-05-08T16:04:20.849Z" }, + { url = "https://files.pythonhosted.org/packages/a0/6e/0c3bf90fae0e910c274db43304ebe25a6b391327f3f10b5dcc638c090795/scipy-1.15.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:ad3432cb0f9ed87477a8d97f03b763fd1d57709f1bbde3c9369b1dff5503b253", size = 30094511, upload-time = "2025-05-08T16:04:27.103Z" }, + { url = "https://files.pythonhosted.org/packages/ea/b1/4deb37252311c1acff7f101f6453f0440794f51b6eacb1aad4459a134081/scipy-1.15.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:aef683a9ae6eb00728a542b796f52a5477b78252edede72b8327a886ab63293f", size = 22368151, upload-time = "2025-05-08T16:04:31.731Z" }, + { url = "https://files.pythonhosted.org/packages/38/7d/f457626e3cd3c29b3a49ca115a304cebb8cc6f31b04678f03b216899d3c6/scipy-1.15.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:1c832e1bd78dea67d5c16f786681b28dd695a8cb1fb90af2e27580d3d0967e92", size = 25121732, upload-time = "2025-05-08T16:04:36.596Z" }, + { url = "https://files.pythonhosted.org/packages/db/0a/92b1de4a7adc7a15dcf5bddc6e191f6f29ee663b30511ce20467ef9b82e4/scipy-1.15.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:263961f658ce2165bbd7b99fa5135195c3a12d9bef045345016b8b50c315cb82", size = 35547617, upload-time = "2025-05-08T16:04:43.546Z" }, + { url = "https://files.pythonhosted.org/packages/8e/6d/41991e503e51fc1134502694c5fa7a1671501a17ffa12716a4a9151af3df/scipy-1.15.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2abc762b0811e09a0d3258abee2d98e0c703eee49464ce0069590846f31d40", size = 37662964, upload-time = "2025-05-08T16:04:49.431Z" }, + { url = "https://files.pythonhosted.org/packages/25/e1/3df8f83cb15f3500478c889be8fb18700813b95e9e087328230b98d547ff/scipy-1.15.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ed7284b21a7a0c8f1b6e5977ac05396c0d008b89e05498c8b7e8f4a1423bba0e", size = 37238749, upload-time = "2025-05-08T16:04:55.215Z" }, + { url = "https://files.pythonhosted.org/packages/93/3e/b3257cf446f2a3533ed7809757039016b74cd6f38271de91682aa844cfc5/scipy-1.15.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5380741e53df2c566f4d234b100a484b420af85deb39ea35a1cc1be84ff53a5c", size = 40022383, upload-time = "2025-05-08T16:05:01.914Z" }, + { url = "https://files.pythonhosted.org/packages/d1/84/55bc4881973d3f79b479a5a2e2df61c8c9a04fcb986a213ac9c02cfb659b/scipy-1.15.3-cp310-cp310-win_amd64.whl", hash = "sha256:9d61e97b186a57350f6d6fd72640f9e99d5a4a2b8fbf4b9ee9a841eab327dc13", size = 41259201, upload-time = "2025-05-08T16:05:08.166Z" }, + { url = "https://files.pythonhosted.org/packages/96/ab/5cc9f80f28f6a7dff646c5756e559823614a42b1939d86dd0ed550470210/scipy-1.15.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:993439ce220d25e3696d1b23b233dd010169b62f6456488567e830654ee37a6b", size = 38714255, upload-time = "2025-05-08T16:05:14.596Z" }, + { url = "https://files.pythonhosted.org/packages/4a/4a/66ba30abe5ad1a3ad15bfb0b59d22174012e8056ff448cb1644deccbfed2/scipy-1.15.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:34716e281f181a02341ddeaad584205bd2fd3c242063bd3423d61ac259ca7eba", size = 30111035, upload-time = "2025-05-08T16:05:20.152Z" }, + { url = "https://files.pythonhosted.org/packages/4b/fa/a7e5b95afd80d24313307f03624acc65801846fa75599034f8ceb9e2cbf6/scipy-1.15.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3b0334816afb8b91dab859281b1b9786934392aa3d527cd847e41bb6f45bee65", size = 22384499, upload-time = "2025-05-08T16:05:24.494Z" }, + { url = "https://files.pythonhosted.org/packages/17/99/f3aaddccf3588bb4aea70ba35328c204cadd89517a1612ecfda5b2dd9d7a/scipy-1.15.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:6db907c7368e3092e24919b5e31c76998b0ce1684d51a90943cb0ed1b4ffd6c1", size = 25152602, upload-time = "2025-05-08T16:05:29.313Z" }, + { url = "https://files.pythonhosted.org/packages/56/c5/1032cdb565f146109212153339f9cb8b993701e9fe56b1c97699eee12586/scipy-1.15.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:721d6b4ef5dc82ca8968c25b111e307083d7ca9091bc38163fb89243e85e3889", size = 35503415, upload-time = "2025-05-08T16:05:34.699Z" }, + { url = "https://files.pythonhosted.org/packages/bd/37/89f19c8c05505d0601ed5650156e50eb881ae3918786c8fd7262b4ee66d3/scipy-1.15.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39cb9c62e471b1bb3750066ecc3a3f3052b37751c7c3dfd0fd7e48900ed52982", size = 37652622, upload-time = "2025-05-08T16:05:40.762Z" }, + { url = "https://files.pythonhosted.org/packages/7e/31/be59513aa9695519b18e1851bb9e487de66f2d31f835201f1b42f5d4d475/scipy-1.15.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:795c46999bae845966368a3c013e0e00947932d68e235702b5c3f6ea799aa8c9", size = 37244796, upload-time = "2025-05-08T16:05:48.119Z" }, + { url = "https://files.pythonhosted.org/packages/10/c0/4f5f3eeccc235632aab79b27a74a9130c6c35df358129f7ac8b29f562ac7/scipy-1.15.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:18aaacb735ab38b38db42cb01f6b92a2d0d4b6aabefeb07f02849e47f8fb3594", size = 40047684, upload-time = "2025-05-08T16:05:54.22Z" }, + { url = "https://files.pythonhosted.org/packages/ab/a7/0ddaf514ce8a8714f6ed243a2b391b41dbb65251affe21ee3077ec45ea9a/scipy-1.15.3-cp311-cp311-win_amd64.whl", hash = "sha256:ae48a786a28412d744c62fd7816a4118ef97e5be0bee968ce8f0a2fba7acf3bb", size = 41246504, upload-time = "2025-05-08T16:06:00.437Z" }, + { url = "https://files.pythonhosted.org/packages/37/4b/683aa044c4162e10ed7a7ea30527f2cbd92e6999c10a8ed8edb253836e9c/scipy-1.15.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac6310fdbfb7aa6612408bd2f07295bcbd3fda00d2d702178434751fe48e019", size = 38766735, upload-time = "2025-05-08T16:06:06.471Z" }, + { url = "https://files.pythonhosted.org/packages/7b/7e/f30be3d03de07f25dc0ec926d1681fed5c732d759ac8f51079708c79e680/scipy-1.15.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:185cd3d6d05ca4b44a8f1595af87f9c372bb6acf9c808e99aa3e9aa03bd98cf6", size = 30173284, upload-time = "2025-05-08T16:06:11.686Z" }, + { url = "https://files.pythonhosted.org/packages/07/9c/0ddb0d0abdabe0d181c1793db51f02cd59e4901da6f9f7848e1f96759f0d/scipy-1.15.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:05dc6abcd105e1a29f95eada46d4a3f251743cfd7d3ae8ddb4088047f24ea477", size = 22446958, upload-time = "2025-05-08T16:06:15.97Z" }, + { url = "https://files.pythonhosted.org/packages/af/43/0bce905a965f36c58ff80d8bea33f1f9351b05fad4beaad4eae34699b7a1/scipy-1.15.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:06efcba926324df1696931a57a176c80848ccd67ce6ad020c810736bfd58eb1c", size = 25242454, upload-time = "2025-05-08T16:06:20.394Z" }, + { url = "https://files.pythonhosted.org/packages/56/30/a6f08f84ee5b7b28b4c597aca4cbe545535c39fe911845a96414700b64ba/scipy-1.15.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05045d8b9bfd807ee1b9f38761993297b10b245f012b11b13b91ba8945f7e45", size = 35210199, upload-time = "2025-05-08T16:06:26.159Z" }, + { url = "https://files.pythonhosted.org/packages/0b/1f/03f52c282437a168ee2c7c14a1a0d0781a9a4a8962d84ac05c06b4c5b555/scipy-1.15.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271e3713e645149ea5ea3e97b57fdab61ce61333f97cfae392c28ba786f9bb49", size = 37309455, upload-time = "2025-05-08T16:06:32.778Z" }, + { url = "https://files.pythonhosted.org/packages/89/b1/fbb53137f42c4bf630b1ffdfc2151a62d1d1b903b249f030d2b1c0280af8/scipy-1.15.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6cfd56fc1a8e53f6e89ba3a7a7251f7396412d655bca2aa5611c8ec9a6784a1e", size = 36885140, upload-time = "2025-05-08T16:06:39.249Z" }, + { url = "https://files.pythonhosted.org/packages/2e/2e/025e39e339f5090df1ff266d021892694dbb7e63568edcfe43f892fa381d/scipy-1.15.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ff17c0bb1cb32952c09217d8d1eed9b53d1463e5f1dd6052c7857f83127d539", size = 39710549, upload-time = "2025-05-08T16:06:45.729Z" }, + { url = "https://files.pythonhosted.org/packages/e6/eb/3bf6ea8ab7f1503dca3a10df2e4b9c3f6b3316df07f6c0ded94b281c7101/scipy-1.15.3-cp312-cp312-win_amd64.whl", hash = "sha256:52092bc0472cfd17df49ff17e70624345efece4e1a12b23783a1ac59a1b728ed", size = 40966184, upload-time = "2025-05-08T16:06:52.623Z" }, + { url = "https://files.pythonhosted.org/packages/73/18/ec27848c9baae6e0d6573eda6e01a602e5649ee72c27c3a8aad673ebecfd/scipy-1.15.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c620736bcc334782e24d173c0fdbb7590a0a436d2fdf39310a8902505008759", size = 38728256, upload-time = "2025-05-08T16:06:58.696Z" }, + { url = "https://files.pythonhosted.org/packages/74/cd/1aef2184948728b4b6e21267d53b3339762c285a46a274ebb7863c9e4742/scipy-1.15.3-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:7e11270a000969409d37ed399585ee530b9ef6aa99d50c019de4cb01e8e54e62", size = 30109540, upload-time = "2025-05-08T16:07:04.209Z" }, + { url = "https://files.pythonhosted.org/packages/5b/d8/59e452c0a255ec352bd0a833537a3bc1bfb679944c4938ab375b0a6b3a3e/scipy-1.15.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8c9ed3ba2c8a2ce098163a9bdb26f891746d02136995df25227a20e71c396ebb", size = 22383115, upload-time = "2025-05-08T16:07:08.998Z" }, + { url = "https://files.pythonhosted.org/packages/08/f5/456f56bbbfccf696263b47095291040655e3cbaf05d063bdc7c7517f32ac/scipy-1.15.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0bdd905264c0c9cfa74a4772cdb2070171790381a5c4d312c973382fc6eaf730", size = 25163884, upload-time = "2025-05-08T16:07:14.091Z" }, + { url = "https://files.pythonhosted.org/packages/a2/66/a9618b6a435a0f0c0b8a6d0a2efb32d4ec5a85f023c2b79d39512040355b/scipy-1.15.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79167bba085c31f38603e11a267d862957cbb3ce018d8b38f79ac043bc92d825", size = 35174018, upload-time = "2025-05-08T16:07:19.427Z" }, + { url = "https://files.pythonhosted.org/packages/b5/09/c5b6734a50ad4882432b6bb7c02baf757f5b2f256041da5df242e2d7e6b6/scipy-1.15.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9deabd6d547aee2c9a81dee6cc96c6d7e9a9b1953f74850c179f91fdc729cb7", size = 37269716, upload-time = "2025-05-08T16:07:25.712Z" }, + { url = "https://files.pythonhosted.org/packages/77/0a/eac00ff741f23bcabd352731ed9b8995a0a60ef57f5fd788d611d43d69a1/scipy-1.15.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dde4fc32993071ac0c7dd2d82569e544f0bdaff66269cb475e0f369adad13f11", size = 36872342, upload-time = "2025-05-08T16:07:31.468Z" }, + { url = "https://files.pythonhosted.org/packages/fe/54/4379be86dd74b6ad81551689107360d9a3e18f24d20767a2d5b9253a3f0a/scipy-1.15.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f77f853d584e72e874d87357ad70f44b437331507d1c311457bed8ed2b956126", size = 39670869, upload-time = "2025-05-08T16:07:38.002Z" }, + { url = "https://files.pythonhosted.org/packages/87/2e/892ad2862ba54f084ffe8cc4a22667eaf9c2bcec6d2bff1d15713c6c0703/scipy-1.15.3-cp313-cp313-win_amd64.whl", hash = "sha256:b90ab29d0c37ec9bf55424c064312930ca5f4bde15ee8619ee44e69319aab163", size = 40988851, upload-time = "2025-05-08T16:08:33.671Z" }, + { url = "https://files.pythonhosted.org/packages/1b/e9/7a879c137f7e55b30d75d90ce3eb468197646bc7b443ac036ae3fe109055/scipy-1.15.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3ac07623267feb3ae308487c260ac684b32ea35fd81e12845039952f558047b8", size = 38863011, upload-time = "2025-05-08T16:07:44.039Z" }, + { url = "https://files.pythonhosted.org/packages/51/d1/226a806bbd69f62ce5ef5f3ffadc35286e9fbc802f606a07eb83bf2359de/scipy-1.15.3-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6487aa99c2a3d509a5227d9a5e889ff05830a06b2ce08ec30df6d79db5fcd5c5", size = 30266407, upload-time = "2025-05-08T16:07:49.891Z" }, + { url = "https://files.pythonhosted.org/packages/e5/9b/f32d1d6093ab9eeabbd839b0f7619c62e46cc4b7b6dbf05b6e615bbd4400/scipy-1.15.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:50f9e62461c95d933d5c5ef4a1f2ebf9a2b4e83b0db374cb3f1de104d935922e", size = 22540030, upload-time = "2025-05-08T16:07:54.121Z" }, + { url = "https://files.pythonhosted.org/packages/e7/29/c278f699b095c1a884f29fda126340fcc201461ee8bfea5c8bdb1c7c958b/scipy-1.15.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:14ed70039d182f411ffc74789a16df3835e05dc469b898233a245cdfd7f162cb", size = 25218709, upload-time = "2025-05-08T16:07:58.506Z" }, + { url = "https://files.pythonhosted.org/packages/24/18/9e5374b617aba742a990581373cd6b68a2945d65cc588482749ef2e64467/scipy-1.15.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a769105537aa07a69468a0eefcd121be52006db61cdd8cac8a0e68980bbb723", size = 34809045, upload-time = "2025-05-08T16:08:03.929Z" }, + { url = "https://files.pythonhosted.org/packages/e1/fe/9c4361e7ba2927074360856db6135ef4904d505e9b3afbbcb073c4008328/scipy-1.15.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db984639887e3dffb3928d118145ffe40eff2fa40cb241a306ec57c219ebbbb", size = 36703062, upload-time = "2025-05-08T16:08:09.558Z" }, + { url = "https://files.pythonhosted.org/packages/b7/8e/038ccfe29d272b30086b25a4960f757f97122cb2ec42e62b460d02fe98e9/scipy-1.15.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:40e54d5c7e7ebf1aa596c374c49fa3135f04648a0caabcb66c52884b943f02b4", size = 36393132, upload-time = "2025-05-08T16:08:15.34Z" }, + { url = "https://files.pythonhosted.org/packages/10/7e/5c12285452970be5bdbe8352c619250b97ebf7917d7a9a9e96b8a8140f17/scipy-1.15.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5e721fed53187e71d0ccf382b6bf977644c533e506c4d33c3fb24de89f5c3ed5", size = 38979503, upload-time = "2025-05-08T16:08:21.513Z" }, + { url = "https://files.pythonhosted.org/packages/81/06/0a5e5349474e1cbc5757975b21bd4fad0e72ebf138c5592f191646154e06/scipy-1.15.3-cp313-cp313t-win_amd64.whl", hash = "sha256:76ad1fb5f8752eabf0fa02e4cc0336b4e8f021e2d5f061ed37d6d264db35e3ca", size = 40308097, upload-time = "2025-05-08T16:08:27.627Z" }, +] + +[[package]] +name = "scipy" +version = "1.17.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/75/b4ce781849931fef6fd529afa6b63711d5a733065722d0c3e2724af9e40a/scipy-1.17.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:1f95b894f13729334fb990162e911c9e5dc1ab390c58aa6cbecb389c5b5e28ec", size = 31613675, upload-time = "2026-02-23T00:16:00.13Z" }, + { url = "https://files.pythonhosted.org/packages/f7/58/bccc2861b305abdd1b8663d6130c0b3d7cc22e8d86663edbc8401bfd40d4/scipy-1.17.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:e18f12c6b0bc5a592ed23d3f7b891f68fd7f8241d69b7883769eb5d5dfb52696", size = 28162057, upload-time = "2026-02-23T00:16:09.456Z" }, + { url = "https://files.pythonhosted.org/packages/6d/ee/18146b7757ed4976276b9c9819108adbc73c5aad636e5353e20746b73069/scipy-1.17.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a3472cfbca0a54177d0faa68f697d8ba4c80bbdc19908c3465556d9f7efce9ee", size = 20334032, upload-time = "2026-02-23T00:16:17.358Z" }, + { url = "https://files.pythonhosted.org/packages/ec/e6/cef1cf3557f0c54954198554a10016b6a03b2ec9e22a4e1df734936bd99c/scipy-1.17.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:766e0dc5a616d026a3a1cffa379af959671729083882f50307e18175797b3dfd", size = 22709533, upload-time = "2026-02-23T00:16:25.791Z" }, + { url = "https://files.pythonhosted.org/packages/4d/60/8804678875fc59362b0fb759ab3ecce1f09c10a735680318ac30da8cd76b/scipy-1.17.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:744b2bf3640d907b79f3fd7874efe432d1cf171ee721243e350f55234b4cec4c", size = 33062057, upload-time = "2026-02-23T00:16:36.931Z" }, + { url = "https://files.pythonhosted.org/packages/09/7d/af933f0f6e0767995b4e2d705a0665e454d1c19402aa7e895de3951ebb04/scipy-1.17.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43af8d1f3bea642559019edfe64e9b11192a8978efbd1539d7bc2aaa23d92de4", size = 35349300, upload-time = "2026-02-23T00:16:49.108Z" }, + { url = "https://files.pythonhosted.org/packages/b4/3d/7ccbbdcbb54c8fdc20d3b6930137c782a163fa626f0aef920349873421ba/scipy-1.17.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd96a1898c0a47be4520327e01f874acfd61fb48a9420f8aa9f6483412ffa444", size = 35127333, upload-time = "2026-02-23T00:17:01.293Z" }, + { url = "https://files.pythonhosted.org/packages/e8/19/f926cb11c42b15ba08e3a71e376d816ac08614f769b4f47e06c3580c836a/scipy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4eb6c25dd62ee8d5edf68a8e1c171dd71c292fdae95d8aeb3dd7d7de4c364082", size = 37741314, upload-time = "2026-02-23T00:17:12.576Z" }, + { url = "https://files.pythonhosted.org/packages/95/da/0d1df507cf574b3f224ccc3d45244c9a1d732c81dcb26b1e8a766ae271a8/scipy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:d30e57c72013c2a4fe441c2fcb8e77b14e152ad48b5464858e07e2ad9fbfceff", size = 36607512, upload-time = "2026-02-23T00:17:23.424Z" }, + { url = "https://files.pythonhosted.org/packages/68/7f/bdd79ceaad24b671543ffe0ef61ed8e659440eb683b66f033454dcee90eb/scipy-1.17.1-cp311-cp311-win_arm64.whl", hash = "sha256:9ecb4efb1cd6e8c4afea0daa91a87fbddbce1b99d2895d151596716c0b2e859d", size = 24599248, upload-time = "2026-02-23T00:17:34.561Z" }, + { url = "https://files.pythonhosted.org/packages/35/48/b992b488d6f299dbe3f11a20b24d3dda3d46f1a635ede1c46b5b17a7b163/scipy-1.17.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:35c3a56d2ef83efc372eaec584314bd0ef2e2f0d2adb21c55e6ad5b344c0dcb8", size = 31610954, upload-time = "2026-02-23T00:17:49.855Z" }, + { url = "https://files.pythonhosted.org/packages/b2/02/cf107b01494c19dc100f1d0b7ac3cc08666e96ba2d64db7626066cee895e/scipy-1.17.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:fcb310ddb270a06114bb64bbe53c94926b943f5b7f0842194d585c65eb4edd76", size = 28172662, upload-time = "2026-02-23T00:18:01.64Z" }, + { url = "https://files.pythonhosted.org/packages/cf/a9/599c28631bad314d219cf9ffd40e985b24d603fc8a2f4ccc5ae8419a535b/scipy-1.17.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:cc90d2e9c7e5c7f1a482c9875007c095c3194b1cfedca3c2f3291cdc2bc7c086", size = 20344366, upload-time = "2026-02-23T00:18:12.015Z" }, + { url = "https://files.pythonhosted.org/packages/35/f5/906eda513271c8deb5af284e5ef0206d17a96239af79f9fa0aebfe0e36b4/scipy-1.17.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:c80be5ede8f3f8eded4eff73cc99a25c388ce98e555b17d31da05287015ffa5b", size = 22704017, upload-time = "2026-02-23T00:18:21.502Z" }, + { url = "https://files.pythonhosted.org/packages/da/34/16f10e3042d2f1d6b66e0428308ab52224b6a23049cb2f5c1756f713815f/scipy-1.17.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e19ebea31758fac5893a2ac360fedd00116cbb7628e650842a6691ba7ca28a21", size = 32927842, upload-time = "2026-02-23T00:18:35.367Z" }, + { url = "https://files.pythonhosted.org/packages/01/8e/1e35281b8ab6d5d72ebe9911edcdffa3f36b04ed9d51dec6dd140396e220/scipy-1.17.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02ae3b274fde71c5e92ac4d54bc06c42d80e399fec704383dcd99b301df37458", size = 35235890, upload-time = "2026-02-23T00:18:49.188Z" }, + { url = "https://files.pythonhosted.org/packages/c5/5c/9d7f4c88bea6e0d5a4f1bc0506a53a00e9fcb198de372bfe4d3652cef482/scipy-1.17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a604bae87c6195d8b1045eddece0514d041604b14f2727bbc2b3020172045eb", size = 35003557, upload-time = "2026-02-23T00:18:54.74Z" }, + { url = "https://files.pythonhosted.org/packages/65/94/7698add8f276dbab7a9de9fb6b0e02fc13ee61d51c7c3f85ac28b65e1239/scipy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f590cd684941912d10becc07325a3eeb77886fe981415660d9265c4c418d0bea", size = 37625856, upload-time = "2026-02-23T00:19:00.307Z" }, + { url = "https://files.pythonhosted.org/packages/a2/84/dc08d77fbf3d87d3ee27f6a0c6dcce1de5829a64f2eae85a0ecc1f0daa73/scipy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:41b71f4a3a4cab9d366cd9065b288efc4d4f3c0b37a91a8e0947fb5bd7f31d87", size = 36549682, upload-time = "2026-02-23T00:19:07.67Z" }, + { url = "https://files.pythonhosted.org/packages/bc/98/fe9ae9ffb3b54b62559f52dedaebe204b408db8109a8c66fdd04869e6424/scipy-1.17.1-cp312-cp312-win_arm64.whl", hash = "sha256:f4115102802df98b2b0db3cce5cb9b92572633a1197c77b7553e5203f284a5b3", size = 24547340, upload-time = "2026-02-23T00:19:12.024Z" }, + { url = "https://files.pythonhosted.org/packages/76/27/07ee1b57b65e92645f219b37148a7e7928b82e2b5dbeccecb4dff7c64f0b/scipy-1.17.1-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:5e3c5c011904115f88a39308379c17f91546f77c1667cea98739fe0fccea804c", size = 31590199, upload-time = "2026-02-23T00:19:17.192Z" }, + { url = "https://files.pythonhosted.org/packages/ec/ae/db19f8ab842e9b724bf5dbb7db29302a91f1e55bc4d04b1025d6d605a2c5/scipy-1.17.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:6fac755ca3d2c3edcb22f479fceaa241704111414831ddd3bc6056e18516892f", size = 28154001, upload-time = "2026-02-23T00:19:22.241Z" }, + { url = "https://files.pythonhosted.org/packages/5b/58/3ce96251560107b381cbd6e8413c483bbb1228a6b919fa8652b0d4090e7f/scipy-1.17.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:7ff200bf9d24f2e4d5dc6ee8c3ac64d739d3a89e2326ba68aaf6c4a2b838fd7d", size = 20325719, upload-time = "2026-02-23T00:19:26.329Z" }, + { url = "https://files.pythonhosted.org/packages/b2/83/15087d945e0e4d48ce2377498abf5ad171ae013232ae31d06f336e64c999/scipy-1.17.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4b400bdc6f79fa02a4d86640310dde87a21fba0c979efff5248908c6f15fad1b", size = 22683595, upload-time = "2026-02-23T00:19:30.304Z" }, + { url = "https://files.pythonhosted.org/packages/b4/e0/e58fbde4a1a594c8be8114eb4aac1a55bcd6587047efc18a61eb1f5c0d30/scipy-1.17.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b64ca7d4aee0102a97f3ba22124052b4bd2152522355073580bf4845e2550b6", size = 32896429, upload-time = "2026-02-23T00:19:35.536Z" }, + { url = "https://files.pythonhosted.org/packages/f5/5f/f17563f28ff03c7b6799c50d01d5d856a1d55f2676f537ca8d28c7f627cd/scipy-1.17.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:581b2264fc0aa555f3f435a5944da7504ea3a065d7029ad60e7c3d1ae09c5464", size = 35203952, upload-time = "2026-02-23T00:19:42.259Z" }, + { url = "https://files.pythonhosted.org/packages/8d/a5/9afd17de24f657fdfe4df9a3f1ea049b39aef7c06000c13db1530d81ccca/scipy-1.17.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:beeda3d4ae615106d7094f7e7cef6218392e4465cc95d25f900bebabfded0950", size = 34979063, upload-time = "2026-02-23T00:19:47.547Z" }, + { url = "https://files.pythonhosted.org/packages/8b/13/88b1d2384b424bf7c924f2038c1c409f8d88bb2a8d49d097861dd64a57b2/scipy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6609bc224e9568f65064cfa72edc0f24ee6655b47575954ec6339534b2798369", size = 37598449, upload-time = "2026-02-23T00:19:53.238Z" }, + { url = "https://files.pythonhosted.org/packages/35/e5/d6d0e51fc888f692a35134336866341c08655d92614f492c6860dc45bb2c/scipy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:37425bc9175607b0268f493d79a292c39f9d001a357bebb6b88fdfaff13f6448", size = 36510943, upload-time = "2026-02-23T00:20:50.89Z" }, + { url = "https://files.pythonhosted.org/packages/2a/fd/3be73c564e2a01e690e19cc618811540ba5354c67c8680dce3281123fb79/scipy-1.17.1-cp313-cp313-win_arm64.whl", hash = "sha256:5cf36e801231b6a2059bf354720274b7558746f3b1a4efb43fcf557ccd484a87", size = 24545621, upload-time = "2026-02-23T00:20:55.871Z" }, + { url = "https://files.pythonhosted.org/packages/6f/6b/17787db8b8114933a66f9dcc479a8272e4b4da75fe03b0c282f7b0ade8cd/scipy-1.17.1-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:d59c30000a16d8edc7e64152e30220bfbd724c9bbb08368c054e24c651314f0a", size = 31936708, upload-time = "2026-02-23T00:19:58.694Z" }, + { url = "https://files.pythonhosted.org/packages/38/2e/524405c2b6392765ab1e2b722a41d5da33dc5c7b7278184a8ad29b6cb206/scipy-1.17.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:010f4333c96c9bb1a4516269e33cb5917b08ef2166d5556ca2fd9f082a9e6ea0", size = 28570135, upload-time = "2026-02-23T00:20:03.934Z" }, + { url = "https://files.pythonhosted.org/packages/fd/c3/5bd7199f4ea8556c0c8e39f04ccb014ac37d1468e6cfa6a95c6b3562b76e/scipy-1.17.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:2ceb2d3e01c5f1d83c4189737a42d9cb2fc38a6eeed225e7515eef71ad301dce", size = 20741977, upload-time = "2026-02-23T00:20:07.935Z" }, + { url = "https://files.pythonhosted.org/packages/d9/b8/8ccd9b766ad14c78386599708eb745f6b44f08400a5fd0ade7cf89b6fc93/scipy-1.17.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:844e165636711ef41f80b4103ed234181646b98a53c8f05da12ca5ca289134f6", size = 23029601, upload-time = "2026-02-23T00:20:12.161Z" }, + { url = "https://files.pythonhosted.org/packages/6d/a0/3cb6f4d2fb3e17428ad2880333cac878909ad1a89f678527b5328b93c1d4/scipy-1.17.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:158dd96d2207e21c966063e1635b1063cd7787b627b6f07305315dd73d9c679e", size = 33019667, upload-time = "2026-02-23T00:20:17.208Z" }, + { url = "https://files.pythonhosted.org/packages/f3/c3/2d834a5ac7bf3a0c806ad1508efc02dda3c8c61472a56132d7894c312dea/scipy-1.17.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74cbb80d93260fe2ffa334efa24cb8f2f0f622a9b9febf8b483c0b865bfb3475", size = 35264159, upload-time = "2026-02-23T00:20:23.087Z" }, + { url = "https://files.pythonhosted.org/packages/4d/77/d3ed4becfdbd217c52062fafe35a72388d1bd82c2d0ba5ca19d6fcc93e11/scipy-1.17.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:dbc12c9f3d185f5c737d801da555fb74b3dcfa1a50b66a1a93e09190f41fab50", size = 35102771, upload-time = "2026-02-23T00:20:28.636Z" }, + { url = "https://files.pythonhosted.org/packages/bd/12/d19da97efde68ca1ee5538bb261d5d2c062f0c055575128f11a2730e3ac1/scipy-1.17.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94055a11dfebe37c656e70317e1996dc197e1a15bbcc351bcdd4610e128fe1ca", size = 37665910, upload-time = "2026-02-23T00:20:34.743Z" }, + { url = "https://files.pythonhosted.org/packages/06/1c/1172a88d507a4baaf72c5a09bb6c018fe2ae0ab622e5830b703a46cc9e44/scipy-1.17.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e30bdeaa5deed6bc27b4cc490823cd0347d7dae09119b8803ae576ea0ce52e4c", size = 36562980, upload-time = "2026-02-23T00:20:40.575Z" }, + { url = "https://files.pythonhosted.org/packages/70/b0/eb757336e5a76dfa7911f63252e3b7d1de00935d7705cf772db5b45ec238/scipy-1.17.1-cp313-cp313t-win_arm64.whl", hash = "sha256:a720477885a9d2411f94a93d16f9d89bad0f28ca23c3f8daa521e2dcc3f44d49", size = 24856543, upload-time = "2026-02-23T00:20:45.313Z" }, + { url = "https://files.pythonhosted.org/packages/cf/83/333afb452af6f0fd70414dc04f898647ee1423979ce02efa75c3b0f2c28e/scipy-1.17.1-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:a48a72c77a310327f6a3a920092fa2b8fd03d7deaa60f093038f22d98e096717", size = 31584510, upload-time = "2026-02-23T00:21:01.015Z" }, + { url = "https://files.pythonhosted.org/packages/ed/a6/d05a85fd51daeb2e4ea71d102f15b34fedca8e931af02594193ae4fd25f7/scipy-1.17.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:45abad819184f07240d8a696117a7aacd39787af9e0b719d00285549ed19a1e9", size = 28170131, upload-time = "2026-02-23T00:21:05.888Z" }, + { url = "https://files.pythonhosted.org/packages/db/7b/8624a203326675d7746a254083a187398090a179335b2e4a20e2ddc46e83/scipy-1.17.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:3fd1fcdab3ea951b610dc4cef356d416d5802991e7e32b5254828d342f7b7e0b", size = 20342032, upload-time = "2026-02-23T00:21:09.904Z" }, + { url = "https://files.pythonhosted.org/packages/c9/35/2c342897c00775d688d8ff3987aced3426858fd89d5a0e26e020b660b301/scipy-1.17.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:7bdf2da170b67fdf10bca777614b1c7d96ae3ca5794fd9587dce41eb2966e866", size = 22678766, upload-time = "2026-02-23T00:21:14.313Z" }, + { url = "https://files.pythonhosted.org/packages/ef/f2/7cdb8eb308a1a6ae1e19f945913c82c23c0c442a462a46480ce487fdc0ac/scipy-1.17.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:adb2642e060a6549c343603a3851ba76ef0b74cc8c079a9a58121c7ec9fe2350", size = 32957007, upload-time = "2026-02-23T00:21:19.663Z" }, + { url = "https://files.pythonhosted.org/packages/0b/2e/7eea398450457ecb54e18e9d10110993fa65561c4f3add5e8eccd2b9cd41/scipy-1.17.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eee2cfda04c00a857206a4330f0c5e3e56535494e30ca445eb19ec624ae75118", size = 35221333, upload-time = "2026-02-23T00:21:25.278Z" }, + { url = "https://files.pythonhosted.org/packages/d9/77/5b8509d03b77f093a0d52e606d3c4f79e8b06d1d38c441dacb1e26cacf46/scipy-1.17.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d2650c1fb97e184d12d8ba010493ee7b322864f7d3d00d3f9bb97d9c21de4068", size = 35042066, upload-time = "2026-02-23T00:21:31.358Z" }, + { url = "https://files.pythonhosted.org/packages/f9/df/18f80fb99df40b4070328d5ae5c596f2f00fffb50167e31439e932f29e7d/scipy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:08b900519463543aa604a06bec02461558a6e1cef8fdbb8098f77a48a83c8118", size = 37612763, upload-time = "2026-02-23T00:21:37.247Z" }, + { url = "https://files.pythonhosted.org/packages/4b/39/f0e8ea762a764a9dc52aa7dabcfad51a354819de1f0d4652b6a1122424d6/scipy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:3877ac408e14da24a6196de0ddcace62092bfc12a83823e92e49e40747e52c19", size = 37290984, upload-time = "2026-02-23T00:22:35.023Z" }, + { url = "https://files.pythonhosted.org/packages/7c/56/fe201e3b0f93d1a8bcf75d3379affd228a63d7e2d80ab45467a74b494947/scipy-1.17.1-cp314-cp314-win_arm64.whl", hash = "sha256:f8885db0bc2bffa59d5c1b72fad7a6a92d3e80e7257f967dd81abb553a90d293", size = 25192877, upload-time = "2026-02-23T00:22:39.798Z" }, + { url = "https://files.pythonhosted.org/packages/96/ad/f8c414e121f82e02d76f310f16db9899c4fcde36710329502a6b2a3c0392/scipy-1.17.1-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:1cc682cea2ae55524432f3cdff9e9a3be743d52a7443d0cba9017c23c87ae2f6", size = 31949750, upload-time = "2026-02-23T00:21:42.289Z" }, + { url = "https://files.pythonhosted.org/packages/7c/b0/c741e8865d61b67c81e255f4f0a832846c064e426636cd7de84e74d209be/scipy-1.17.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:2040ad4d1795a0ae89bfc7e8429677f365d45aa9fd5e4587cf1ea737f927b4a1", size = 28585858, upload-time = "2026-02-23T00:21:47.706Z" }, + { url = "https://files.pythonhosted.org/packages/ed/1b/3985219c6177866628fa7c2595bfd23f193ceebbe472c98a08824b9466ff/scipy-1.17.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:131f5aaea57602008f9822e2115029b55d4b5f7c070287699fe45c661d051e39", size = 20757723, upload-time = "2026-02-23T00:21:52.039Z" }, + { url = "https://files.pythonhosted.org/packages/c0/19/2a04aa25050d656d6f7b9e7b685cc83d6957fb101665bfd9369ca6534563/scipy-1.17.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:9cdc1a2fcfd5c52cfb3045feb399f7b3ce822abdde3a193a6b9a60b3cb5854ca", size = 23043098, upload-time = "2026-02-23T00:21:56.185Z" }, + { url = "https://files.pythonhosted.org/packages/86/f1/3383beb9b5d0dbddd030335bf8a8b32d4317185efe495374f134d8be6cce/scipy-1.17.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e3dcd57ab780c741fde8dc68619de988b966db759a3c3152e8e9142c26295ad", size = 33030397, upload-time = "2026-02-23T00:22:01.404Z" }, + { url = "https://files.pythonhosted.org/packages/41/68/8f21e8a65a5a03f25a79165ec9d2b28c00e66dc80546cf5eb803aeeff35b/scipy-1.17.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a9956e4d4f4a301ebf6cde39850333a6b6110799d470dbbb1e25326ac447f52a", size = 35281163, upload-time = "2026-02-23T00:22:07.024Z" }, + { url = "https://files.pythonhosted.org/packages/84/8d/c8a5e19479554007a5632ed7529e665c315ae7492b4f946b0deb39870e39/scipy-1.17.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:a4328d245944d09fd639771de275701ccadf5f781ba0ff092ad141e017eccda4", size = 35116291, upload-time = "2026-02-23T00:22:12.585Z" }, + { url = "https://files.pythonhosted.org/packages/52/52/e57eceff0e342a1f50e274264ed47497b59e6a4e3118808ee58ddda7b74a/scipy-1.17.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a77cbd07b940d326d39a1d1b37817e2ee4d79cb30e7338f3d0cddffae70fcaa2", size = 37682317, upload-time = "2026-02-23T00:22:18.513Z" }, + { url = "https://files.pythonhosted.org/packages/11/2f/b29eafe4a3fbc3d6de9662b36e028d5f039e72d345e05c250e121a230dd4/scipy-1.17.1-cp314-cp314t-win_amd64.whl", hash = "sha256:eb092099205ef62cd1782b006658db09e2fed75bffcae7cc0d44052d8aa0f484", size = 37345327, upload-time = "2026-02-23T00:22:24.442Z" }, + { url = "https://files.pythonhosted.org/packages/07/39/338d9219c4e87f3e708f18857ecd24d22a0c3094752393319553096b98af/scipy-1.17.1-cp314-cp314t-win_arm64.whl", hash = "sha256:200e1050faffacc162be6a486a984a0497866ec54149a01270adc8a59b7c7d21", size = 25489165, upload-time = "2026-02-23T00:22:29.563Z" }, +] + +[[package]] +name = "scitokens" +version = "1.9.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, + { name = "pyjwt" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ed/d3/f7a71bb402afbc602e1f6624514115ad59a588a1ad696274f957d532f35f/scitokens-1.9.7.tar.gz", hash = "sha256:9aabefbd68859e94a3909f3dc08bd73c0e7a9c08203c16de338c9512ace821e3", size = 54241, upload-time = "2026-03-13T21:28:51.67Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/5b/ed5d8ce3d0e9d2bd38c7901d8131a346fe69d941357f92700bceee12b32e/scitokens-1.9.7-py3-none-any.whl", hash = "sha256:05a539ece3a114233e28c5b1552e5d1fbd13282c6ce683f5a073de116b228df0", size = 34527, upload-time = "2026-03-13T21:28:50.43Z" }, +] + +[[package]] +name = "setuptools" +version = "81.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/1c/73e719955c59b8e424d015ab450f51c0af856ae46ea2da83eba51cc88de1/setuptools-81.0.0.tar.gz", hash = "sha256:487b53915f52501f0a79ccfd0c02c165ffe06631443a886740b91af4b7a5845a", size = 1198299, upload-time = "2026-02-06T21:10:39.601Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/e3/c164c88b2e5ce7b24d667b9bd83589cf4f3520d97cad01534cd3c4f55fdb/setuptools-81.0.0-py3-none-any.whl", hash = "sha256:fdd925d5c5d9f62e4b74b30d6dd7828ce236fd6ed998a08d81de62ce5a6310d6", size = 1062021, upload-time = "2026-02-06T21:10:37.175Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "sympy" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, +] + +[[package]] +name = "tenacity" +version = "9.1.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/47/c6/ee486fd809e357697ee8a44d3d69222b344920433d3b6666ccd9b374630c/tenacity-9.1.4.tar.gz", hash = "sha256:adb31d4c263f2bd041081ab33b498309a57c77f9acf2db65aadf0898179cf93a", size = 49413, upload-time = "2026-02-07T10:45:33.841Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/c1/eb8f9debc45d3b7918a32ab756658a0904732f75e555402972246b0b8e71/tenacity-9.1.4-py3-none-any.whl", hash = "sha256:6095a360c919085f28c6527de529e76a06ad89b23659fa881ae0649b867a9d55", size = 28926, upload-time = "2026-02-07T10:45:32.24Z" }, +] + +[[package]] +name = "threadpoolctl" +version = "3.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274, upload-time = "2025-03-13T13:49:23.031Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" }, +] + +[[package]] +name = "tomli" +version = "2.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30", size = 154704, upload-time = "2026-03-25T20:21:10.473Z" }, + { url = "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a", size = 149454, upload-time = "2026-03-25T20:21:12.036Z" }, + { url = "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076", size = 237561, upload-time = "2026-03-25T20:21:13.098Z" }, + { url = "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", size = 243824, upload-time = "2026-03-25T20:21:14.569Z" }, + { url = "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c", size = 242227, upload-time = "2026-03-25T20:21:15.712Z" }, + { url = "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc", size = 247859, upload-time = "2026-03-25T20:21:17.001Z" }, + { url = "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049", size = 97204, upload-time = "2026-03-25T20:21:18.079Z" }, + { url = "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e", size = 108084, upload-time = "2026-03-25T20:21:18.978Z" }, + { url = "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece", size = 95285, upload-time = "2026-03-25T20:21:20.309Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" }, + { url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" }, + { url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" }, + { url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" }, + { url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" }, + { url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" }, + { url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" }, + { url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" }, + { url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" }, + { url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" }, + { url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" }, + { url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" }, + { url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" }, + { url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" }, + { url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" }, + { url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726, upload-time = "2026-03-25T20:21:42.23Z" }, + { url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859, upload-time = "2026-03-25T20:21:43.386Z" }, + { url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713, upload-time = "2026-03-25T20:21:44.474Z" }, + { url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084, upload-time = "2026-03-25T20:21:45.62Z" }, + { url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973, upload-time = "2026-03-25T20:21:46.937Z" }, + { url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223, upload-time = "2026-03-25T20:21:48.467Z" }, + { url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973, upload-time = "2026-03-25T20:21:49.526Z" }, + { url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082, upload-time = "2026-03-25T20:21:50.506Z" }, + { url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490, upload-time = "2026-03-25T20:21:51.474Z" }, + { url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263, upload-time = "2026-03-25T20:21:52.543Z" }, + { url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736, upload-time = "2026-03-25T20:21:53.674Z" }, + { url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717, upload-time = "2026-03-25T20:21:55.129Z" }, + { url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461, upload-time = "2026-03-25T20:21:56.228Z" }, + { url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855, upload-time = "2026-03-25T20:21:57.653Z" }, + { url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144, upload-time = "2026-03-25T20:21:59.089Z" }, + { url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683, upload-time = "2026-03-25T20:22:00.214Z" }, + { url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196, upload-time = "2026-03-25T20:22:01.169Z" }, + { url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393, upload-time = "2026-03-25T20:22:02.137Z" }, + { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, +] + +[[package]] +name = "torch" +version = "2.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-bindings", marker = "sys_platform == 'linux'" }, + { name = "cuda-toolkit", extra = ["cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "sys_platform == 'linux'" }, + { name = "filelock" }, + { name = "fsspec" }, + { name = "jinja2" }, + { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "nvidia-cublas", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cudnn-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nvshmem-cu13", marker = "sys_platform == 'linux'" }, + { name = "setuptools" }, + { name = "sympy" }, + { name = "triton", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/b7/53fe0436586716ab7aecff41e26b9302d57c85ded481fd83a2cd741e6b4e/torch-2.12.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:1834bd984f8a2f4f16bdfbeecca9146184b220aa46276bf5756735b5dae12812", size = 87981887, upload-time = "2026-05-13T14:55:53.234Z" }, + { url = "https://files.pythonhosted.org/packages/34/60/d930eac44c30de06ed16f6d1ba4e785e1632532b50d8f0bf9bf699a4d0c7/torch-2.12.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:d4d029801cb7b6df858804a2a21b00cc2aa0bf0ee5d2ab18d343c9e9e5681f35", size = 426355000, upload-time = "2026-05-13T14:54:31.944Z" }, + { url = "https://files.pythonhosted.org/packages/8e/0c/c76b6a087820bab55705b94dfc074e520de9ae91f5ef90da2ecbf2a3ef12/torch-2.12.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:d47e7dee68ac4cd7a068b26bcd6b989935427709fae1c8f7bd0019978f829e15", size = 532144998, upload-time = "2026-05-13T14:56:05.523Z" }, + { url = "https://files.pythonhosted.org/packages/4a/64/8a0d036e166a6aa85ee09bef072f3655d1ba5d5486a68d1b03b6813c01b3/torch-2.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:cf9839790285dd472e7a16aafcb4a4e6bf58ec1b494045044b0eefb0eb4bd1f2", size = 122949877, upload-time = "2026-05-13T14:55:46.841Z" }, + { url = "https://files.pythonhosted.org/packages/18/62/131124fb95df03811b8260d1d43dcc5ee85ea1a344b964613d7efe77fb08/torch-2.12.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:10802fd383bbfed646212e765a72c37d2185205d4f26eb197a254e8ac7ddcb25", size = 87990344, upload-time = "2026-05-13T14:55:42.154Z" }, + { url = "https://files.pythonhosted.org/packages/12/9c/dda0dbd547dc549839824135f223792fd0e725f28ed0715dda366b7acaa2/torch-2.12.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:c12592630aef72feaf18bd3f197ef587bbfa21131b31c38b23ab2e55fce92e36", size = 426362932, upload-time = "2026-05-13T14:54:15.295Z" }, + { url = "https://files.pythonhosted.org/packages/e2/d2/a7dd5a3f9bdaa7842124e8e2359202b317c48d47d2fc5816fafdf2049adb/torch-2.12.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:415c1b8d0412f67551c8e89a2daca0fb3e56694af0281ba155eaa9da481f58b4", size = 532170085, upload-time = "2026-05-13T14:55:20.788Z" }, + { url = "https://files.pythonhosted.org/packages/12/1b/a61ce2004f9ab0ea8964a6e6168133a127795667639e2ff4f8f2bdb16a65/torch-2.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:dd37188ea325042cb1f6cafa56822b11ada2520c04791a52629b0af25bdfbfd9", size = 122953128, upload-time = "2026-05-13T14:54:52.744Z" }, + { url = "https://files.pythonhosted.org/packages/ef/bb/285d643f254731294c9b595a007eac39db4600a98682d7bca688f42ca164/torch-2.12.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:b41339df93d491435e790ff8bcbae1c0ce777175889bfd1281d119862793e6a2", size = 88010197, upload-time = "2026-05-13T14:55:35.414Z" }, + { url = "https://files.pythonhosted.org/packages/79/81/76debf1db1343bd929bbb5d74c89fb437c2ed88eb144712557e7bd3eea45/torch-2.12.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:8fbef9f108a863e7722a73740998967e3b074742a834fc5be3a535a2befa7057", size = 426376751, upload-time = "2026-05-13T14:55:03.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/f0/80026028b603c4650ff270fc3785bdef4bd6738765a9cc5a0f5a637d65a2/torch-2.12.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:4b4f64c2c2b11f7510d93dd6412b87025ff6eddd6bb61c3b5a3d892ea20c4756", size = 532261691, upload-time = "2026-05-13T14:52:54.453Z" }, + { url = "https://files.pythonhosted.org/packages/b9/c2/64b06cbb7830fb3cd9be13e1158b31a3f36b68e6a209105ee3c9d9480be0/torch-2.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:8b958caff4a14d3a3b0b2dfc6a378f64dda9728a9dad28c08a0db9ce4dafb549", size = 122988114, upload-time = "2026-05-13T14:54:42.153Z" }, + { url = "https://files.pythonhosted.org/packages/86/ca/01896c80ba921676aa45886b2c5b8d774912de2a1f719de48169c6f755cd/torch-2.12.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:90dd587a5f61bfe1307148b581e2084fc5bc4a06e2b90a20e9a36b81087ff16b", size = 88009511, upload-time = "2026-05-13T14:54:47.411Z" }, + { url = "https://files.pythonhosted.org/packages/a5/04/52bdaf4787eab6ac7d7f5851dff934e4def0bc8ead9c8fd2b69b3e529699/torch-2.12.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:864392c73b7654f4d2b3ae712f607937d0dbb1101c4555fbb41848106b297f39", size = 426383231, upload-time = "2026-05-13T14:53:32.129Z" }, + { url = "https://files.pythonhosted.org/packages/49/8a/94bdecd13f5aaa90d45920b89789d9fe7c6f4af8c3cdd7ce01fcb59908fc/torch-2.12.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5d6b560dfa7d56291c07d615c3bb73e8d9943d9b6d87f76cd0d9d570c4797fa6", size = 532269288, upload-time = "2026-05-13T14:53:49.423Z" }, + { url = "https://files.pythonhosted.org/packages/3e/2f/bdbaaa267de519ef1b73054bf590d8c93c37a266c9a4e24a01bd38b6918f/torch-2.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:3fee918902090ade827643e758e98363278815de583c75d111fdd665ebffde9f", size = 122987706, upload-time = "2026-05-13T14:54:00.335Z" }, + { url = "https://files.pythonhosted.org/packages/9b/ad/e95e822f3538171e22640a7fbe839a1fdb666600bf6487025de2ff03b11a/torch-2.12.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:10ee1448a9f304d3b987eb4656f664ba6e4d7b410ca7a5a7c642199777a2cf88", size = 88319556, upload-time = "2026-05-13T14:54:05.574Z" }, + { url = "https://files.pythonhosted.org/packages/b7/07/055d06d985b445d67422d25b033c11cf55bbb81785d4c4e68e28bca5820e/torch-2.12.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:af68dbf403439cae9ceaeaaf92f8352b460787dcd27b92aa05c40dd4a19c0f1e", size = 426397656, upload-time = "2026-05-13T14:52:38.84Z" }, + { url = "https://files.pythonhosted.org/packages/43/94/b0b4fdc3014122e0a7302fb90086d352aa48f2576f0b252561ebb38c01a8/torch-2.12.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:a6a2eebb237d3b1d9ad3b378e86d9b9e0782afdea8b1e0eba6a13646b9b49c07", size = 532183124, upload-time = "2026-05-13T14:53:16.178Z" }, + { url = "https://files.pythonhosted.org/packages/d8/c8/052405e6ad05d3237bfe5a4df78f917773956f8e17813a2d44c059068b74/torch-2.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2140e373e9a51a3e22ef62e8d14366d0b470d18f0adf19fdc757368077133a34", size = 123232462, upload-time = "2026-05-13T14:52:27.26Z" }, + { url = "https://files.pythonhosted.org/packages/67/dc/ac069f8d6e8be701535921141055293b0d4819d3d7f224a4612cf157c7f9/torch-2.12.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:f7dfae4a519197dfa050e98d8e36378a0fb5899625a875c2b54445005a2e404e", size = 88027282, upload-time = "2026-05-13T14:53:05.258Z" }, + { url = "https://files.pythonhosted.org/packages/33/c3/1c1eb00e34555b536dddf792676026a988d710ed36981aa00499b36b0620/torch-2.12.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:891c769072637c74e9a5a77a3bc782894696d8ffec83b938df8536dee7f0ba78", size = 426386961, upload-time = "2026-05-13T14:51:28.406Z" }, + { url = "https://files.pythonhosted.org/packages/cd/d4/7e730dba0c7032a4154dc9056b76cf9625515e030e269cfbf8098fcfee7d/torch-2.12.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:e2ad3eb85d39c3cab62dfa93ed5a73516e6a53c6713cb97d004004fe089f0f1f", size = 532272265, upload-time = "2026-05-13T14:51:59.308Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b4/92c80d1bbfee1c0036c06d1d2155a3065bd2423134c83bf8a47e65cd6b9b/torch-2.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:c66696857e987efb8bc1777a37357ec4f60ab5e8af6250b83d6034437fa2d8f3", size = 122987138, upload-time = "2026-05-13T14:51:45.942Z" }, + { url = "https://files.pythonhosted.org/packages/7b/78/2e12b37ce50a19a037d7bc62d652a5a8f27385a7b05859d6bc9204f20cfe/torch-2.12.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:b4556715c8572758625d62b6e0ae3b1f76c440221913a6fb5e100f321fb4fb02", size = 88320100, upload-time = "2026-05-13T14:51:39.955Z" }, + { url = "https://files.pythonhosted.org/packages/56/5e/83c450ec7b0bb40a7b74611c1b5440f9260e33c54c90d556fd4a1f0fd955/torch-2.12.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:a43ac605a5e13116c72b64c359644cce0229f213dde48d2ae0ae5eb5becf7feb", size = 426391871, upload-time = "2026-05-13T14:52:14.989Z" }, + { url = "https://files.pythonhosted.org/packages/c9/e9/1a0b575d98d0afedd8f157d23fa3d2759421483660448e60d0a4b10b6daa/torch-2.12.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6a7512adfdd7f6732e40de1c620831e3c75b39b98cef60b11d0c5f0a76473ec5", size = 532192241, upload-time = "2026-05-13T14:51:07.795Z" }, + { url = "https://files.pythonhosted.org/packages/88/21/afadd25ecd81b3cea1e11c73cf1ab41a983a50271548c3ec7ec3b9efc3e9/torch-2.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:5f96b63f8287f66a005dd1b5a6abba2920f11156c5e5c4d815f3e2050fd1aa16", size = 123231092, upload-time = "2026-05-13T14:51:18.854Z" }, +] + +[[package]] +name = "tqdm" +version = "4.67.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", size = 169598, upload-time = "2026-02-03T17:35:53.048Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" }, +] + +[[package]] +name = "triton" +version = "3.7.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/97/dcd1f2a0f8336691bff74abc59b2ed9c69a0c0f8f65cd77109c49e05f068/triton-3.7.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223ac302091491436c248a34ee1e6c47a1026486579103c906ffd805be50cb89", size = 188367104, upload-time = "2026-05-07T19:04:56.68Z" }, + { url = "https://files.pythonhosted.org/packages/b2/c0/c2ac4fd2d8809b7579d4a820a0f9e5de62a9bc8a757ed4b3abf4f7ee964a/triton-3.7.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c631b65668d4951213b948a413c0564184305b77bb45cc9d686d3e1ecc4701a3", size = 201313191, upload-time = "2026-05-07T18:45:58.444Z" }, + { url = "https://files.pythonhosted.org/packages/b8/c1/5d842314bb6c78442cc60437928781701c6050b8d479bc2a1aed691d37ca/triton-3.7.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a9e71fc392675fac364e0ecf4ef3f76f85b7f5433a16f4c3c5fe5f05a52c85fe", size = 188480277, upload-time = "2026-05-07T19:05:03.231Z" }, + { url = "https://files.pythonhosted.org/packages/13/31/8315ea5f8dd18e60970b3022e3a8b93fd37e0b784fbbef86e10c8e6e5ca1/triton-3.7.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22bacffce443f54593dd20f05294d5a40622e0ea9ab632816f87154504356221", size = 201415942, upload-time = "2026-05-07T18:46:06.479Z" }, + { url = "https://files.pythonhosted.org/packages/f7/13/ec05adfcd87311d532ba61e3af143e8be59fcd26675884c4682841406a20/triton-3.7.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4bf49b00a7a377a68a6da603a876e797614e6455a80e9021669c476a953ad9a", size = 188505104, upload-time = "2026-05-07T19:05:09.843Z" }, + { url = "https://files.pythonhosted.org/packages/62/7b/468a576e35beef1426e0828e28e9ba9e65f5474d496f16ee126c15646324/triton-3.7.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f111161d49bf903c0eaedde3962353a3d841c08a836839b7cc1025b8426efcf", size = 201457567, upload-time = "2026-05-07T18:46:13.505Z" }, + { url = "https://files.pythonhosted.org/packages/01/e1/a59a583de59b8f62c495d67c80ee3ea97d09e91ac80c4c6e76456ed8d8ac/triton-3.7.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:abdf6beaa89b1bcfb9a43cd990536ce66091a997841a4814b260b7bee4c88c3c", size = 188503209, upload-time = "2026-05-07T19:05:17.935Z" }, + { url = "https://files.pythonhosted.org/packages/30/b1/b7507bb9815d403927c8dd51d4158ed2e11751a92dbc118a044f247b6848/triton-3.7.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a35d7afe3f3f058e7ec49fcce09794049e0ffc5c59019ac25ec3413741b8c4e7", size = 201453566, upload-time = "2026-05-07T18:46:20.427Z" }, + { url = "https://files.pythonhosted.org/packages/a6/8f/0bea7a6a0c989315c9135a1d7fb37e41905cfb3a17cbc1f10044ebd4cc3a/triton-3.7.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc1d61c172d257db80ddf42595131fb196ad2e9bdd751e90fe2ef13531734e8b", size = 188612899, upload-time = "2026-05-07T19:05:24.955Z" }, + { url = "https://files.pythonhosted.org/packages/e1/02/d96f57828d0912aec733b9bc7e0e7dbfd2c6f079a8fa433ac25cb93d1a30/triton-3.7.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:70fb9bbdc9f400afc54bbf6eb2670af28829a6ae3996863317964783141daf56", size = 201553816, upload-time = "2026-05-07T18:46:27.49Z" }, + { url = "https://files.pythonhosted.org/packages/40/fb/82a802dac4689f2a2fb2e69302e6a138eecc3e175bbe976ba3cfc717683a/triton-3.7.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a44a8476d0d3571eac4e4d1048e1ff75aad81a09ff4602ccfc56c6dea1672e", size = 188507879, upload-time = "2026-05-07T19:05:32.209Z" }, + { url = "https://files.pythonhosted.org/packages/8f/af/9904ec6d3c93d9b24e5ec360445bbdf758b7f00bfbeedb89cb0eb64eb8bb/triton-3.7.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b9b85e72968a9d8bba5ddb24e9b64aaabaf48affb042f2755cb7cfa92b7531ce", size = 201460637, upload-time = "2026-05-07T18:46:34.749Z" }, + { url = "https://files.pythonhosted.org/packages/a1/f9/4835a8ea746b88727d8899f4e3ccce4f9cacb38abfc3bb0a638266c53111/triton-3.7.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:18a160de426fd99f92b0baf509045360afbd3bfaa0b4a5171dde800ec9f09684", size = 188608706, upload-time = "2026-05-07T19:05:39.218Z" }, + { url = "https://files.pythonhosted.org/packages/c1/68/fa86e5a39608000f645535b2c124920126327ab731f8c4fafd5b07ff8d4b/triton-3.7.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce061073102714b725f3660ec6939d94a1da7984b3aa99c921417cae273672f5", size = 201546766, upload-time = "2026-05-07T18:46:42.088Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "tzdata" +version = "2026.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/19/1b9b0e29f30c6d35cb345486df41110984ea67ae69dddbc0e8a100999493/tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10", size = 198254, upload-time = "2026-04-24T15:22:08.651Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321, upload-time = "2026-04-24T15:22:05.876Z" }, +] + +[[package]] +name = "tzlocal" +version = "5.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "tzdata", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/2e/c14812d3d4d9cd1773c6be938f89e5735a1f11a9f184ac3639b93cef35d5/tzlocal-5.3.1.tar.gz", hash = "sha256:cceffc7edecefea1f595541dbd6e990cb1ea3d19bf01b2809f362a03dd7921fd", size = 30761, upload-time = "2025-03-05T21:17:41.549Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/14/e2a54fabd4f08cd7af1c07030603c3356b74da07f7cc056e600436edfa17/tzlocal-5.3.1-py3-none-any.whl", hash = "sha256:eb1a66c3ef5847adf7a834f1be0800581b683b5608e74f86ecbcef8ab91bb85d", size = 18026, upload-time = "2025-03-05T21:17:39.857Z" }, +] + +[[package]] +name = "urllib3" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, +] + +[[package]] +name = "wadler-lindig" +version = "0.1.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1e/67/cbae4bf7683a64755c2c1778c418fea96d00e34395bb91743f08bd951571/wadler_lindig-0.1.7.tar.gz", hash = "sha256:81d14d3fe77d441acf3ebd7f4aefac20c74128bf460e84b512806dccf7b2cd55", size = 15842, upload-time = "2025-06-18T07:00:42.843Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/96/04e7b441807b26b794da5b11e59ed7f83b2cf8af202bd7eba8ad2fa6046e/wadler_lindig-0.1.7-py3-none-any.whl", hash = "sha256:e3ec83835570fd0a9509f969162aeb9c65618f998b1f42918cfc8d45122fe953", size = 20516, upload-time = "2025-06-18T07:00:41.684Z" }, +] From 3dc8d639bfc6de5d440288a7360f02b0139c9813 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Fri, 23 Jan 2026 10:44:12 -0500 Subject: [PATCH 063/140] MAINT: make sure compat subpackages are listed in pyproject --- pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 319e6e4d3..d1d12f720 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -102,11 +102,13 @@ addopts = [ packages = [ "bilby", "bilby.bilby_mcmc", + "bilby.compat" "bilby.core", "bilby.core.prior", "bilby.core.sampler", "bilby.core.utils", "bilby.gw", + "bilby.gw.compat", "bilby.gw.detector", "bilby.gw.eos", "bilby.gw.likelihood", From a13304c1e35ccb173d08629df791d680ae777eeb Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Fri, 23 Jan 2026 10:46:24 -0500 Subject: [PATCH 064/140] TYPO: Fix package list formatting in pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d1d12f720..1ef14fc63 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -102,7 +102,7 @@ addopts = [ packages = [ "bilby", "bilby.bilby_mcmc", - "bilby.compat" + "bilby.compat", "bilby.core", "bilby.core.prior", "bilby.core.sampler", From f7ba0d3de8bd2d854dfd51aec4bf6f9bb18ed2a4 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Fri, 23 Jan 2026 11:00:43 -0500 Subject: [PATCH 065/140] BUG: readd erroneously removed line --- bilby/gw/likelihood/base.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bilby/gw/likelihood/base.py b/bilby/gw/likelihood/base.py index 269b28496..4319c3581 100644 --- a/bilby/gw/likelihood/base.py +++ b/bilby/gw/likelihood/base.py @@ -1118,6 +1118,7 @@ def get_sky_frame_parameters(self, parameters): ) else: raise + else: ra = parameters["ra"] dec = parameters["dec"] if "geocent" not in self.time_reference and f"{self.time_reference}_time" in parameters: From 3ec5f6cd69014de8f15a3f89473124aa383c411b Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Fri, 23 Jan 2026 12:16:33 -0500 Subject: [PATCH 066/140] DOC: remove extraneous docstring --- bilby/gw/time.py | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/bilby/gw/time.py b/bilby/gw/time.py index 51de99e3c..996bad070 100644 --- a/bilby/gw/time.py +++ b/bilby/gw/time.py @@ -172,7 +172,7 @@ def n_leap_seconds(gps_time, leap_seconds): Parameters ---------- - gps_time : float + gps_time : float | np.ndarray | int GPS time in seconds. leap_seconds : array_like GPS time of leap seconds. @@ -188,19 +188,6 @@ def n_leap_seconds(gps_time, leap_seconds): @dispatch def n_leap_seconds(gps_time: np.ndarray | float | int): # noqa F811 - """ - Calculate the number of leap seconds that have occurred up to a given GPS time. - - Parameters - ---------- - gps_time : float | np.ndarray | int - GPS time in seconds. - - Returns - ------- - float - Number of leap seconds - """ xp = array_module(gps_time) return n_leap_seconds(gps_time, xp.array(LEAP_SECONDS)) From 79ef95cabcec84a6da0881f89fbe50e3045563e2 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Thu, 29 Jan 2026 08:09:09 +0000 Subject: [PATCH 067/140] TEST: fix test failures --- bilby/gw/likelihood/multiband.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/bilby/gw/likelihood/multiband.py b/bilby/gw/likelihood/multiband.py index c805627ce..87a61036d 100644 --- a/bilby/gw/likelihood/multiband.py +++ b/bilby/gw/likelihood/multiband.py @@ -715,11 +715,8 @@ def _setup_time_marginalization_multiband(self): ) / 2 N = self.Nbs[-1] // 2 self._delta_tc = self.durations[0] / N - self._times = ( - np.arange(N) * self._delta_tc + (self._beam_pattern_reference_time - self.interferometers.start_time) - ) % self.interferometers.duration + self.interferometers.start_time - self.time_prior_array = \ - self.priors['geocent_time'].prob(self._times) * self._delta_tc + self._times = self.interferometers.start_time + np.arange(N) * self._delta_tc + self.time_prior_array = self.priors['geocent_time'].prob(self._times) * self._delta_tc # allocate array which is FFTed at each likelihood evaluation self._full_d_h = np.zeros(N, dtype=complex) # idxs to convert full frequency points to banded frequency points, used for filling _full_d_h. From 19f296bb21d209c4b03fc4d0950f93bbf83de0e7 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Sat, 31 Jan 2026 11:31:03 +0000 Subject: [PATCH 068/140] TEST: start adding jax tests --- bilby/core/utils/calculus.py | 24 +++++--- bilby/core/utils/samples.py | 7 ++- optional_requirements.txt | 1 + requirements.txt | 3 +- test/conftest.py | 38 ++++++++++++ test/core/likelihood_test.py | 23 +++++-- test/core/series_test.py | 32 +++++----- test/core/utils_test.py | 114 +++++++++++++++++++++-------------- 8 files changed, 163 insertions(+), 79 deletions(-) diff --git a/bilby/core/utils/calculus.py b/bilby/core/utils/calculus.py index 7b9e9b017..bf3714caa 100644 --- a/bilby/core/utils/calculus.py +++ b/bilby/core/utils/calculus.py @@ -6,7 +6,7 @@ from scipy.special import logsumexp from .log import logger -from ...compat.utils import array_module +from ...compat.utils import array_module, xp_wrap def derivatives( @@ -154,7 +154,8 @@ def derivatives( return grads -def logtrapzexp(lnf, dx): +@xp_wrap +def logtrapzexp(lnf, dx, *, xp=np): """ Perform trapezium rule integration for the logarithm of a function on a grid. @@ -173,18 +174,23 @@ def logtrapzexp(lnf, dx): lnfdx1 = lnf[:-1] lnfdx2 = lnf[1:] - if isinstance(dx, (int, float)): + + if ( + isinstance(dx, (int, float)) or + (aac.is_array_api_obj(dx) and dx.size == 1) + ): C = np.log(dx / 2.0) - elif isinstance(dx, (list, np.ndarray)): - if len(dx) != len(lnf) - 1: + elif isinstance(dx, (list, xp.ndarray)): + dx = xp.asarray(dx) + if dx.size != len(lnf) - 1: raise ValueError( "Step size array must have length one less than the function length" ) - lndx = np.log(dx) + lndx = xp.log(dx) lnfdx1 = lnfdx1.copy() + lndx lnfdx2 = lnfdx2.copy() + lndx - C = -np.log(2.0) + C = -xp.log(2.0) else: raise TypeError("Step size must be a single value or array-like") @@ -235,7 +241,7 @@ def __init__(self, x, y, z, bbox=[None] * 4, kx=3, ky=3, s=0, fill_value=None): 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) + 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): @@ -269,7 +275,7 @@ def _call_jax(self, x, y): jnp.asarray(self.x), jnp.asarray(self.y), jnp.asarray(self.z), - extrap=self.fill_value, + extrap=self.fill_value if self.fill_value is not None else False, method="cubic2", ) diff --git a/bilby/core/utils/samples.py b/bilby/core/utils/samples.py index a075d6dcd..93fdac0ac 100644 --- a/bilby/core/utils/samples.py +++ b/bilby/core/utils/samples.py @@ -1,3 +1,4 @@ +import array_api_extra as xpx import numpy as np from scipy.special import logsumexp @@ -135,7 +136,7 @@ def reflect(u): u: array-like The input array, modified in place. """ - idxs_even = np.mod(u, 2) < 1 - u[idxs_even] = np.mod(u[idxs_even], 1) - u[~idxs_even] = 1 - np.mod(u[~idxs_even], 1) + idxs_even = (u % 2) < 1 + u = xpx.at(u)[idxs_even].set(u[idxs_even] % 1) + u = xpx.at(u)[~idxs_even].set(1 - (u[~idxs_even] % 1)) return u diff --git a/optional_requirements.txt b/optional_requirements.txt index c10d7908b..f0f2205f6 100644 --- a/optional_requirements.txt +++ b/optional_requirements.txt @@ -1,5 +1,6 @@ celerite george +parameterized plotly pytest-requires pytest-rerunfailures diff --git a/requirements.txt b/requirements.txt index 3539f45b3..ead66363d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,5 @@ +array_api_compat +array_api_extra dynesty>=2.0.1 emcee corner @@ -11,4 +13,3 @@ tqdm h5py attrs plum-dispatch -array_api_compat diff --git a/test/conftest.py b/test/conftest.py index d08c38604..6da49894b 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -1,3 +1,4 @@ +import importlib import pytest @@ -5,6 +6,11 @@ def pytest_addoption(parser): parser.addoption( "--skip-roqs", action="store_true", default=False, help="Skip all tests that require ROQs" ) + parser.addoption( + "--array-backend", + default="numpy", + help="Which array to use for testing", + ) def pytest_configure(config): @@ -17,3 +23,35 @@ def pytest_collection_modifyitems(config, items): for item in items: if "requires_roqs" in item.keywords: item.add_marker(skip_roqs) + if config.getoption("--array-backend") is not None: + array_only = pytest.mark.skip(reason="Only running backend dependent tests") + for item in items: + if "array_backend" not in item.keywords: + item.add_marker(array_only) + + +def _xp(request): + backend = request.config.getoption("--array-backend") + match backend: + case None | "numpy": + import numpy + return numpy + case "jax" | "jax.numpy": + import jax + jax.config.update("jax_enable_x64", True) + return jax.numpy + case _: + try: + importlib.import_module(backend) + except ImportError: + raise ValueError(f"Unknown backend for testing: {backend}") + + +@pytest.fixture +def xp(request): + return _xp(request) + + +@pytest.fixture(scope="class") +def xp_class(request): + request.cls.xp = _xp(request) diff --git a/test/core/likelihood_test.py b/test/core/likelihood_test.py index a8e570cd5..5bef5bf62 100644 --- a/test/core/likelihood_test.py +++ b/test/core/likelihood_test.py @@ -2,6 +2,7 @@ from unittest import mock import numpy as np +import pytest import bilby.core.likelihood from bilby.core.likelihood import ( @@ -51,10 +52,12 @@ def test_meta_data(self): self.assertEqual(self.likelihood.meta_data, meta_data) +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestAnalytical1DLikelihood(unittest.TestCase): def setUp(self): - self.x = np.arange(start=0, stop=100, step=1) - self.y = np.arange(start=0, stop=100, step=1) + self.x = self.xp.arange(start=0, stop=100, step=1) + self.y = self.xp.arange(start=0, stop=100, step=1) def test_func(x, parameter1, parameter2): return parameter1 * x + parameter2 @@ -78,7 +81,7 @@ def test_init_x(self): self.assertTrue(np.array_equal(self.x, self.analytical_1d_likelihood.x)) def test_set_x_to_array(self): - new_x = np.arange(start=0, stop=50, step=2) + new_x = self.xp.arange(start=0, stop=50, step=2) self.analytical_1d_likelihood.x = new_x self.assertTrue(np.array_equal(new_x, self.analytical_1d_likelihood.x)) @@ -98,7 +101,7 @@ def test_init_y(self): self.assertTrue(np.array_equal(self.y, self.analytical_1d_likelihood.y)) def test_set_y_to_array(self): - new_y = np.arange(start=0, stop=50, step=2) + new_y = self.xp.arange(start=0, stop=50, step=2) self.analytical_1d_likelihood.y = new_y self.assertTrue(np.array_equal(new_y, self.analytical_1d_likelihood.y)) @@ -154,17 +157,20 @@ def test_repr(self): self.assertEqual(expected, repr(self.analytical_1d_likelihood)) +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestGaussianLikelihood(unittest.TestCase): def setUp(self): self.N = 100 self.sigma = 0.1 - self.x = np.linspace(0, 1, self.N) - self.y = 2 * self.x + 1 + np.random.normal(0, self.sigma, self.N) + self.x = self.xp.linspace(0, 1, self.N) + self.y = 2 * self.x + 1 + self.xp.array(np.random.normal(0, self.sigma, self.N)) def test_function(x, m, c): return m * x + c self.function = test_function + self.parameters = dict(m=self.xp.array(2.0), c=self.xp.array(0.0)) def tearDown(self): del self.N @@ -211,6 +217,11 @@ def test_repr(self): ) self.assertEqual(expected, repr(likelihood)) + def test_return_class(self): + likelihood = GaussianLikelihood(self.x, self.y, self.function, self.sigma) + logl = likelihood.log_likelihood(self.parameters) + self.assertEqual(logl.__array_namespace__(), self.xp) + class TestStudentTLikelihood(unittest.TestCase): def setUp(self): diff --git a/test/core/series_test.py b/test/core/series_test.py index bf1b19c43..7b85c2bc5 100644 --- a/test/core/series_test.py +++ b/test/core/series_test.py @@ -1,15 +1,19 @@ import unittest + import numpy as np +import pytest from bilby.core.utils import create_frequency_series, create_time_series from bilby.core.series import CoupledTimeAndFrequencySeries +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestCoupledTimeAndFrequencySeries(unittest.TestCase): def setUp(self): - self.duration = 2 - self.sampling_frequency = 4096 - self.start_time = -1 + self.duration = self.xp.array(2.0) + self.sampling_frequency = self.xp.array(4096.0) + self.start_time = self.xp.array(-1.0) self.series = CoupledTimeAndFrequencySeries( duration=self.duration, sampling_frequency=self.sampling_frequency, @@ -43,10 +47,10 @@ def test_start_time_from_init(self): self.assertEqual(self.start_time, self.series.start_time) def test_frequency_array_type(self): - self.assertIsInstance(self.series.frequency_array, np.ndarray) + self.assertIsInstance(self.series.frequency_array, self.xp.ndarray) def test_time_array_type(self): - self.assertIsInstance(self.series.time_array, np.ndarray) + self.assertIsInstance(self.series.time_array, self.xp.ndarray) def test_frequency_array_from_init(self): expected = create_frequency_series( @@ -63,8 +67,8 @@ def test_time_array_from_init(self): self.assertTrue(np.array_equal(expected, self.series.time_array)) def test_frequency_array_setter(self): - new_sampling_frequency = 100 - new_duration = 3 + new_sampling_frequency = self.xp.array(100.0) + new_duration = self.xp.array(3.0) new_frequency_array = create_frequency_series( sampling_frequency=new_sampling_frequency, duration=new_duration ) @@ -79,9 +83,9 @@ def test_frequency_array_setter(self): self.assertAlmostEqual(self.start_time, self.series.start_time) def test_time_array_setter(self): - new_sampling_frequency = 100 - new_duration = 3 - new_start_time = 4 + new_sampling_frequency = self.xp.array(100.0) + new_duration = self.xp.array(3.0) + new_start_time = self.xp.array(4.0) new_time_array = create_time_series( sampling_frequency=new_sampling_frequency, duration=new_duration, @@ -97,24 +101,24 @@ def test_time_array_setter(self): def test_time_array_without_sampling_frequency(self): self.series.sampling_frequency = None - self.series.duration = 4 + self.series.duration = self.xp.array(4) with self.assertRaises(ValueError): _ = self.series.time_array def test_time_array_without_duration(self): - self.series.sampling_frequency = 4096 + self.series.sampling_frequency = self.xp.array(4096) self.series.duration = None with self.assertRaises(ValueError): _ = self.series.time_array def test_frequency_array_without_sampling_frequency(self): self.series.sampling_frequency = None - self.series.duration = 4 + self.series.duration = self.xp.array(4) with self.assertRaises(ValueError): _ = self.series.frequency_array def test_frequency_array_without_duration(self): - self.series.sampling_frequency = 4096 + self.series.sampling_frequency = self.xp.array(4096) self.series.duration = None with self.assertRaises(ValueError): _ = self.series.frequency_array diff --git a/test/core/utils_test.py b/test/core/utils_test.py index df46d6bb3..fdd4afeef 100644 --- a/test/core/utils_test.py +++ b/test/core/utils_test.py @@ -1,6 +1,7 @@ import unittest import os +import array_api_extra as xpx import dill import numpy as np from astropy import constants @@ -49,35 +50,39 @@ def test_gravitational_constant(self): self.assertEqual(bilby.core.utils.gravitational_constant, lal.G_SI) +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestFFT(unittest.TestCase): def setUp(self): - self.sampling_frequency = 10 + self.sampling_frequency = self.xp.array(10) def tearDown(self): del self.sampling_frequency def test_nfft_sine_function(self): - injected_frequency = 2.7324 - duration = 100 - times = utils.create_time_series(self.sampling_frequency, duration) + xp = self.xp + injected_frequency = xp.array(2.7324) + duration = xp.array(100) + times = utils.create_time_series(xp.array(self.sampling_frequency), duration) - time_domain_strain = np.sin(2 * np.pi * times * injected_frequency + 0.4) + time_domain_strain = xp.sin(2 * np.pi * times * injected_frequency + 0.4) frequency_domain_strain, frequencies = bilby.core.utils.nfft( time_domain_strain, self.sampling_frequency ) - frequency_at_peak = frequencies[np.argmax(np.abs(frequency_domain_strain))] + frequency_at_peak = frequencies[xp.argmax(abs(frequency_domain_strain))] self.assertAlmostEqual(injected_frequency, frequency_at_peak, places=1) def test_nfft_infft(self): - time_domain_strain = np.random.normal(0, 1, 10) + xp = self.xp + time_domain_strain = xp.array(np.random.normal(0, 1, 10)) frequency_domain_strain, _ = bilby.core.utils.nfft( time_domain_strain, self.sampling_frequency ) new_time_domain_strain = bilby.core.utils.infft( frequency_domain_strain, self.sampling_frequency ) - self.assertTrue(np.allclose(time_domain_strain, new_time_domain_strain)) + self.assertTrue(xp.allclose(time_domain_strain, new_time_domain_strain)) class TestInferParameters(unittest.TestCase): @@ -119,11 +124,13 @@ def test_self_handling_method_as_function(self): self.assertListEqual(expected, actual) +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestTimeAndFrequencyArrays(unittest.TestCase): def setUp(self): - self.start_time = 1.3 - self.sampling_frequency = 5 - self.duration = 1.6 + self.start_time = self.xp.array(1.3) + self.sampling_frequency = self.xp.array(5) + self.duration = self.xp.array(1.6) self.frequency_array = utils.create_frequency_series( sampling_frequency=self.sampling_frequency, duration=self.duration ) @@ -141,12 +148,13 @@ def tearDown(self): del self.time_array def test_create_time_array(self): - expected_time_array = np.array([1.3, 1.5, 1.7, 1.9, 2.1, 2.3, 2.5, 2.7]) + expected_time_array = self.xp.array([1.3, 1.5, 1.7, 1.9, 2.1, 2.3, 2.5, 2.7]) time_array = utils.create_time_series( sampling_frequency=self.sampling_frequency, duration=self.duration, starting_time=self.start_time, ) + self.assertEqual(time_array.__array_namespace__(), self.xp) self.assertTrue(np.allclose(expected_time_array, time_array)) def test_create_frequency_array(self): @@ -164,7 +172,7 @@ def test_get_sampling_frequency_from_time_array(self): self.assertEqual(self.sampling_frequency, new_sampling_freq) def test_get_sampling_frequency_from_time_array_unequally_sampled(self): - self.time_array[-1] += 0.0001 + self.time_array = xpx.at(self.time_array, -1).set(self.time_array[-1] + 0.0001) with self.assertRaises(ValueError): _, _ = utils.get_sampling_frequency_and_duration_from_time_array( self.time_array @@ -190,7 +198,9 @@ def test_get_sampling_frequency_from_frequency_array(self): self.assertEqual(self.sampling_frequency, new_sampling_freq) def test_get_sampling_frequency_from_frequency_array_unequally_sampled(self): - self.frequency_array[-1] += 0.0001 + self.frequency_array = xpx.at( + self.frequency_array, -1 + ).set(self.frequency_array[-1] + 0.0001) with self.assertRaises(ValueError): _, _ = utils.get_sampling_frequency_and_duration_from_frequency_array( self.frequency_array @@ -233,34 +243,38 @@ def test_consistency_frequency_array_to_frequency_array(self): def test_illegal_sampling_frequency_and_duration(self): with self.assertRaises(utils.IllegalDurationAndSamplingFrequencyException): _ = utils.create_time_series( - sampling_frequency=7.7, duration=1.3, starting_time=0 + sampling_frequency=self.xp.array(7.7), + duration=self.xp.array(1.3), + starting_time=self.xp.array(0), ) +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestReflect(unittest.TestCase): def test_in_range(self): - xprime = np.array([0.1, 0.5, 0.9]) - x = np.array([0.1, 0.5, 0.9]) + xprime = self.xp.array([0.1, 0.5, 0.9]) + x = self.xp.array([0.1, 0.5, 0.9]) self.assertTrue(np.testing.assert_allclose(utils.reflect(xprime), x) is None) def test_in_one_to_two(self): - xprime = np.array([1.1, 1.5, 1.9]) - x = np.array([0.9, 0.5, 0.1]) + xprime = self.xp.array([1.1, 1.5, 1.9]) + x = self.xp.array([0.9, 0.5, 0.1]) self.assertTrue(np.testing.assert_allclose(utils.reflect(xprime), x) is None) def test_in_two_to_three(self): - xprime = np.array([2.1, 2.5, 2.9]) - x = np.array([0.1, 0.5, 0.9]) + xprime = self.xp.array([2.1, 2.5, 2.9]) + x = self.xp.array([0.1, 0.5, 0.9]) self.assertTrue(np.testing.assert_allclose(utils.reflect(xprime), x) is None) def test_in_minus_one_to_zero(self): - xprime = np.array([-0.9, -0.5, -0.1]) - x = np.array([0.9, 0.5, 0.1]) + xprime = self.xp.array([-0.9, -0.5, -0.1]) + x = self.xp.array([0.9, 0.5, 0.1]) self.assertTrue(np.testing.assert_allclose(utils.reflect(xprime), x) is None) def test_in_minus_two_to_minus_one(self): - xprime = np.array([-1.9, -1.5, -1.1]) - x = np.array([0.1, 0.5, 0.9]) + xprime = self.xp.array([-1.9, -1.5, -1.1]) + x = self.xp.array([0.1, 0.5, 0.9]) self.assertTrue(np.testing.assert_allclose(utils.reflect(xprime), x) is None) @@ -325,6 +339,8 @@ 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): self.xx = np.linspace(0, 1, 10) @@ -343,36 +359,42 @@ def test_returns_none_for_floats_outside_range(self): self.assertIsNone(self.interpolant(-0.5, 0.5)) def test_returns_float_for_float_and_array(self): - self.assertIsInstance(self.interpolant(0.5, np.random.random(10)), np.ndarray) - self.assertIsInstance(self.interpolant(np.random.random(10), 0.5), np.ndarray) - self.assertIsInstance( - self.interpolant(np.random.random(10), np.random.random(10)), np.ndarray + input_array = self.xp.array(np.random.random(10)) + self.assertEqual(self.interpolant(input_array, 0.5).__array_namespace__(), self.xp) + self.assertEqual( + self.interpolant(input_array, input_array).__array_namespace__(), self.xp ) + self.assertEqual(self.interpolant(0.5, input_array).__array_namespace__(), self.xp) def test_raises_for_mismatched_arrays(self): with self.assertRaises(ValueError): - self.interpolant(np.random.random(10), np.random.random(20)) + self.interpolant( + self.xp.array(np.random.random(10)), + self.xp.array(np.random.random(20)), + ) def test_returns_fill_in_correct_place(self): - x_data = np.random.random(10) - y_data = np.random.random(10) - x_data[3] = -1 - self.assertTrue(np.isnan(self.interpolant(x_data, y_data)[3])) + x_data = self.xp.array(np.random.random(10)) + y_data = self.xp.array(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): def setUp(self): - self.x = np.linspace(0, 1, 100) - self.dxs = np.diff(self.x) + self.x = self.xp.linspace(0, 1, 100) + self.dxs = self.xp.diff(self.x) self.dx = self.dxs[0] with np.errstate(divide="ignore"): - self.lnfunc1 = np.log(self.x) + self.lnfunc1 = self.xp.log(self.x) self.func1int = (self.x[-1] ** 2 - self.x[0] ** 2) / 2 with np.errstate(divide="ignore"): - self.lnfunc2 = np.log(self.x ** 2) + self.lnfunc2 = self.xp.log(self.x ** 2) self.func2int = (self.x[-1] ** 3 - self.x[0] ** 3) / 3 - self.irregularx = np.array( + self.irregularx = self.xp.array( [ self.x[0], self.x[12], @@ -390,9 +412,9 @@ def setUp(self): ] ) with np.errstate(divide="ignore"): - self.lnfunc1irregular = np.log(self.irregularx) - self.lnfunc2irregular = np.log(self.irregularx ** 2) - self.irregulardxs = np.diff(self.irregularx) + self.lnfunc1irregular = self.xp.log(self.irregularx) + self.lnfunc2irregular = self.xp.log(self.irregularx ** 2) + self.irregulardxs = self.xp.diff(self.irregularx) def test_incorrect_step_type(self): with self.assertRaises(TypeError): @@ -407,19 +429,19 @@ def test_integral_func1(self): res2 = utils.logtrapzexp(self.lnfunc1, self.dxs) self.assertTrue(np.abs(res1 - res2) < 1e-12) - self.assertTrue(np.abs((np.exp(res1) - self.func1int) / self.func1int) < 1e-12) + self.assertTrue(np.abs((self.xp.exp(res1) - self.func1int) / self.func1int) < 1e-12) def test_integral_func2(self): res = utils.logtrapzexp(self.lnfunc2, self.dxs) - self.assertTrue(np.abs((np.exp(res) - self.func2int) / self.func2int) < 1e-4) + self.assertTrue(np.abs((self.xp.exp(res) - self.func2int) / self.func2int) < 1e-4) def test_integral_func1_irregular_steps(self): res = utils.logtrapzexp(self.lnfunc1irregular, self.irregulardxs) - self.assertTrue(np.abs((np.exp(res) - self.func1int) / self.func1int) < 1e-12) + self.assertTrue(np.abs((self.xp.exp(res) - self.func1int) / self.func1int) < 1e-12) def test_integral_func2_irregular_steps(self): res = utils.logtrapzexp(self.lnfunc2irregular, self.irregulardxs) - self.assertTrue(np.abs((np.exp(res) - self.func2int) / self.func2int) < 1e-2) + self.assertTrue(np.abs((self.xp.exp(res) - self.func2int) / self.func2int) < 1e-2) class TestSavingNumpyRandomGenerator(unittest.TestCase): From f57b86fd78819e56dfdbbb3c64f574fa2403c3f4 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Sat, 31 Jan 2026 11:33:11 +0000 Subject: [PATCH 069/140] CI: add jax tests to CI --- .github/workflows/unit-tests.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 3c2eaba5d..93ce16b9a 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -66,6 +66,9 @@ jobs: - name: Run unit tests run: | python -m pytest --cov=bilby --cov-branch --durations 10 -ra --color yes --cov-report=xml --junitxml=pytest.xml + - name: Run jax-backend unit tests + run: | + pytest --array-backend jax --durations 10 - name: Run sampler tests run: | pytest test/integration/sampler_run_test.py --durations 10 -v From c3c59c11b1aba391a83851c7edd9e48434958fea Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Sat, 31 Jan 2026 12:34:40 +0000 Subject: [PATCH 070/140] MAINT: add jax extras option --- .github/workflows/unit-tests.yml | 1 + pyproject.toml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 93ce16b9a..39aa3b1b8 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -68,6 +68,7 @@ jobs: python -m pytest --cov=bilby --cov-branch --durations 10 -ra --color yes --cov-report=xml --junitxml=pytest.xml - name: Run jax-backend unit tests run: | + python -m pip install .[jax] pytest --array-backend jax --durations 10 - name: Run sampler tests run: | diff --git a/pyproject.toml b/pyproject.toml index 1ef14fc63..30d311f3f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -123,10 +123,12 @@ dependencies = {file = ["requirements.txt"]} [tool.setuptools.dynamic.optional-dependencies] all = {file = [ "gw_requirements.txt", + "jax_requirements.txt", "mcmc_requirements.txt", "optional_requirements.txt" ]} gw = {file = ["gw_requirements.txt"]} +jax = {file = ["jax_requirements.txt"]} mcmc = {file = ["mcmc_requirements.txt"]} [tool.setuptools.package-data] From 2730baaccb53b5265788f5b6d99b1cbf7f13c28e Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Sat, 31 Jan 2026 12:34:59 +0000 Subject: [PATCH 071/140] Some more jax testing updates --- bilby/core/likelihood.py | 30 ++++++------ bilby/core/utils/calculus.py | 2 +- test/core/likelihood_test.py | 94 +++++++++++++++++++++++++----------- 3 files changed, 82 insertions(+), 44 deletions(-) diff --git a/bilby/core/likelihood.py b/bilby/core/likelihood.py index 68995e8e5..86e198e7c 100644 --- a/bilby/core/likelihood.py +++ b/bilby/core/likelihood.py @@ -260,13 +260,13 @@ def log_likelihood(self, parameters): raise ValueError( "Poisson rate function returns wrong value type! " "Is {} when it should be numpy.ndarray".format(type(rate))) - elif any(rate < 0.): + xp = rate.__array_namespace__() + if xp.any(rate < 0.): raise ValueError(("Poisson rate function returns a negative", " value!")) - elif any(rate == 0.): + elif xp.any(rate == 0.): return -np.inf else: - xp = array_module(rate) return xp.sum(-rate + self.y * xp.log(rate) - gammaln(self.y + 1)) def __repr__(self): @@ -279,10 +279,11 @@ def y(self): @y.setter def y(self, y): - if not isinstance(y, np.ndarray): - y = np.array([y]) + if not is_array_api_obj(y): + y = np.atleast_1d(y) + xp = y.__array_namespace__() # check array is a non-negative integer array - if y.dtype.kind not in 'ui' or np.any(y < 0): + if y.dtype.kind not in 'ui' or xp.any(y < 0): raise ValueError("Data must be non-negative integers") self.__y = y @@ -308,7 +309,7 @@ def __init__(self, x, y, func, **kwargs): def log_likelihood(self, parameters): mu = self.func(self.x, **self.model_parameters(parameters=parameters), **self.kwargs) - xp = array_module(mu) + xp = mu.__array_namespace__() if xp.any(mu < 0.): return -np.inf return -xp.sum(xp.log(mu) + (self.y / mu)) @@ -323,9 +324,10 @@ def y(self): @y.setter def y(self, y): - if not isinstance(y, np.ndarray): - y = np.array([y]) - if any(y < 0): + if not is_array_api_obj(y): + y = np.atleast_1d(y) + xp = y.__array_namespace__() + if xp.any(y < 0): raise ValueError("Data must be non-negative") self._y = y @@ -487,8 +489,7 @@ def __init__(self, mean, cov): f"Multivariate normal likelihood not implemented for {xp.__name__} backend" ) - parameters = {"x{0}".format(i): 0 for i in range(self.dim)} - super(AnalyticalMultidimensionalCovariantGaussian, self).__init__(parameters=parameters) + super(AnalyticalMultidimensionalCovariantGaussian, self).__init__() @property def dim(self): @@ -527,8 +528,7 @@ def __init__(self, mean_1, mean_2, cov): raise NotImplementedError( f"Multivariate normal likelihood not implemented for {xp.__name__} backend" ) - parameters = {"x{0}".format(i): 0 for i in range(self.dim)} - super(AnalyticalMultidimensionalBimodalCovariantGaussian, self).__init__(parameters=parameters) + super(AnalyticalMultidimensionalBimodalCovariantGaussian, self).__init__() @property def dim(self): @@ -536,7 +536,7 @@ def dim(self): def log_likelihood(self, parameters): xp = array_module(self.cov) - x = xp.array([self.parameters["x{0}".format(i)] for i in range(self.dim)]) + x = xp.array([parameters["x{0}".format(i)] for i in range(self.dim)]) return -xp.log(2) + xp.logaddexp(self.logpdf_1(x), self.logpdf_2(x)) diff --git a/bilby/core/utils/calculus.py b/bilby/core/utils/calculus.py index bf3714caa..13e1b2586 100644 --- a/bilby/core/utils/calculus.py +++ b/bilby/core/utils/calculus.py @@ -174,7 +174,7 @@ def logtrapzexp(lnf, dx, *, xp=np): lnfdx1 = lnf[:-1] lnfdx2 = lnf[1:] - + if ( isinstance(dx, (int, float)) or (aac.is_array_api_obj(dx) and dx.size == 1) diff --git a/test/core/likelihood_test.py b/test/core/likelihood_test.py index 5bef5bf62..3406cc857 100644 --- a/test/core/likelihood_test.py +++ b/test/core/likelihood_test.py @@ -3,6 +3,7 @@ import numpy as np import pytest +import array_api_extra as xpx import bilby.core.likelihood from bilby.core.likelihood import ( @@ -223,18 +224,21 @@ def test_return_class(self): self.assertEqual(logl.__array_namespace__(), self.xp) +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestStudentTLikelihood(unittest.TestCase): def setUp(self): self.N = 100 self.nu = self.N - 2 self.sigma = 1 - self.x = np.linspace(0, 1, self.N) - self.y = 2 * self.x + 1 + np.random.normal(0, self.sigma, self.N) + self.x = self.xp.linspace(0, 1, self.N) + self.y = 2 * self.x + 1 + self.xp.array(np.random.normal(0, self.sigma, self.N)) def test_function(x, m, c): return m * x + c self.function = test_function + self.parameters = dict(m=self.xp.array(2.0), c=self.xp.array(0.0)) def tearDown(self): del self.N @@ -273,6 +277,11 @@ def test_log_likelihood_nu_negative(self): with self.assertRaises(ValueError): likelihood.log_likelihood(parameters) + def test_setting_nu_positive_does_not_change_class_attribute(self): + likelihood = StudentTLikelihood(self.x, self.y, self.function, nu=None) + likelihood.nu = 98 + self.assertEqual(likelihood.nu, 98) + def test_lam(self): likelihood = StudentTLikelihood(self.x, self.y, self.function, nu=0, sigma=0.5) @@ -290,25 +299,28 @@ def test_repr(self): self.assertEqual(expected, repr(likelihood)) +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestPoissonLikelihood(unittest.TestCase): def setUp(self): self.N = 100 self.mu = 5 - self.x = np.linspace(0, 1, self.N) - self.y = np.random.poisson(self.mu, self.N) - self.yfloat = np.copy(self.y) * 1.0 - self.yneg = np.copy(self.y) - self.yneg[0] = -1 + self.x = self.xp.linspace(0, 1, self.N) + self.y = self.xp.array(np.random.poisson(self.mu, self.N)) + self.yfloat = self.y.copy() * 1.0 + self.yneg = self.y.copy() + self.yneg = xpx.at(self.yneg, 0).set(-1) def test_function(x, c): return c def test_function_array(x, c): - return np.ones(len(x)) * c + return self.xp.ones(len(x)) * c self.function = test_function self.function_array = test_function_array self.poisson_likelihood = PoissonLikelihood(self.x, self.y, self.function) + self.bad_parameters = dict(c=self.xp.array(-2.0)) def tearDown(self): del self.N @@ -341,12 +353,12 @@ def test_neg_rate_array(self): likelihood.log_likelihood(parameters) def test_init_y(self): - self.assertTrue(np.array_equal(self.y, self.poisson_likelihood.y)) + self.assertTrue(self.xp.array_equal(self.y, self.poisson_likelihood.y)) def test_set_y_to_array(self): - new_y = np.arange(start=0, stop=50, step=2) + new_y = self.xp.arange(start=0, stop=50, step=2) self.poisson_likelihood.y = new_y - self.assertTrue(np.array_equal(new_y, self.poisson_likelihood.y)) + self.assertTrue(self.xp.array_equal(new_y, self.poisson_likelihood.y)) def test_set_y_to_positive_int(self): new_y = 5 @@ -371,25 +383,25 @@ def test_log_likelihood_wrong_func_return_type(self): def test_log_likelihood_negative_func_return_element(self): poisson_likelihood = PoissonLikelihood( - x=self.x, y=self.y, func=lambda x: np.array([3, 6, -2]) + x=self.x, y=self.y, func=lambda x: self.xp.array([3, 6, -2]) ) with self.assertRaises(ValueError): poisson_likelihood.log_likelihood(dict()) def test_log_likelihood_zero_func_return_element(self): poisson_likelihood = PoissonLikelihood( - x=self.x, y=self.y, func=lambda x: np.array([3, 6, 0]) + x=self.x, y=self.y, func=lambda x: self.xp.array([3, 6, 0]) ) self.assertEqual(-np.inf, poisson_likelihood.log_likelihood(dict())) def test_log_likelihood_dummy(self): """ Merely tests if it goes into the right if else bracket """ poisson_likelihood = PoissonLikelihood( - x=self.x, y=self.y, func=lambda x: np.linspace(1, 100, self.N) + x=self.x, y=self.y, func=lambda x: self.xp.linspace(1, 100, self.N) ) - with mock.patch("array_api_compat.numpy.sum") as m: + with mock.patch(f"{self.xp.__name__}.sum") as m: m.return_value = 1 - self.assertEqual(1, poisson_likelihood.log_likelihood(dict())) + self.assertEqual(1, poisson_likelihood.log_likelihood(dict(c=5))) def test_repr(self): likelihood = PoissonLikelihood(self.x, self.y, self.function) @@ -399,26 +411,29 @@ def test_repr(self): self.assertEqual(expected, repr(likelihood)) +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestExponentialLikelihood(unittest.TestCase): def setUp(self): self.N = 100 self.mu = 5 - self.x = np.linspace(0, 1, self.N) - self.y = np.random.exponential(self.mu, self.N) - self.yneg = np.copy(self.y) - self.yneg[0] = -1.0 + self.x = self.xp.linspace(0, 1, self.N) + self.y = self.xp.array(np.random.exponential(self.mu, self.N)) + self.yneg = self.y.copy() + self.yneg = xpx.at(self.yneg, 0).set(-1.0) def test_function(x, c): return c def test_function_array(x, c): - return c * np.ones(len(x)) + return c * self.xp.ones(len(x)) self.function = test_function self.function_array = test_function_array self.exponential_likelihood = ExponentialLikelihood( x=self.x, y=self.y, func=self.function ) + self.bad_parameters = dict(c=self.xp.array(-1.0)) def tearDown(self): del self.N @@ -447,7 +462,7 @@ def test_init_y(self): self.assertTrue(np.array_equal(self.y, self.exponential_likelihood.y)) def test_set_y_to_array(self): - new_y = np.arange(start=0, stop=50, step=2) + new_y = self.xp.arange(start=0, stop=50, step=2) self.exponential_likelihood.y = new_y self.assertTrue(np.array_equal(new_y, self.exponential_likelihood.y)) @@ -472,14 +487,14 @@ def test_set_y_to_negative_float(self): def test_set_y_to_nd_array_with_negative_element(self): with self.assertRaises(ValueError): - self.exponential_likelihood.y = np.array([4.3, -1.2, 4]) + self.exponential_likelihood.y = self.xp.array([4.3, -1.2, 4]) def test_log_likelihood_default(self): """ Merely tests that it ends up at the right place in the code """ exponential_likelihood = ExponentialLikelihood( - x=self.x, y=self.y, func=lambda x: np.array([4.2]) + x=self.x, y=self.y, func=lambda x: self.xp.array([4.2]) ) - with mock.patch("array_api_compat.numpy.sum") as m: + with mock.patch(f"{self.xp.__name__}.sum") as m: m.return_value = 3 self.assertEqual(-3, exponential_likelihood.log_likelihood(dict())) @@ -490,11 +505,17 @@ def test_repr(self): self.assertEqual(expected, repr(self.exponential_likelihood)) +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestAnalyticalMultidimensionalCovariantGaussian(unittest.TestCase): def setUp(self): self.cov = [[1, 0, 0], [0, 4, 0], [0, 0, 9]] self.sigma = [1, 2, 3] self.mean = [10, 11, 12] + if self.xp != np: + self.cov = self.xp.array(self.cov) + self.sigma = self.xp.array(self.sigma) + self.mean = self.xp.array(self.mean) self.likelihood = AnalyticalMultidimensionalCovariantGaussian( mean=self.mean, cov=self.cov ) @@ -518,16 +539,30 @@ def test_dim(self): self.assertEqual(3, self.likelihood.dim) def test_log_likelihood(self): - likelihood = AnalyticalMultidimensionalCovariantGaussian(mean=[0], cov=[1]) - self.assertEqual(-np.log(2 * np.pi) / 2, likelihood.log_likelihood(dict(x0=0))) + likelihood = AnalyticalMultidimensionalCovariantGaussian( + mean=self.xp.array([0]), cov=self.xp.array([1]) + ) + logl = likelihood.log_likelihood(dict(x0=self.xp.array(0.0))) + self.assertEqual( + -np.log(2 * np.pi) / 2, + likelihood.log_likelihood(dict(x0=self.xp.array(0.0))), + ) + self.assertEqual(logl.__array_namespace__(), self.xp) +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestAnalyticalMultidimensionalBimodalCovariantGaussian(unittest.TestCase): def setUp(self): self.cov = [[1, 0, 0], [0, 4, 0], [0, 0, 9]] self.sigma = [1, 2, 3] self.mean_1 = [10, 11, 12] self.mean_2 = [20, 21, 22] + if self.xp != np: + self.cov = self.xp.array(self.cov) + self.sigma = self.xp.array(self.sigma) + self.mean_1 = self.xp.array(self.mean_1) + self.mean_2 = self.xp.array(self.mean_2) self.likelihood = AnalyticalMultidimensionalBimodalCovariantGaussian( mean_1=self.mean_1, mean_2=self.mean_2, cov=self.cov ) @@ -558,7 +593,10 @@ def test_log_likelihood(self): likelihood = AnalyticalMultidimensionalBimodalCovariantGaussian( mean_1=[0], mean_2=[0], cov=[1] ) - self.assertEqual(-np.log(2 * np.pi) / 2, likelihood.log_likelihood(dict(x0=0))) + self.assertEqual( + -np.log(2 * np.pi) / 2, + likelihood.log_likelihood(dict(x0=self.xp.array(0.0))), + ) class TestJointLikelihood(unittest.TestCase): From 083e86541ef8dc5ddf4fbe8bd4e7501e8ed14aa6 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Sat, 31 Jan 2026 12:42:02 +0000 Subject: [PATCH 072/140] MAINT: actually add jax requirements --- jax_requirements.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 jax_requirements.txt diff --git a/jax_requirements.txt b/jax_requirements.txt new file mode 100644 index 000000000..b325586a3 --- /dev/null +++ b/jax_requirements.txt @@ -0,0 +1,2 @@ +interpax +jax \ No newline at end of file From 59b3da87f4eba16ed1835813884f802221652182 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Sat, 31 Jan 2026 12:55:10 +0000 Subject: [PATCH 073/140] CI: don't trivially skip all tests... --- test/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/conftest.py b/test/conftest.py index 6da49894b..6efaa82e2 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -8,7 +8,7 @@ def pytest_addoption(parser): ) parser.addoption( "--array-backend", - default="numpy", + default=None, help="Which array to use for testing", ) From 72c8afaeef5a3248e27da08286b879aafe97326c Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Sat, 31 Jan 2026 13:40:14 +0000 Subject: [PATCH 074/140] Initial pass at making grid work with jax --- bilby/core/grid.py | 76 +++++++++++++++++++++++++----------- bilby/core/utils/calculus.py | 2 +- test/core/grid_test.py | 37 ++++++++++++------ 3 files changed, 79 insertions(+), 36 deletions(-) diff --git a/bilby/core/grid.py b/bilby/core/grid.py index c574a7a3a..6f1f81912 100644 --- a/bilby/core/grid.py +++ b/bilby/core/grid.py @@ -9,6 +9,7 @@ BilbyJsonEncoder, load_json, move_old_file ) from .result import FileMovedError +from ..compat.utils import array_module def grid_file_name(outdir, label, gzip=False): @@ -35,8 +36,11 @@ def grid_file_name(outdir, label, gzip=False): class Grid(object): - def __init__(self, likelihood=None, priors=None, grid_size=101, - save=False, label='no_label', outdir='.', gzip=False): + def __init__( + self, likelihood=None, priors=None, grid_size=101, + save=False, label='no_label', outdir='.', gzip=False, + xp=None, + ): """ Parameters @@ -57,8 +61,16 @@ def __init__(self, likelihood=None, priors=None, grid_size=101, The output directory to which the grid will be saved gzip: bool Set whether to gzip the output grid file + xp: array module | None + The array module to use for calculations (e.g., :code:`numpy`, + :code:`cupy`). If :code:`None`, defaults to :code:`numpy`. + """ + if xp is None: + xp = np + logger.debug("No array module given for grid, defaulting to numpy.") + if priors is None: priors = dict() self.likelihood = likelihood @@ -67,13 +79,15 @@ def __init__(self, likelihood=None, priors=None, grid_size=101, self.parameter_names = list(self.priors.keys()) self.sample_points = dict() - self._get_sample_points(grid_size) + self._get_sample_points(grid_size, xp=xp) # evaluate the prior on the grid points if self.n_dims > 0: self._ln_prior = self.priors.ln_prob( {key: self.mesh_grid[i].flatten() for i, key in enumerate(self.parameter_names)}, axis=0).reshape( self.mesh_grid[0].shape) + else: + self._ln_prior = xp.array(0.0) self._ln_likelihood = None # evaluate the likelihood on the grid points @@ -96,12 +110,14 @@ def ln_prior(self): @property def prior(self): - return np.exp(self.ln_prior) + lnp = self.ln_prior + xp = array_module(lnp) + return xp.exp(lnp) @property def ln_likelihood(self): if self._ln_likelihood is None: - self._evaluate() + self._evaluate(xp=array_module(self._ln_prior)) return self._ln_likelihood @property @@ -115,7 +131,8 @@ def marginalize(self, log_array, parameters=None, not_parameters=None): Parameters ========== log_array: array_like - A :class:`numpy.ndarray` of log likelihood/posterior values. + A :code:`Python` array-api compatible array of log + likelihood/posterior values. parameters: list, str A list, or single string, of parameters to marginalize over. If None then all parameters will be marginalized over. @@ -165,7 +182,8 @@ def _marginalize_single(self, log_array, name, non_marg_names=None): Parameters ========== log_array: array_like - A :class:`numpy.ndarray` of log likelihood/posterior values. + A :code:`Python` array-api compatible array of log + likelihood/posterior values. name: str The name of the parameter to marginalize over. non_marg_names: list @@ -188,17 +206,19 @@ def _marginalize_single(self, log_array, name, non_marg_names=None): non_marg_names.remove(name) places = self.sample_points[name] + xp = log_array.__array_namespace__() + print(xp) if len(places) > 1: - dx = np.diff(places) - out = np.apply_along_axis( + dx = xp.diff(places) + out = xp.apply_along_axis( logtrapzexp, axis, log_array, dx ) else: # no marginalisation required, just remove the singleton dimension z = log_array.shape - q = np.arange(0, len(z)).astype(int) != axis - out = np.reshape(log_array, tuple((np.array(list(z)))[q])) + q = xp.arange(0, len(z)).astype(int) != axis + out = xp.reshape(log_array, tuple((xp.array(list(z)))[q])) return out @@ -276,8 +296,9 @@ def marginalize_likelihood(self, parameters=None, not_parameters=None): """ ln_like = self.marginalize(self.ln_likelihood, parameters=parameters, not_parameters=not_parameters) + xp = ln_like.__array_namespace__() # NOTE: the output will not be properly normalised - return np.exp(ln_like - np.max(ln_like)) + return xp.exp(ln_like - xp.max(ln_like)) def marginalize_posterior(self, parameters=None, not_parameters=None): """ @@ -300,18 +321,29 @@ def marginalize_posterior(self, parameters=None, not_parameters=None): ln_post = self.marginalize(self.ln_posterior, parameters=parameters, not_parameters=not_parameters) # NOTE: the output will not be properly normalised - return np.exp(ln_post - np.max(ln_post)) + xp = ln_post.__array_namespace__() + return xp.exp(ln_post - xp.max(ln_post)) def _evaluate(self): - self._ln_likelihood = np.empty(self.mesh_grid[0].shape) - self._evaluate_recursion(0, parameters=dict()) + xp = self.mesh_grid[0].__array_namespace__() + if xp.__name__ == "jax.numpy": + from jax import vmap + self._ln_likelihood = vmap(self.likelihood.log_likelihood)( + {key: self.mesh_grid[i].flatten() for i, key in enumerate(self.parameter_names)} + ).reshape(self.mesh_grid[0].shape) + print(type(self._ln_likelihood)) + + else: + self._ln_likelihood = xp.empty(self.mesh_grid[0].shape) + self._evaluate_recursion(0, parameters=dict()) self.ln_noise_evidence = self.likelihood.noise_log_likelihood() def _evaluate_recursion(self, dimension, parameters): if dimension == self.n_dims: - current_point = tuple([[int(np.where( + xp = self.mesh_grid[0].__array_namespace__() + current_point = tuple([[xp.where( parameters[name] == - self.sample_points[name])[0])] for name in self.parameter_names]) + self.sample_points[name])[0].item()] for name in self.parameter_names]) self._ln_likelihood[current_point] = self.likelihood.log_likelihood(parameters) else: name = self.parameter_names[dimension] @@ -319,29 +351,29 @@ def _evaluate_recursion(self, dimension, parameters): parameters[name] = self.sample_points[name][ii] self._evaluate_recursion(dimension + 1, parameters) - def _get_sample_points(self, grid_size): + def _get_sample_points(self, grid_size, *, xp=np): for ii, key in enumerate(self.parameter_names): if isinstance(self.priors[key], Prior): if isinstance(grid_size, int): self.sample_points[key] = self.priors[key].rescale( - np.linspace(0, 1, grid_size)) + xp.linspace(0, 1, grid_size)) elif isinstance(grid_size, list): if isinstance(grid_size[ii], int): self.sample_points[key] = self.priors[key].rescale( - np.linspace(0, 1, grid_size[ii])) + xp.linspace(0, 1, grid_size[ii])) else: self.sample_points[key] = grid_size[ii] elif isinstance(grid_size, dict): if isinstance(grid_size[key], int): self.sample_points[key] = self.priors[key].rescale( - np.linspace(0, 1, grid_size[key])) + xp.linspace(0, 1, grid_size[key])) else: self.sample_points[key] = grid_size[key] else: raise TypeError("Unrecognized 'grid_size' type") # set the mesh of points - self.mesh_grid = np.meshgrid( + self.mesh_grid = xp.meshgrid( *(self.sample_points[key] for key in self.parameter_names), indexing='ij') diff --git a/bilby/core/utils/calculus.py b/bilby/core/utils/calculus.py index 13e1b2586..2cc2b6ae1 100644 --- a/bilby/core/utils/calculus.py +++ b/bilby/core/utils/calculus.py @@ -194,7 +194,7 @@ def logtrapzexp(lnf, dx, *, xp=np): else: raise TypeError("Step size must be a single value or array-like") - return C + logsumexp([logsumexp(lnfdx1), logsumexp(lnfdx2)]) + return C + logsumexp(xp.array([logsumexp(lnfdx1), logsumexp(lnfdx2)])) class interp1d(_interp1d): diff --git a/test/core/grid_test.py b/test/core/grid_test.py index f14a95134..e0c9a455e 100644 --- a/test/core/grid_test.py +++ b/test/core/grid_test.py @@ -1,30 +1,35 @@ import unittest -import numpy as np import shutil import os -from scipy.stats import multivariate_normal + +import numpy as np +import pytest import bilby +from bilby.compat.patches import multivariate_logpdf -# set 2D multivariate Gaussian likelihood class MultiGaussian(bilby.Likelihood): - def __init__(self, mean, cov): + # set 2D multivariate Gaussian likelihood + def __init__(self, mean, cov, *, xp=np): super(MultiGaussian, self).__init__() - self.cov = np.array(cov) - self.mean = np.array(mean) - self.sigma = np.sqrt(np.diag(self.cov)) - self.pdf = multivariate_normal(mean=self.mean, cov=self.cov) + self.xp = xp + self.cov = xp.array(cov) + self.mean = xp.array(mean) + self.sigma = xp.sqrt(xp.diag(self.cov)) + self.logpdf = multivariate_logpdf(xp=xp, mean=self.mean, cov=self.cov) @property def dim(self): return len(self.cov[0]) def log_likelihood(self, parameters): - x = np.array([parameters["x{0}".format(i)] for i in range(self.dim)]) - return self.pdf.logpdf(x) + x = self.xp.array([parameters["x{0}".format(i)] for i in range(self.dim)]) + return self.logpdf(x) +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestGrid(unittest.TestCase): def setUp(self): bilby.core.utils.random.seed(7) @@ -33,7 +38,7 @@ def setUp(self): self.mus = [0.0, 0.0] self.cov = [[1.0, 0.0], [0.0, 1.0]] dim = len(self.mus) - self.likelihood = MultiGaussian(self.mus, self.cov) + self.likelihood = MultiGaussian(self.mus, self.cov, xp=self.xp) # set priors out to +/- 5 sigma self.priors = bilby.core.prior.PriorDict() @@ -61,6 +66,7 @@ def setUp(self): grid_size=self.grid_size, likelihood=self.likelihood, save=True, + xp=self.xp, ) self.grid = grid @@ -151,6 +157,7 @@ def test_fail_grid_size(self): grid_size=2.3, likelihood=self.likelihood, save=True, + xp=self.xp, ) def test_mesh_grid(self): @@ -165,7 +172,8 @@ def test_grid_integer_points(self): outdir="outdir", priors=self.priors, grid_size=n_points, - likelihood=self.likelihood + likelihood=self.likelihood, + xp=self.xp, ) self.assertTupleEqual(tuple(n_points), grid.mesh_grid[0].shape) @@ -179,7 +187,8 @@ def test_grid_dict_points(self): outdir="outdir", priors=self.priors, grid_size=n_points, - likelihood=self.likelihood + likelihood=self.likelihood, + xp=self.xp, ) self.assertTupleEqual((n_points["x0"], n_points["x1"]), grid.mesh_grid[0].shape) self.assertEqual(grid.mesh_grid[0][0, 0], self.priors[self.grid.parameter_names[0]].minimum) @@ -196,6 +205,8 @@ def test_grid_from_array(self): priors=self.priors, grid_size=n_points, likelihood=self.likelihood, + xp=self.xp, + vectorized=True, ) self.assertTupleEqual((len(x0s), len(x1s)), grid.mesh_grid[0].shape) From 3538000bf9654647caacbf9e7d476221e7413000 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Sun, 1 Feb 2026 11:05:55 -0500 Subject: [PATCH 075/140] TEST: add more jax test coverage --- bilby/compat/patches.py | 14 +++ bilby/compat/utils.py | 35 ++++++- bilby/core/grid.py | 1 - bilby/core/prior/analytical.py | 143 ++++++++++++++-------------- bilby/core/prior/base.py | 60 ++++++++---- bilby/core/prior/conditional.py | 31 ++++-- bilby/core/prior/dict.py | 71 ++++++++------ bilby/core/prior/interpolated.py | 8 +- bilby/core/prior/joint.py | 97 +++++++++---------- bilby/core/prior/slabspike.py | 22 +++-- bilby/core/utils/calculus.py | 2 +- bilby/gw/prior.py | 66 +++++++------ test/conftest.py | 3 + test/core/grid_test.py | 1 - test/core/prior/analytical_test.py | 132 ++++++++++++++++--------- test/core/prior/base_test.py | 26 ++++- test/core/prior/conditional_test.py | 24 ++++- test/core/prior/dict_test.py | 50 ++++++---- test/core/prior/prior_test.py | 120 ++++++++++++++--------- test/core/prior/slabspike_test.py | 64 +++++++------ test/core/result_test.py | 14 ++- test/gw/prior_test.py | 17 +++- 22 files changed, 623 insertions(+), 378 deletions(-) diff --git a/bilby/compat/patches.py b/bilby/compat/patches.py index 02cbc3394..7c497a24e 100644 --- a/bilby/compat/patches.py +++ b/bilby/compat/patches.py @@ -1,4 +1,5 @@ import array_api_compat as aac +import numpy as np from .utils import BackendNotImplementedError @@ -35,3 +36,16 @@ def multivariate_logpdf(xp, mean, cov): else: raise BackendNotImplementedError return logpdf + + +def logsumexp(a, axis=None, b=None, keepdims=False, return_sign=False, *, xp=None): + if xp is None: + xp = a.__array_namespace__() + + if "jax" in xp.__name__: + # the scipy version of logsumexp cannot be vmapped + from jax.scipy.special import logsumexp as lse + else: + from scipy.special import logsumexp as lse + + return lse(a=a, axis=axis, b=b, keepdims=keepdims, return_sign=return_sign) diff --git a/bilby/compat/utils.py b/bilby/compat/utils.py index 4b099969b..5e00f8969 100644 --- a/bilby/compat/utils.py +++ b/bilby/compat/utils.py @@ -43,14 +43,39 @@ def promote_to_array(args, backend, skip=None): return args -def xp_wrap(func): +def xp_wrap(func, no_xp=False): + """ + A decorator that will figure out the array module from the input + arguments and pass it to the function as the 'xp' keyword argument. - def wrapped(self, *args, **kwargs): - if "xp" not in kwargs: + Parameters + ========== + func: function + The function to be decorated. + no_xp: bool + If True, the decorator will not attempt to add the 'xp' keyword + argument and so the wrapper is a no-op. + + Returns + ======= + function + The decorated function. + """ + + def wrapped(self, *args, xp=None, **kwargs): + if not no_xp and xp is None: try: - kwargs["xp"] = array_module(*args) + if len(args) > 0: + array_module = array_namespace(*args) + elif len(kwargs) > 0: + array_module = array_namespace(*kwargs.values()) + else: + array_module = np + kwargs["xp"] = array_module except TypeError: - pass + kwargs["xp"] = np + elif not no_xp: + kwargs["xp"] = xp return func(self, *args, **kwargs) return wrapped diff --git a/bilby/core/grid.py b/bilby/core/grid.py index 6f1f81912..3e5ef1fc3 100644 --- a/bilby/core/grid.py +++ b/bilby/core/grid.py @@ -207,7 +207,6 @@ def _marginalize_single(self, log_array, name, non_marg_names=None): places = self.sample_points[name] xp = log_array.__array_namespace__() - print(xp) if len(places) > 1: dx = xp.diff(places) diff --git a/bilby/core/prior/analytical.py b/bilby/core/prior/analytical.py index ca48e3393..118e36ce8 100644 --- a/bilby/core/prior/analytical.py +++ b/bilby/core/prior/analytical.py @@ -45,7 +45,7 @@ def __init__(self, peak, name=None, latex_label=None, unit=None): self._is_fixed = True self.least_recently_sampled = peak - def rescale(self, val): + def rescale(self, val, *, xp=None): """Rescale everything to the peak with the correct shape. Parameters @@ -58,7 +58,7 @@ def rescale(self, val): """ return self.peak * val ** 0 - def prob(self, val): + def prob(self, val, *, xp=None): """Return the prior probability of val Parameters @@ -73,7 +73,7 @@ def prob(self, val): at_peak = (val == self.peak) return at_peak * 1.0 - def cdf(self, val): + def cdf(self, val, *, xp=None): return 1.0 * (val > self.peak) @@ -106,7 +106,7 @@ def __init__(self, alpha, minimum, maximum, name=None, latex_label=None, self.alpha = alpha @xp_wrap - def rescale(self, val, *, xp=np): + def rescale(self, val, *, xp=None): """ 'Rescale' a sample from the unit line element to the power-law prior. @@ -128,7 +128,7 @@ def rescale(self, val, *, xp=np): (self.maximum ** (1 + self.alpha) - self.minimum ** (1 + self.alpha))) ** (1. / (1 + self.alpha)) @xp_wrap - def prob(self, val, *, xp=np): + def prob(self, val, *, xp=None): """Return the prior probability of val Parameters @@ -147,7 +147,7 @@ def prob(self, val, *, xp=np): self.minimum ** (1 + self.alpha))) * self.is_in_prior_range(val) @xp_wrap - def ln_prob(self, val, *, xp=np): + def ln_prob(self, val, *, xp=None): """Return the logarithmic prior probability of val Parameters @@ -172,7 +172,7 @@ def ln_prob(self, val, *, xp=np): return ln_p + ln_in_range @xp_wrap - def cdf(self, val, *, xp=np): + def cdf(self, val, *, xp=None): if self.alpha == -1: with np.errstate(invalid="ignore"): _cdf = xp.log(val / self.minimum) / xp.log(self.maximum / self.minimum) @@ -210,7 +210,7 @@ def __init__(self, minimum, maximum, name=None, latex_label=None, minimum=minimum, maximum=maximum, unit=unit, boundary=boundary) - def rescale(self, val): + def rescale(self, val, *, xp=None): """ 'Rescale' a sample from the unit line element to the power-law prior. @@ -227,7 +227,7 @@ def rescale(self, val): """ return self.minimum + val * (self.maximum - self.minimum) - def prob(self, val): + def prob(self, val, *, xp=None): """Return the prior probability of val Parameters @@ -241,7 +241,7 @@ def prob(self, val): return ((val >= self.minimum) & (val <= self.maximum)) / (self.maximum - self.minimum) @xp_wrap - def ln_prob(self, val, *, xp=np): + def ln_prob(self, val, *, xp=None): """Return the log prior probability of val Parameters @@ -256,7 +256,7 @@ def ln_prob(self, val, *, xp=np): return xp.log(self.prob(val)) @xp_wrap - def cdf(self, val, *, xp=np): + def cdf(self, val, *, xp=None): _cdf = (val - self.minimum) / (self.maximum - self.minimum) return xp.clip(_cdf, 0, 1) @@ -319,7 +319,7 @@ def __init__(self, minimum, maximum, name=None, latex_label=None, boundary=boundary) @xp_wrap - def rescale(self, val, *, xp=np): + def rescale(self, val, *, xp=None): """ 'Rescale' a sample from the unit line element to the power-law prior. @@ -337,7 +337,7 @@ def rescale(self, val, *, xp=np): return xp.sign(2 * val - 1) * self.minimum * xp.exp(abs(2 * val - 1) * xp.log(self.maximum / self.minimum)) @xp_wrap - def prob(self, val, *, xp=np): + def prob(self, val, *, xp=None): """Return the prior probability of val Parameters @@ -353,7 +353,7 @@ def prob(self, val, *, xp=np): self.is_in_prior_range(val)) @xp_wrap - def ln_prob(self, val, *, xp=np): + def ln_prob(self, val, *, xp=None): """Return the logarithmic prior probability of val Parameters @@ -368,7 +368,7 @@ def ln_prob(self, val, *, xp=np): return xp.nan_to_num(- xp.log(2 * xp.abs(val)) - xp.log(xp.log(self.maximum / self.minimum))) @xp_wrap - def cdf(self, val, *, xp=np): + def cdf(self, val, *, xp=None): asymmetric = xp.log(abs(val) / self.minimum) / xp.log(self.maximum / self.minimum) return xp.clip(0.5 * (1 + xp.sign(val) * asymmetric), 0, 1) @@ -398,7 +398,7 @@ def __init__(self, minimum=-np.pi / 2, maximum=np.pi / 2, name=None, latex_label=latex_label, unit=unit, boundary=boundary) @xp_wrap - def rescale(self, val, *, xp=np): + def rescale(self, val, *, xp=None): """ 'Rescale' a sample from the unit line element to a uniform in cosine prior. @@ -408,7 +408,7 @@ def rescale(self, val, *, xp=np): return xp.arcsin(val / norm + xp.sin(self.minimum)) @xp_wrap - def prob(self, val, *, xp=np): + def prob(self, val, *, xp=None): """Return the prior probability of val. Defined over [-pi/2, pi/2]. Parameters @@ -422,7 +422,7 @@ def prob(self, val, *, xp=np): return xp.cos(val) / 2 * self.is_in_prior_range(val) @xp_wrap - def cdf(self, val, *, xp=np): + def cdf(self, val, *, xp=None): _cdf = ( (xp.sin(val) - xp.sin(self.minimum)) / (xp.sin(self.maximum) - xp.sin(self.minimum)) @@ -458,7 +458,7 @@ def __init__(self, minimum=0, maximum=np.pi, name=None, latex_label=latex_label, unit=unit, boundary=boundary) @xp_wrap - def rescale(self, val, *, xp=np): + def rescale(self, val, *, xp=None): """ 'Rescale' a sample from the unit line element to a uniform in sine prior. @@ -468,7 +468,7 @@ def rescale(self, val, *, xp=np): return xp.arccos(xp.cos(self.minimum) - val / norm) @xp_wrap - def prob(self, val, *, xp=np): + def prob(self, val, *, xp=None): """Return the prior probability of val. Defined over [0, pi]. Parameters @@ -482,7 +482,7 @@ def prob(self, val, *, xp=np): return xp.sin(val) / 2 * self.is_in_prior_range(val) @xp_wrap - def cdf(self, val, *, xp=np): + def cdf(self, val, *, xp=None): _cdf = ( (xp.cos(val) - xp.cos(self.minimum)) / (xp.cos(self.maximum) - xp.cos(self.minimum)) @@ -518,7 +518,7 @@ def __init__(self, mu, sigma, name=None, latex_label=None, unit=None, boundary=N self.sigma = sigma @xp_wrap - def rescale(self, val, *, xp=np): + def rescale(self, val, *, xp=None): """ 'Rescale' a sample from the unit line element to the appropriate Gaussian prior. @@ -535,7 +535,7 @@ def rescale(self, val, *, xp=np): return self.mu + erfinv(2 * val - 1) * 2 ** 0.5 * self.sigma @xp_wrap - def prob(self, val, *, xp=np): + def prob(self, val, *, xp=None): """Return the prior probability of val. Parameters @@ -549,7 +549,7 @@ def prob(self, val, *, xp=np): return xp.exp(-(self.mu - val) ** 2 / (2 * self.sigma ** 2)) / (2 * np.pi) ** 0.5 / self.sigma @xp_wrap - def ln_prob(self, val, *, xp=np): + def ln_prob(self, val, *, xp=None): """Return the Log prior probability of val. Parameters @@ -562,7 +562,7 @@ def ln_prob(self, val, *, xp=np): """ return -0.5 * ((self.mu - val) ** 2 / self.sigma ** 2 + xp.log(2 * np.pi * self.sigma ** 2)) - def cdf(self, val): + def cdf(self, val, *, xp=None): return (1 - erf((self.mu - val) / 2 ** 0.5 / self.sigma)) / 2 @@ -614,7 +614,7 @@ def normalisation(self): (self.minimum - self.mu) / 2 ** 0.5 / self.sigma)) / 2 @xp_wrap - def rescale(self, val, *, xp=np): + def rescale(self, val, *, xp=None): """ 'Rescale' a sample from the unit line element to the appropriate truncated Gaussian prior. @@ -630,7 +630,7 @@ def rescale(self, val, *, xp=np): (self.minimum - self.mu) / 2 ** 0.5 / self.sigma)) * 2 ** 0.5 * self.sigma + self.mu @xp_wrap - def prob(self, val, *, xp=np): + def prob(self, val, *, xp=None): """Return the prior probability of val. Parameters @@ -644,7 +644,7 @@ def prob(self, val, *, xp=np): return xp.exp(-(self.mu - val) ** 2 / (2 * self.sigma ** 2)) / (2 * np.pi) ** 0.5 \ / self.sigma / self.normalisation * self.is_in_prior_range(val) - def cdf(self, val): + def cdf(self, val, *, xp=None): _cdf = (erf((val - self.mu) / 2 ** 0.5 / self.sigma) - erf( (self.minimum - self.mu) / 2 ** 0.5 / self.sigma)) / 2 / self.normalisation _cdf *= val >= self.minimum @@ -714,7 +714,7 @@ def __init__(self, mu, sigma, name=None, latex_label=None, unit=None, boundary=N self.sigma = sigma @xp_wrap - def rescale(self, val, *, xp=np): + def rescale(self, val, *, xp=None): """ 'Rescale' a sample from the unit line element to the appropriate LogNormal prior. @@ -727,7 +727,7 @@ def rescale(self, val, *, xp=np): return xp.exp(self.mu + (2 * self.sigma ** 2)**0.5 * erfinv(2 * val - 1)) @xp_wrap - def prob(self, val, *, xp=np): + def prob(self, val, *, xp=None): """Returns the prior probability of val. Parameters @@ -741,7 +741,7 @@ def prob(self, val, *, xp=np): return xp.exp(self.ln_prob(val, xp=xp)) @xp_wrap - def ln_prob(self, val, *, xp=np): + def ln_prob(self, val, *, xp=None): """Returns the log prior probability of val. Parameters @@ -759,7 +759,7 @@ def ln_prob(self, val, *, xp=np): ) + xp.log(val > self.minimum), nan=-xp.inf, neginf=-xp.inf, posinf=-xp.inf) @xp_wrap - def cdf(self, val, *, xp=np): + def cdf(self, val, *, xp=None): with np.errstate(divide="ignore"): return 0.5 + erf( (xp.log(xp.maximum(val, self.minimum)) - self.mu) / self.sigma / np.sqrt(2) @@ -792,7 +792,7 @@ def __init__(self, mu, name=None, latex_label=None, unit=None, boundary=None): self.mu = mu @xp_wrap - def rescale(self, val, *, xp=np): + def rescale(self, val, *, xp=None): """ 'Rescale' a sample from the unit line element to the appropriate Exponential prior. @@ -802,7 +802,7 @@ def rescale(self, val, *, xp=np): return -self.mu * xp.log1p(-val) @xp_wrap - def prob(self, val, *, xp=np): + def prob(self, val, *, xp=None): """Return the prior probability of val. Parameters @@ -816,7 +816,7 @@ def prob(self, val, *, xp=np): return xp.exp(self.ln_prob(val, xp=xp)) @xp_wrap - def ln_prob(self, val, *, xp=np): + def ln_prob(self, val, *, xp=None): """Returns the log prior probability of val. Parameters @@ -831,7 +831,7 @@ def ln_prob(self, val, *, xp=np): return -val / self.mu - xp.log(self.mu) + xp.log(val >= self.minimum) @xp_wrap - def cdf(self, val, *, xp=np): + def cdf(self, val, *, xp=None): with np.errstate(divide="ignore", invalid="ignore", over="ignore"): return xp.maximum(1. - xp.exp(-val / self.mu), 0) @@ -871,7 +871,7 @@ def __init__(self, df, mu=0., scale=1., name=None, latex_label=None, self.scale = scale @xp_wrap - def rescale(self, val, *, xp=np): + def rescale(self, val, *, xp=None): """ 'Rescale' a sample from the unit line element to the appropriate Student's t-prior. @@ -889,7 +889,7 @@ def rescale(self, val, *, xp=np): ) @xp_wrap - def prob(self, val, *, xp=np): + def prob(self, val, *, xp=None): """Return the prior probability of val. Parameters @@ -903,7 +903,7 @@ def prob(self, val, *, xp=np): return xp.exp(self.ln_prob(val)) @xp_wrap - def ln_prob(self, val, *, xp=np): + def ln_prob(self, val, *, xp=None): """Returns the log prior probability of val. Parameters @@ -920,7 +920,7 @@ def ln_prob(self, val, *, xp=np): * xp.log(1 + ((val - self.mu) / self.scale) ** 2 / self.df) ) - def cdf(self, val): + def cdf(self, val, *, xp=None): return stdtr(self.df, (val - self.mu) / self.scale) @@ -963,7 +963,7 @@ def __init__(self, alpha, beta, minimum=0, maximum=1, name=None, self.beta = beta @xp_wrap - def rescale(self, val, *, xp=np): + def rescale(self, val, *, xp=None): """ 'Rescale' a sample from the unit line element to the appropriate Beta prior. @@ -979,7 +979,7 @@ def rescale(self, val, *, xp=np): ) @xp_wrap - def prob(self, val, *, xp=np): + def prob(self, val, *, xp=None): """Return the prior probability of val. Parameters @@ -993,7 +993,7 @@ def prob(self, val, *, xp=np): return xp.exp(self.ln_prob(val, xp=xp)) @xp_wrap - def ln_prob(self, val, *, xp=np): + def ln_prob(self, val, *, xp=None): """Returns the log prior probability of val. Parameters @@ -1009,7 +1009,7 @@ def ln_prob(self, val, *, xp=np): return xp.nan_to_num(ln_prob, nan=-xp.inf, neginf=-xp.inf, posinf=-xp.inf) @xp_wrap - def cdf(self, val, *, xp=np): + def cdf(self, val, *, xp=None): return xp.nan_to_num( betainc(self.alpha, self.beta, (val - self.minimum) / (self.maximum - self.minimum)) ) + (val > self.maximum) @@ -1045,7 +1045,7 @@ def __init__(self, mu, scale, name=None, latex_label=None, unit=None, boundary=N self.scale = scale @xp_wrap - def rescale(self, val, *, xp=np): + def rescale(self, val, *, xp=None): """ 'Rescale' a sample from the unit line element to the appropriate Logistic prior. @@ -1056,7 +1056,7 @@ def rescale(self, val, *, xp=np): return self.mu + self.scale * xp.log(xp.maximum(val / (1 - val), 0)) @xp_wrap - def prob(self, val, *, xp=np): + def prob(self, val, *, xp=None): """Return the prior probability of val. Parameters @@ -1070,7 +1070,7 @@ def prob(self, val, *, xp=np): return xp.exp(self.ln_prob(val)) @xp_wrap - def ln_prob(self, val, *, xp=np): + def ln_prob(self, val, *, xp=None): """Returns the log prior probability of val. Parameters @@ -1086,7 +1086,7 @@ def ln_prob(self, val, *, xp=np): 2. * xp.log1p(xp.exp(-(val - self.mu) / self.scale)) - xp.log(self.scale) @xp_wrap - def cdf(self, val, *, xp=np): + def cdf(self, val, *, xp=None): return 1. / (1. + xp.exp(-(val - self.mu) / self.scale)) @@ -1120,7 +1120,7 @@ def __init__(self, alpha, beta, name=None, latex_label=None, unit=None, boundary self.beta = beta @xp_wrap - def rescale(self, val, *, xp=np): + def rescale(self, val, *, xp=None): """ 'Rescale' a sample from the unit line element to the appropriate Cauchy prior. @@ -1130,7 +1130,7 @@ def rescale(self, val, *, xp=np): with np.errstate(divide="ignore", invalid="ignore"): return rescaled - xp.log(val < 1) + xp.log(val > 0) - def prob(self, val): + def prob(self, val, *, xp=None): """Return the prior probability of val. Parameters @@ -1144,7 +1144,7 @@ def prob(self, val): return 1. / self.beta / np.pi / (1. + ((val - self.alpha) / self.beta) ** 2) @xp_wrap - def ln_prob(self, val, *, xp=np): + def ln_prob(self, val, *, xp=None): """Return the log prior probability of val. Parameters @@ -1158,7 +1158,7 @@ def ln_prob(self, val, *, xp=np): return - xp.log(self.beta * np.pi) - xp.log(1. + ((val - self.alpha) / self.beta) ** 2) @xp_wrap - def cdf(self, val, *, xp=np): + def cdf(self, val, *, xp=None): return 0.5 + xp.arctan((val - self.alpha) / self.beta) / np.pi @@ -1197,7 +1197,7 @@ def __init__(self, k, theta=1., name=None, latex_label=None, unit=None, boundary self.theta = theta @xp_wrap - def rescale(self, val, *, xp=np): + def rescale(self, val, *, xp=None): """ 'Rescale' a sample from the unit line element to the appropriate Gamma prior. @@ -1206,7 +1206,7 @@ def rescale(self, val, *, xp=np): return xp.asarray(gammaincinv(self.k, val)) * self.theta @xp_wrap - def prob(self, val, *, xp=np): + def prob(self, val, *, xp=None): """Return the prior probability of val. Parameters @@ -1220,7 +1220,7 @@ def prob(self, val, *, xp=np): return xp.exp(self.ln_prob(val)) @xp_wrap - def ln_prob(self, val, *, xp=np): + def ln_prob(self, val, *, xp=None): """Returns the log prior probability of val. Parameters @@ -1239,7 +1239,7 @@ def ln_prob(self, val, *, xp=np): return xp.nan_to_num(ln_prob, nan=-xp.inf, neginf=-xp.inf, posinf=xp.inf) @xp_wrap - def cdf(self, val, *, xp=np): + def cdf(self, val, *, xp=None): return gammainc(xp.asarray(self.k), xp.maximum(val, self.minimum) / self.theta) @@ -1331,7 +1331,7 @@ def __init__(self, sigma, mu=None, r=None, name=None, latex_label=None, self.expr = xp.exp(self.r) @xp_wrap - def rescale(self, val, *, xp=np): + def rescale(self, val, *, xp=None): """ 'Rescale' a sample from the unit line element to the appropriate Fermi-Dirac prior. @@ -1352,7 +1352,7 @@ def rescale(self, val, *, xp=np): return -self.sigma * xp.log(xp.maximum(inv, 0)) @xp_wrap - def prob(self, val, *, xp=np): + def prob(self, val, *, xp=None): """Return the prior probability of val. Parameters @@ -1370,7 +1370,7 @@ def prob(self, val, *, xp=np): ) @xp_wrap - def ln_prob(self, val, *, xp=np): + def ln_prob(self, val, *, xp=None): """Return the log prior probability of val. Parameters @@ -1384,7 +1384,7 @@ def ln_prob(self, val, *, xp=np): return xp.log(self.prob(val)) @xp_wrap - def cdf(self, val, *, xp=np): + def cdf(self, val, *, xp=None): """ Evaluate the CDF of the Fermi-Dirac distribution using a slightly modified form of Equation 23 of [1]_. @@ -1482,7 +1482,7 @@ def __init__( self._cumulative_weights_array = xp.insert(_cumulative_weights_array, 0, 0) @xp_wrap - def rescale(self, val, *, xp=np): + def rescale(self, val, *, xp=None): """ 'Rescale' a sample from the unit line element to the discrete-value prior. @@ -1501,7 +1501,7 @@ def rescale(self, val, *, xp=np): return xp.asarray(self._values_array)[index] @xp_wrap - def cdf(self, val, *, xp=np): + def cdf(self, val, *, xp=None): """Return the cumulative prior probability of val. Parameters @@ -1516,7 +1516,7 @@ def cdf(self, val, *, xp=np): return xp.asarray(self._cumulative_weights_array)[index] @xp_wrap - def prob(self, val, *, xp=np): + def prob(self, val, *, xp=None): """Return the prior probability of val. Parameters @@ -1533,7 +1533,8 @@ def prob(self, val, *, xp=np): # turn 0d numpy array to scalar return p[()] - def ln_prob(self, val): + @xp_wrap + def ln_prob(self, val, xp=None): """Return the logarithmic prior probability of val Parameters @@ -1545,12 +1546,12 @@ def ln_prob(self, val): float: """ - index = np.searchsorted(self._values_array, val) - index = np.clip(index, 0, self.nvalues - 1) - lnp = np.where( + index = xp.searchsorted(self._values_array, val) + index = xp.clip(index, 0, self.nvalues - 1) + lnp = xp.where( self._values_array[index] == val, self._lnweights_array[index], -np.inf ) - # turn 0d numpy array to scalar + # turn 0d array to scalar return lnp[()] @@ -1674,7 +1675,7 @@ def __init__(self, mode, minimum, maximum, name=None, latex_label=None, unit=Non self.rescaled_minimum = self.minimum - (self.minimum == self.mode) * self.scale self.rescaled_maximum = self.maximum + (self.maximum == self.mode) * self.scale - def rescale(self, val): + def rescale(self, val, *, xp=None): """ 'Rescale' a sample from standard uniform to a triangular distribution. @@ -1696,7 +1697,7 @@ def rescale(self, val): self.maximum - above_mode ) * (val >= self.fractional_mode) - def prob(self, val): + def prob(self, val, *, xp=None): """ Return the prior probability of val @@ -1723,7 +1724,7 @@ def prob(self, val): ) return 2.0 * (between_minimum_and_mode + between_mode_and_maximum) / self.scale - def cdf(self, val): + def cdf(self, val, *, xp=None): """ Return the prior cumulative probability at val diff --git a/bilby/core/prior/base.py b/bilby/core/prior/base.py index 5ca28de28..ea6ef5475 100644 --- a/bilby/core/prior/base.py +++ b/bilby/core/prior/base.py @@ -2,7 +2,9 @@ import json import os import re +import warnings +import array_api_compat as aac import numpy as np import scipy.stats @@ -14,6 +16,7 @@ get_dict_with_properties, WrappedInterp1d as interp1d, ) +from ...compat.utils import xp_wrap class Prior(object): @@ -57,6 +60,27 @@ def __init__(self, name=None, latex_label=None, unit=None, minimum=-np.inf, self.boundary = boundary self._is_fixed = False + def __init_subclass__(cls): + for method_name in ["prob", "ln_prob", "rescale", "cdf", "sample"]: + method = getattr(cls, method_name, None) + if method is not None: + from inspect import signature + + sig = signature(method) + if "xp" not in sig.parameters: + warnings.warn( + f"The method {method_name} of the prior class " + f"{cls.__name__} does not accept an 'xp' keyword " + "argument. This may cause some behaviour to fail. " + "Please see the bilby documentation for more " + "information: https://bilby-dev.github.io/bilby/" + "array_api.html" + f" {sig}", + DeprecationWarning, + stacklevel=2, + ) + setattr(cls, method_name, xp_wrap(method, no_xp=True)) + def __call__(self): """Overrides the __call__ special method. Calls the sample method. @@ -106,7 +130,7 @@ def __eq__(self, other): for key in this_dict: if key == "least_recently_sampled": continue - if isinstance(this_dict[key], np.ndarray): + if aac.is_array_api_obj(this_dict[key]): if not np.array_equal(this_dict[key], other_dict[key]): return False elif isinstance(this_dict[key], type(scipy.stats.beta(1., 1.))): @@ -116,7 +140,7 @@ def __eq__(self, other): return False return True - def sample(self, size=None): + def sample(self, size=None, *, xp=np): """Draw a sample from the prior Parameters @@ -131,10 +155,12 @@ def sample(self, size=None): """ from ..utils import random - self.least_recently_sampled = self.rescale(random.rng.uniform(0, 1, size)) + self.least_recently_sampled = self.rescale( + xp.array(random.rng.uniform(0, 1, size)) + ) return self.least_recently_sampled - def rescale(self, val): + def rescale(self, val, *, xp=None): """ 'Rescale' a sample from the unit line element to the prior. @@ -152,7 +178,7 @@ def rescale(self, val): """ return None - def prob(self, val): + def prob(self, val, *, xp=None): """Return the prior probability of val, this should be overwritten Parameters @@ -166,24 +192,22 @@ def prob(self, val): """ return np.nan - def cdf(self, val): + @xp_wrap + def cdf(self, val, *, xp=None): """ Generic method to calculate CDF, can be overwritten in subclass """ from scipy.integrate import cumulative_trapezoid if np.any(np.isinf([self.minimum, self.maximum])): raise ValueError( "Unable to use the generic CDF calculation for priors with" "infinite support") - x = np.linspace(self.minimum, self.maximum, 1000) - pdf = self.prob(x) + x = xp.linspace(self.minimum, self.maximum, 1000) + pdf = self.prob(x, xp=xp) cdf = cumulative_trapezoid(pdf, x, initial=0) - interp = interp1d(x, cdf, assume_sorted=True, bounds_error=False, - fill_value=(0, 1)) - output = interp(val) - if isinstance(val, (int, float)): - output = float(output) - return output - - def ln_prob(self, val): + output = xp.interp(val, x, cdf / cdf[-1], left=0, right=1) + return output[()] + + @xp_wrap + def ln_prob(self, val, *, xp=None): """Return the prior ln probability of val, this should be overwritten Parameters @@ -196,7 +220,7 @@ def ln_prob(self, val): """ with np.errstate(divide='ignore'): - return np.log(self.prob(val)) + return xp.log(self.prob(val, xp=xp)) def is_in_prior_range(self, val): """Returns True if val is in the prior boundaries, zero otherwise @@ -473,7 +497,7 @@ def __init__(self, minimum, maximum, name=None, latex_label=None, latex_label=latex_label, unit=unit) self._is_fixed = True - def prob(self, val): + def prob(self, val, *, xp=None): return (val > self.minimum) & (val < self.maximum) diff --git a/bilby/core/prior/conditional.py b/bilby/core/prior/conditional.py index d0c7191a4..c221939b8 100644 --- a/bilby/core/prior/conditional.py +++ b/bilby/core/prior/conditional.py @@ -1,9 +1,12 @@ +import numpy as np + from .base import Prior, PriorException from .interpolated import Interped from .analytical import DeltaFunction, PowerLaw, Uniform, LogUniform, \ SymmetricLogUniform, Cosine, Sine, Gaussian, TruncatedGaussian, HalfGaussian, \ LogNormal, Exponential, StudentT, Beta, Logistic, Cauchy, Gamma, ChiSquared, FermiDirac from ..utils import infer_args_from_method, infer_parameters_from_function +from ...compat.utils import xp_wrap def conditional_prior_factory(prior_class): @@ -59,7 +62,7 @@ def condition_func(reference_params, y): self.__class__.__name__ = 'Conditional{}'.format(prior_class.__name__) self.__class__.__qualname__ = 'Conditional{}'.format(prior_class.__qualname__) - def sample(self, size=None, **required_variables): + def sample(self, size=None, *, xp=np, **required_variables): """Draw a sample from the prior Parameters @@ -76,10 +79,15 @@ def sample(self, size=None, **required_variables): """ from ..utils import random - self.least_recently_sampled = self.rescale(random.rng.uniform(0, 1, size), **required_variables) + self.least_recently_sampled = self.rescale( + xp.array(random.rng.uniform(0, 1, size)), + xp=xp, + **required_variables, + ) return self.least_recently_sampled - def rescale(self, val, **required_variables): + @xp_wrap + def rescale(self, val, *, xp=None, **required_variables): """ 'Rescale' a sample from the unit line element to the prior. @@ -93,9 +101,10 @@ def rescale(self, val, **required_variables): """ self.update_conditions(**required_variables) - return super(ConditionalPrior, self).rescale(val) + return super(ConditionalPrior, self).rescale(val, xp=xp) - def prob(self, val, **required_variables): + @xp_wrap + def prob(self, val, *, xp=None, **required_variables): """Return the prior probability of val. Parameters @@ -111,9 +120,10 @@ def prob(self, val, **required_variables): float: Prior probability of val """ self.update_conditions(**required_variables) - return super(ConditionalPrior, self).prob(val) + return super(ConditionalPrior, self).prob(val, xp=xp) - def ln_prob(self, val, **required_variables): + @xp_wrap + def ln_prob(self, val, *, xp=None, **required_variables): """Return the natural log prior probability of val. Parameters @@ -129,9 +139,10 @@ def ln_prob(self, val, **required_variables): float: Natural log prior probability of val """ self.update_conditions(**required_variables) - return super(ConditionalPrior, self).ln_prob(val) + return super(ConditionalPrior, self).ln_prob(val, xp=xp) - def cdf(self, val, **required_variables): + @xp_wrap + def cdf(self, val, *, xp=None, **required_variables): """Return the cdf of val. Parameters @@ -147,7 +158,7 @@ def cdf(self, val, **required_variables): float: CDF of val """ self.update_conditions(**required_variables) - return super(ConditionalPrior, self).cdf(val) + return super(ConditionalPrior, self).cdf(val, xp=xp) def update_conditions(self, **required_variables): """ diff --git a/bilby/core/prior/dict.py b/bilby/core/prior/dict.py index eb02ae841..579a2050a 100644 --- a/bilby/core/prior/dict.py +++ b/bilby/core/prior/dict.py @@ -16,7 +16,7 @@ BilbyJsonEncoder, decode_bilby_json, ) -from ...compat.utils import array_module +from ...compat.utils import array_module, xp_wrap class PriorDict(dict): @@ -353,7 +353,7 @@ def fill_priors(self, likelihood=None, default_priors_file=None): for key in self: self.test_redundancy(key) - def sample(self, size=None): + def sample(self, size=None, *, xp=np): """Draw samples from the prior set Parameters @@ -365,9 +365,9 @@ def sample(self, size=None): ======= dict: Dictionary of the samples """ - return self.sample_subset_constrained(keys=list(self.keys()), size=size) + return self.sample_subset_constrained(keys=list(self.keys()), size=size, xp=xp) - def sample_subset_constrained_as_array(self, keys=iter([]), size=None): + def sample_subset_constrained_as_array(self, keys=iter([]), size=None, *, xp=np): """Return an array of samples Parameters @@ -382,12 +382,12 @@ def sample_subset_constrained_as_array(self, keys=iter([]), size=None): array: array_like An array of shape (len(key), size) of the samples (ordered by keys) """ - samples_dict = self.sample_subset_constrained(keys=keys, size=size) - samples_dict = {key: np.atleast_1d(val) for key, val in samples_dict.items()} + samples_dict = self.sample_subset_constrained(keys=keys, size=size, xp=xp) + samples_dict = {key: xp.atleast_1d(val) for key, val in samples_dict.items()} samples_list = [samples_dict[key] for key in keys] - return np.array(samples_list) + return xp.array(samples_list) - def sample_subset(self, keys=iter([]), size=None): + def sample_subset(self, keys=iter([]), size=None, *, xp=np): """Draw samples from the prior set for parameters which are not a DeltaFunction Parameters @@ -407,7 +407,7 @@ def sample_subset(self, keys=iter([]), size=None): if isinstance(self[key], Constraint): continue elif isinstance(self[key], Prior): - samples[key] = self[key].sample(size=size) + samples[key] = self[key].sample(size=size, xp=xp) else: logger.debug("{} not a known prior.".format(key)) return samples @@ -430,7 +430,7 @@ def fixed_keys(self): def constraint_keys(self): return [k for k, p in self.items() if isinstance(p, Constraint)] - def sample_subset_constrained(self, keys=iter([]), size=None): + def sample_subset_constrained(self, keys=iter([]), size=None, *, xp=np): """ Sample a subset of priors while ensuring constraints are satisfied. @@ -446,7 +446,7 @@ def sample_subset_constrained(self, keys=iter([]), size=None): dict: Dictionary of valid samples. """ if not any(isinstance(self[key], Constraint) for key in self): - return self.sample_subset(keys=keys, size=size) + return self.sample_subset(keys=keys, size=size, xp=xp) efficiency_warning_was_issued = False @@ -462,7 +462,7 @@ def check_efficiency(n_tested, n_valid): n_tested_samples, n_valid_samples = 0, 0 if size is None or size == 1: while True: - sample = self.sample_subset(keys=keys, size=size) + sample = self.sample_subset(keys=keys, size=size, xp=xp) is_valid = self.evaluate_constraints(sample) n_tested_samples += 1 n_valid_samples += int(is_valid) @@ -477,17 +477,17 @@ def check_efficiency(n_tested, n_valid): all_samples = {key: np.array([]) for key in keys} _first_key = list(all_samples.keys())[0] while len(all_samples[_first_key]) < needed: - samples = self.sample_subset(keys=keys, size=needed) + samples = self.sample_subset(keys=keys, size=needed, xp=xp) keep = np.array(self.evaluate_constraints(samples), dtype=bool) for key in keys: - all_samples[key] = np.hstack( + all_samples[key] = xp.hstack( [all_samples[key], samples[key][keep].flatten()] ) n_tested_samples += needed n_valid_samples += np.sum(keep) check_efficiency(n_tested_samples, n_valid_samples) all_samples = { - key: np.reshape(all_samples[key][:needed], size) for key in keys + key: xp.reshape(all_samples[key][:needed], size) for key in keys } return all_samples @@ -512,15 +512,15 @@ def normalize_constraint_factor( self._cached_normalizations[keys] = factor_rounded return factor_rounded - def _estimate_normalization(self, keys, min_accept, sampling_chunk): - samples = self.sample_subset(keys=keys, size=sampling_chunk) + def _estimate_normalization(self, keys, min_accept, sampling_chunk, *, xp=np): + samples = self.sample_subset(keys=keys, size=sampling_chunk, xp=xp) keep = np.atleast_1d(self.evaluate_constraints(samples)) if len(keep) == 1: self._cached_normalizations[keys] = 1 return 1 all_samples = {key: np.array([]) for key in keys} while np.count_nonzero(keep) < min_accept: - samples = self.sample_subset(keys=keys, size=sampling_chunk) + samples = self.sample_subset(keys=keys, size=sampling_chunk, xp=xp) for key in samples: all_samples[key] = np.hstack([all_samples[key], samples[key].flatten()]) keep = np.array(self.evaluate_constraints(all_samples), dtype=bool) @@ -610,7 +610,8 @@ def check_ln_prob(self, sample, ln_prob, normalized=True): constrained_ln_prob[in_bounds] = ln_prob[in_bounds] + keep + np.log(ratio) return constrained_ln_prob - def cdf(self, sample): + @xp_wrap + def cdf(self, sample, *, xp=None): """Evaluate the cumulative distribution function at the provided points Parameters @@ -624,10 +625,10 @@ def cdf(self, sample): """ return sample.__class__( - {key: self[key].cdf(sample) for key, sample in sample.items()} + {key: self[key].cdf(sample, xp=xp) for key, sample in sample.items()} ) - def rescale(self, keys, theta): + def rescale(self, keys, theta, *, xp=None): """Rescale samples from unit cube to prior Parameters @@ -643,9 +644,10 @@ def rescale(self, keys, theta): """ if isinstance(theta, {}.values().__class__): theta = list(theta) - xp = array_module(theta) + if xp is None: + xp = array_module(theta) - return xp.asarray([self[key].rescale(sample) for key, sample in zip(keys, theta)]) + return xp.asarray([self[key].rescale(sample, xp=xp) for key, sample in zip(keys, theta)]) def test_redundancy(self, key, disable_logging=False): """Empty redundancy test, should be overwritten in subclasses""" @@ -745,7 +747,7 @@ def _check_conditions_resolved(self, key, sampled_keys): conditions_resolved = False return conditions_resolved - def sample_subset(self, keys=iter([]), size=None): + def sample_subset(self, keys=iter([]), size=None, *, xp=np): self.convert_floats_to_delta_functions() add_delta_keys = [ key @@ -765,7 +767,9 @@ def sample_subset(self, keys=iter([]), size=None): if isinstance(self[key], Prior): try: samples[key] = subset_dict[key].sample( - size=size, **subset_dict.get_required_variables(key) + size=size, + xp=xp, + **subset_dict.get_required_variables(key), ) except ValueError: # Some prior classes can not handle an array of conditional parameters (e.g. alpha for PowerLaw) @@ -776,7 +780,10 @@ def sample_subset(self, keys=iter([]), size=None): rvars = { key: value[i] for key, value in required_variables.items() } - samples[key][i] = subset_dict[key].sample(**rvars) + samples[key][i] = subset_dict[key].sample( + **rvars, + xp=xp, + ) else: logger.debug("{} not a known prior.".format(key)) return samples @@ -798,7 +805,8 @@ def get_required_variables(self, key): for k in getattr(self[key], "required_variables", []) } - def prob(self, sample, **kwargs): + @xp_wrap + def prob(self, sample, *, xp=None, **kwargs): """ Parameters @@ -814,9 +822,9 @@ def prob(self, sample, **kwargs): """ self._prepare_evaluation(*zip(*sample.items())) - xp = array_module(sample.values()) + print(sample, xp) res = xp.asarray([ - self[key].prob(sample[key], **self.get_required_variables(key)) + self[key].prob(sample[key], **self.get_required_variables(key), xp=xp) for key in sample ]) prob = xp.prod(res, **kwargs) @@ -852,10 +860,11 @@ def ln_prob(self, sample, axis=None, normalized=True): # return self.check_ln_prob(sample, ln_prob, # normalized=normalized) - def cdf(self, sample): + @xp_wrap + def cdf(self, sample, *, xp=None): self._prepare_evaluation(*zip(*sample.items())) res = { - key: self[key].cdf(sample[key], **self.get_required_variables(key)) + key: self[key].cdf(sample[key], **self.get_required_variables(key), xp=xp) for key in sample } return sample.__class__(res) diff --git a/bilby/core/prior/interpolated.py b/bilby/core/prior/interpolated.py index ab03809d1..1983877d7 100644 --- a/bilby/core/prior/interpolated.py +++ b/bilby/core/prior/interpolated.py @@ -66,7 +66,8 @@ def __eq__(self, other): return True return False - def prob(self, val): + @xp_wrap + def prob(self, val, *, xp=None): """Return the prior probability of val. Parameters @@ -79,11 +80,12 @@ def prob(self, val): """ return self.probability_density(val)[()] - def cdf(self, val): + @xp_wrap + def cdf(self, val, *, xp=None): return self.cumulative_distribution(val)[()] @xp_wrap - def rescale(self, val, *, xp=np): + def rescale(self, val, *, xp=None): """ 'Rescale' a sample from the unit line element to the prior. diff --git a/bilby/core/prior/joint.py b/bilby/core/prior/joint.py index 06a740497..0e8e8abfa 100644 --- a/bilby/core/prior/joint.py +++ b/bilby/core/prior/joint.py @@ -1,5 +1,6 @@ import re +import array_api_extra as xpx import numpy as np import scipy.stats from scipy.special import erfinv @@ -173,13 +174,14 @@ def _split_repr(cls, string): kwargs[key.strip()] = arg return kwargs - def prob(self, samp): + @xp_wrap + def prob(self, samp, *, xp=None): """ Get the probability of a sample. For bounded priors the probability will not be properly normalised. """ - return np.exp(self.ln_prob(samp)) + return xp.exp(self.ln_prob(samp, xp=xp)) def _check_samp(self, value): """ @@ -217,7 +219,8 @@ def _check_samp(self, value): break return samp, outbounds - def ln_prob(self, value): + @xp_wrap + def ln_prob(self, value, *, xp=None): """ Get the log-probability of a sample. For bounded priors the probability will not be properly normalised. @@ -231,14 +234,12 @@ def ln_prob(self, value): """ samp, outbounds = self._check_samp(value) - lnprob = -np.inf * np.ones(samp.shape[0]) - lnprob = self._ln_prob(samp, lnprob, outbounds) - if samp.shape[0] == 1: - return lnprob[0] - else: - return lnprob + lnprob = -np.inf * xp.ones(samp.shape[0]) + lnprob = self._ln_prob(samp, lnprob, outbounds, xp=xp) + return lnprob[()] - def _ln_prob(self, samp, lnprob, outbounds): + @xp_wrap + def _ln_prob(self, samp, lnprob, outbounds, *, xp=None): """ Get the log-probability of a sample. For bounded priors the probability will not be properly normalised. **this method needs overwritten by child class** @@ -262,7 +263,7 @@ def _ln_prob(self, samp, lnprob, outbounds): """ return lnprob - def sample(self, size=1, **kwargs): + def sample(self, size=1, *, xp=np, **kwargs): """ Draw, and set, a sample from the Dist, accompanying method _sample needs to overwritten @@ -274,14 +275,11 @@ def sample(self, size=1, **kwargs): if size is None: size = 1 - samps = self._sample(size=size, **kwargs) + samps = self._sample(size=size, xp=xp, **kwargs) for i, name in enumerate(self.names): - if size == 1: - self.current_sample[name] = samps[:, i].flatten()[0] - else: - self.current_sample[name] = samps[:, i].flatten() + self.current_sample[name] = samps[:, i].flatten()[()] - def _sample(self, size, **kwargs): + def _sample(self, size, *, xp=np, **kwargs): """ Draw, and set, a sample from the joint dist (**needs to be ovewritten by child class**) @@ -290,14 +288,14 @@ def _sample(self, size, **kwargs): size: int number of samples to generate, defaults to 1 """ - samps = np.zeros((size, len(self))) + samps = xp.zeros((size, len(self))) """ Here is where the subclass where overwrite sampling method """ return samps @xp_wrap - def rescale(self, value, *, xp=np, **kwargs): + def rescale(self, value, *, xp=None, **kwargs): """ Rescale from a unit hypercube to JointPriorDist. Note that no bounds are applied in the rescale function. (child classes need to @@ -614,8 +612,7 @@ def add_mode(self, mus=None, sigmas=None, corrcoef=None, cov=None, weight=1.0): ) @xp_wrap - def _rescale(self, samp, *, xp=np, **kwargs): - print(samp, xp) + def _rescale(self, samp, *, xp=None, **kwargs): try: mode = kwargs["mode"] except KeyError: @@ -630,12 +627,12 @@ def _rescale(self, samp, *, xp=np, **kwargs): samp = erfinv(2.0 * samp - 1) * 2.0 ** 0.5 # rotate and scale to the multivariate normal shape - samp = self.mus[mode] + self.sigmas[mode] * xp.einsum( + samp = xp.array(self.mus[mode]) + self.sigmas[mode] * xp.einsum( "ij,kj->ik", samp * self.sqeigvalues[mode], self.eigvectors[mode] ) return samp - def _sample(self, size, **kwargs): + def _sample(self, size, *, xp=np, **kwargs): try: mode = kwargs["mode"] except KeyError: @@ -677,18 +674,21 @@ def _sample(self, size, **kwargs): if not outbound: inbound = True - return samps + return xp.array(samps) - def _ln_prob(self, samp, lnprob, outbounds): + @xp_wrap + def _ln_prob(self, samp, lnprob, outbounds, *, xp=None): for j in range(samp.shape[0]): # loop over the modes and sum the probabilities for i in range(self.nmodes): # self.mvn[i] is a "standard" multivariate normal distribution; see add_mode() z = (samp[j] - self.mus[i]) / self.sigmas[i] - lnprob[j] = np.logaddexp(lnprob[j], self.mvn[i].logpdf(z) - self.logprodsigmas[i]) + lnprob = xpx.at(lnprob, j).set( + xp.logaddexp(lnprob[j], self.mvn[i].logpdf(z) - self.logprodsigmas[i]) + ) # set out-of-bounds values to -inf - lnprob[outbounds] = -np.inf + lnprob = xp.where(outbounds, -xp.inf, lnprob) return lnprob def __eq__(self, other): @@ -783,7 +783,7 @@ def maximum(self, maximum): self.dist.bounds[self.name] = (self.dist.bounds[self.name][0], maximum) @xp_wrap - def rescale(self, val, *, xp=np, **kwargs): + def rescale(self, val, *, xp=None, **kwargs): """ Scale a unit hypercube sample to the prior. @@ -808,7 +808,7 @@ def rescale(self, val, *, xp=np, **kwargs): else: return [] # return empty list - def sample(self, size=1, **kwargs): + def sample(self, size=1, *, xp=np, **kwargs): """ Draw a sample from the prior. @@ -833,7 +833,7 @@ def sample(self, size=1, **kwargs): if len(self.dist.current_sample) == 0: # generate a sample - self.dist.sample(size=size, **kwargs) + self.dist.sample(size=size, xp=xp, **kwargs) sample = self.dist.current_sample[self.name] @@ -846,7 +846,8 @@ def sample(self, size=1, **kwargs): self.least_recently_sampled = sample return sample - def ln_prob(self, val): + @xp_wrap + def ln_prob(self, val, *, xp=None): """ Return the natural logarithm of the prior probability. Note that this will not be correctly normalised if there are bounds on the @@ -868,25 +869,16 @@ def ln_prob(self, val): values = list(self.dist.requested_parameters.values()) # check for the same number of values for each parameter - for i in range(len(self.dist) - 1): - if isinstance(values[i], (list, np.ndarray)) or isinstance( - values[i + 1], (list, np.ndarray) - ): - if isinstance(values[i], (list, np.ndarray)) and isinstance( - values[i + 1], (list, np.ndarray) - ): - if len(values[i]) != len(values[i + 1]): - raise ValueError( - "Each parameter must have the same " - "number of requested values." - ) - else: - raise ValueError( - "Each parameter must have the same " - "number of requested values." - ) + shapes = set() + for v in values: + shapes.add(xp.array(v).shape) + if len(shapes) > 1: + raise ValueError( + "Each parameter must have the same " + "number of requested values." + ) - lnp = self.dist.ln_prob(np.asarray(values).T) + lnp = self.dist.ln_prob(xp.array(values).T) # reset the requested parameters self.dist.reset_request() @@ -905,9 +897,10 @@ def ln_prob(self, val): if len(val) == 1: return 0.0 else: - return np.zeros_like(val) + return xp.zeros_like(val) - def prob(self, val): + @xp_wrap + def prob(self, val, *, xp=None): """Return the prior probability of val Parameters @@ -921,7 +914,7 @@ def prob(self, val): the p value for the prior at given sample """ - return np.exp(self.ln_prob(val)) + return xp.exp(self.ln_prob(val, xp=xp)) class MultivariateGaussian(JointPrior): diff --git a/bilby/core/prior/slabspike.py b/bilby/core/prior/slabspike.py index ff823a369..30ca16639 100644 --- a/bilby/core/prior/slabspike.py +++ b/bilby/core/prior/slabspike.py @@ -73,7 +73,7 @@ def _find_inverse_cdf_fraction_before_spike(self): return float(self.slab.cdf(self.spike_location)) * self.slab_fraction @xp_wrap - def rescale(self, val, *, xp=np): + def rescale(self, val, *, xp=None): """ 'Rescale' a sample from the unit line element to the prior. @@ -93,15 +93,20 @@ def rescale(self, val, *, xp=np): ) higher_indices = val >= (self.inverse_cdf_below_spike + self.spike_height) - slab_scaled = self._contracted_rescale(val - self.spike_height * higher_indices) + slab_scaled = self._contracted_rescale( + val - self.spike_height * higher_indices, xp=xp + ) + print(type(slab_scaled)) res = xp.select( [lower_indices | higher_indices, intermediate_indices], [slab_scaled, self.spike_location], ) + print(type(res)) return res - def _contracted_rescale(self, val): + @xp_wrap + def _contracted_rescale(self, val, *, xp=None): """ Contracted version of the rescale function that implements the `rescale` function on the pure slab part of the prior. @@ -115,10 +120,10 @@ def _contracted_rescale(self, val): ======= array_like: Associated prior value with input value. """ - return self.slab.rescale(val / self.slab_fraction) + return self.slab.rescale(val / self.slab_fraction, xp=xp) @xp_wrap - def prob(self, val, *, xp=np): + def prob(self, val, *, xp=None): """Return the prior probability of val. Returns np.inf for the spike location @@ -136,7 +141,7 @@ def prob(self, val, *, xp=np): return res @xp_wrap - def ln_prob(self, val, *, xp=np): + def ln_prob(self, val, *, xp=None): """Return the Log prior probability of val. Returns np.inf for the spike location @@ -153,7 +158,8 @@ def ln_prob(self, val, *, xp=np): res += xp.nan_to_num(xp.inf * (val == self.spike_location), posinf=xp.inf) return res - def cdf(self, val): + @xp_wrap + def cdf(self, val, *, xp=None): """ Return the CDF of the prior. This calls to the slab CDF and adds a discrete step at the spike location. @@ -167,6 +173,6 @@ def cdf(self, val): array_like: CDF value of val """ - res = self.slab.cdf(val) * self.slab_fraction + res = self.slab.cdf(val, xp=xp) * self.slab_fraction res += (val > self.spike_location) * self.spike_height return res diff --git a/bilby/core/utils/calculus.py b/bilby/core/utils/calculus.py index 2cc2b6ae1..889e086b0 100644 --- a/bilby/core/utils/calculus.py +++ b/bilby/core/utils/calculus.py @@ -3,9 +3,9 @@ import array_api_compat as aac import numpy as np from scipy.interpolate import RectBivariateSpline, interp1d as _interp1d -from scipy.special import logsumexp from .log import logger +from ...compat.patches import logsumexp from ...compat.utils import array_module, xp_wrap diff --git a/bilby/gw/prior.py b/bilby/gw/prior.py index 3258d37d1..3fe9ed242 100644 --- a/bilby/gw/prior.py +++ b/bilby/gw/prior.py @@ -1,6 +1,7 @@ import os import copy +import array_api_extra as xpx import numpy as np from scipy.integrate import cumulative_trapezoid, trapezoid, quad from scipy.interpolate import InterpolatedUnivariateSpline @@ -431,23 +432,24 @@ def __init__(self, minimum, maximum, name='mass_ratio', latex_label='$q$', def _integral(q): return -5. * q**(-1. / 5.) * hyp2f1(-2. / 5., -1. / 5., 4. / 5., -q) - def cdf(self, val): + def cdf(self, val, *, xp=np): return (self._integral(val) - self._integral(self.minimum)) / self.norm - def rescale(self, val): + @xp_wrap + def rescale(self, val, *, xp=None): if self.equal_mass: - val = 2 * np.minimum(val, 1 - val) + val = 2 * xp.minimum(val, 1 - val) return self.icdf(val) - def prob(self, val): + def prob(self, val, *, xp=np): in_prior = (val >= self.minimum) & (val <= self.maximum) with np.errstate(invalid="ignore"): prob = (1. + val)**(2. / 5.) / (val**(6. / 5.)) / self.norm * in_prior return prob - def ln_prob(self, val): + def ln_prob(self, val, *, xp=np): with np.errstate(divide="ignore"): - return np.log(self.prob(val)) + return np.log(self.prob(val, xp=xp)) class AlignedSpin(Interped): @@ -512,7 +514,7 @@ def integrand(aa, chi): after performing the integral over spin orientation using a delta function identity. """ - return a_prior.prob(aa, xp=np) * z_prior.prob(chi / aa, xp=np) / aa + return a_prior.prob(aa, xp=None) * z_prior.prob(chi / aa, xp=None) / aa self.num_interp = 10_000 if num_interp is None else num_interp xx = np.linspace(chi_min, chi_max, self.num_interp) @@ -619,7 +621,8 @@ def ln_prob(self, val, *, xp=np, **required_variables): with np.errstate(divide="ignore"): return xp.log(self.prob(val, **required_variables)) - def cdf(self, val, **required_variables): + @xp_wrap + def cdf(self, val, *, xp=np, **required_variables): r""" .. math:: \text{CDF}(\chi_\per) = N ln(1 + (\chi_\perp / \chi) ** 2) @@ -639,14 +642,15 @@ def cdf(self, val, **required_variables): """ self.update_conditions(**required_variables) chi_aligned = abs(required_variables[self._required_variables[0]]) - return np.maximum(np.minimum( + return xp.clip( (val >= self.minimum) * (val <= self.maximum) - * np.log(1 + (val / chi_aligned) ** 2) - / 2 / np.log(self._reference_maximum / chi_aligned) - , 1 - ), 0) + * xp.log(1 + (val / chi_aligned) ** 2) + / 2 / xp.log(self._reference_maximum / chi_aligned), + 0, + 1 + ) - def rescale(self, val, **required_variables): + def rescale(self, val, *, xp=np, **required_variables): r""" .. math:: \text{PPF}(\chi_\perp) = ((a_\max / \chi) ** (2x) - 1) ** 0.5 * \chi @@ -695,13 +699,13 @@ def __init__(self, minimum=-np.inf, maximum=np.inf): super().__init__(minimum=minimum, maximum=maximum, name=None, latex_label=None, unit=None) - def prob(self, val): + def prob(self, val, *, xp=np): """ Returns the result of the equation of state check in the conversion function. """ return val - def ln_prob(self, val): + def ln_prob(self, val, *, xp=np): if val: result = 0.0 @@ -1521,7 +1525,8 @@ def _check_imports(): raise ImportError("Must have healpy installed on this machine to use HealPixMapPrior") return healpy - def _rescale(self, samp, **kwargs): + @xp_wrap + def _rescale(self, samp, *, xp=np, **kwargs): """ Overwrites the _rescale method of BaseJoint Prior to rescale a single value from the unitcube onto two values (ra, dec) or 3 (ra, dec, dist) if distance is included @@ -1544,17 +1549,19 @@ def _rescale(self, samp, **kwargs): else: samp = samp[:, 0] pix_rescale = self.inverse_cdf(samp) - sample = np.empty((len(pix_rescale), 2)) - dist_samples = np.empty((len(pix_rescale))) + sample = xp.empty((len(pix_rescale), 2)) + dist_samples = xp.empty((len(pix_rescale))) for i, val in enumerate(pix_rescale): theta, ra = self.hp.pix2ang(self.nside, int(round(val))) dec = 0.5 * np.pi - theta - sample[i, :] = self.draw_from_pixel(ra, dec, int(round(val))) + sample = xpx.at(sample, i).set(xp.array(self.draw_from_pixel(ra, dec, int(round(val))))) if self.distance: self.update_distance(int(round(val))) - dist_samples[i] = self.distance_icdf(dist_samp[i]) + dist_samples = xpx.at(dist_samples, i).set( + xp.array(self.distance_icdf(dist_samp[i])) + ) if self.distance: - sample = np.vstack([sample[:, 0], sample[:, 1], dist_samples]) + sample = xp.vstack([sample[:, 0], sample[:, 1], dist_samples]) return sample.reshape((-1, self.num_vars)) def update_distance(self, pix_idx): @@ -1600,7 +1607,7 @@ def _check_norm(array): norm = np.finfo(array.dtype).eps return array / norm - def _sample(self, size, **kwargs): + def _sample(self, size, *, xp=np, **kwargs): """ Overwrites the _sample method of BaseJoint Prior. Picks a pixel value according to their probabilities, then uniformly samples ra, and decs that are contained in chosen pixel. If the PriorDist includes distance it then @@ -1631,7 +1638,7 @@ def _sample(self, size, **kwargs): sample[samp, :] = [ra_dec[0], ra_dec[1], dist] else: sample[samp, :] = self.draw_from_pixel(ra, dec, sample_pix[samp]) - return sample.reshape((-1, self.num_vars)) + return xp.array(sample.reshape((-1, self.num_vars))) def draw_distance(self, pix): """ @@ -1710,7 +1717,8 @@ def check_in_pixel(self, ra, dec, pix): pixel = self.hp.ang2pix(self.nside, theta, phi) return pix == pixel - def _ln_prob(self, samp, lnprob, outbounds): + @xp_wrap + def _ln_prob(self, samp, lnprob, outbounds, *, xp=None): """ Overwrites the _lnprob method of BaseJoint Prior @@ -1736,11 +1744,13 @@ def _ln_prob(self, samp, lnprob, outbounds): phi, dec = samp[0] theta = 0.5 * np.pi - dec pixel = self.hp.ang2pix(self.nside, theta, phi) - lnprob[i] = np.log(self.prob[pixel] / self.pixel_area) + xpx.at(lnprob, i).set(xp.log(self.prob[pixel] / self.pixel_area)) if self.distance: self.update_distance(pixel) - lnprob[i] += np.log(self.distance_pdf(dist) * dist ** 2) - lnprob[outbounds] = -np.inf + lnprob = xpx.at(lnprob, i).set( + lnprob[i] + xp.log(self.distance_pdf(dist) * dist ** 2) + ) + lnprob = xp.where(outbounds, -np.inf, lnprob) return lnprob def __eq__(self, other): diff --git a/test/conftest.py b/test/conftest.py index 6efaa82e2..355c3074b 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -37,7 +37,10 @@ def _xp(request): import numpy return numpy case "jax" | "jax.numpy": + import os import jax + + os.environ["SCIPY_ARRAY_API"] = "1" jax.config.update("jax_enable_x64", True) return jax.numpy case _: diff --git a/test/core/grid_test.py b/test/core/grid_test.py index e0c9a455e..d99af8327 100644 --- a/test/core/grid_test.py +++ b/test/core/grid_test.py @@ -206,7 +206,6 @@ def test_grid_from_array(self): grid_size=n_points, likelihood=self.likelihood, xp=self.xp, - vectorized=True, ) self.assertTupleEqual((len(x0s), len(x1s)), grid.mesh_grid[0].shape) diff --git a/test/core/prior/analytical_test.py b/test/core/prior/analytical_test.py index 12892aca1..8325be232 100644 --- a/test/core/prior/analytical_test.py +++ b/test/core/prior/analytical_test.py @@ -1,16 +1,19 @@ import unittest -import numpy as np import bilby +import numpy as np +import pytest +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestDiscreteValuesPrior(unittest.TestCase): def test_single_sample(self): values = [1.1, 1.2, 1.3] discrete_value_prior = bilby.core.prior.DiscreteValues(values) in_prior = True for _ in range(1000): - s = discrete_value_prior.sample() + s = discrete_value_prior.sample(xp=self.xp) if s not in values: in_prior = False self.assertTrue(in_prior) @@ -20,7 +23,7 @@ def test_array_sample(self): nvalues = 4 discrete_value_prior = bilby.core.prior.DiscreteValues(values) N = 100000 - s = discrete_value_prior.sample(N) + s = discrete_value_prior.sample(N, xp=self.xp) zeros = np.sum(s == 1.0) ones = np.sum(s == 1.1) twos = np.sum(s == 1.2) @@ -35,21 +38,25 @@ def test_single_probability(self): N = 3 values = [1.1, 2.2, 300.0] discrete_value_prior = bilby.core.prior.DiscreteValues(values) - self.assertEqual(discrete_value_prior.prob(1.1), 1 / N) - self.assertEqual(discrete_value_prior.prob(2.2), 1 / N) - self.assertEqual(discrete_value_prior.prob(300.0), 1 / N) - self.assertEqual(discrete_value_prior.prob(0.5), 0) - self.assertEqual(discrete_value_prior.prob(200), 0) + self.assertEqual(discrete_value_prior.prob(self.xp.array(1.1)), 1 / N) + self.assertEqual(discrete_value_prior.prob(self.xp.array(2.2)), 1 / N) + self.assertEqual(discrete_value_prior.prob(self.xp.array(300.0)), 1 / N) + self.assertEqual(discrete_value_prior.prob(self.xp.array(0.5)), 0) + self.assertEqual(discrete_value_prior.prob(self.xp.array(200)), 0) def test_single_probability_unsorted(self): N = 3 values = [1.1, 300, 2.2] discrete_value_prior = bilby.core.prior.DiscreteValues(values) - self.assertEqual(discrete_value_prior.prob(1.1), 1 / N) - self.assertEqual(discrete_value_prior.prob(2.2), 1 / N) - self.assertEqual(discrete_value_prior.prob(300.0), 1 / N) - self.assertEqual(discrete_value_prior.prob(0.5), 0) - self.assertEqual(discrete_value_prior.prob(200), 0) + self.assertEqual(discrete_value_prior.prob(self.xp.array(1.1)), 1 / N) + self.assertEqual(discrete_value_prior.prob(self.xp.array(2.2)), 1 / N) + self.assertEqual(discrete_value_prior.prob(self.xp.array(300.0)), 1 / N) + self.assertEqual(discrete_value_prior.prob(self.xp.array(0.5)), 0) + self.assertEqual(discrete_value_prior.prob(self.xp.array(200)), 0) + self.assertEqual( + discrete_value_prior.prob(self.xp.array(0.5)).__array_namespace__(), + self.xp, + ) def test_array_probability(self): N = 3 @@ -57,7 +64,7 @@ def test_array_probability(self): discrete_value_prior = bilby.core.prior.DiscreteValues(values) self.assertTrue( np.all( - discrete_value_prior.prob([1.1, 2.2, 2.2, 300.0, 200.0]) + discrete_value_prior.prob(self.xp.array([1.1, 2.2, 2.2, 300.0, 200.0])) == np.array([1 / N, 1 / N, 1 / N, 1 / N, 0]) ) ) @@ -66,10 +73,14 @@ def test_single_lnprobability(self): N = 3 values = [1.1, 2.2, 300.0] discrete_value_prior = bilby.core.prior.DiscreteValues(values) - self.assertEqual(discrete_value_prior.ln_prob(1.1), -np.log(N)) - self.assertEqual(discrete_value_prior.ln_prob(2.2), -np.log(N)) - self.assertEqual(discrete_value_prior.ln_prob(300), -np.log(N)) - self.assertEqual(discrete_value_prior.ln_prob(150), -np.inf) + self.assertEqual(discrete_value_prior.ln_prob(self.xp.array(1.1)), -np.log(N)) + self.assertEqual(discrete_value_prior.ln_prob(self.xp.array(2.2)), -np.log(N)) + self.assertEqual(discrete_value_prior.ln_prob(self.xp.array(300)), -np.log(N)) + self.assertEqual(discrete_value_prior.ln_prob(self.xp.array(150)), -np.inf) + self.assertEqual( + discrete_value_prior.ln_prob(self.xp.array(0.5)).__array_namespace__(), + self.xp, + ) def test_array_lnprobability(self): N = 3 @@ -77,18 +88,20 @@ def test_array_lnprobability(self): discrete_value_prior = bilby.core.prior.DiscreteValues(values) self.assertTrue( np.all( - discrete_value_prior.ln_prob([1.1, 2.2, 2.2, 300, 150]) + discrete_value_prior.ln_prob(self.xp.array([1.1, 2.2, 2.2, 300, 150])) == np.array([-np.log(N), -np.log(N), -np.log(N), -np.log(N), -np.inf]) ) ) +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestCategoricalPrior(unittest.TestCase): def test_single_sample(self): categorical_prior = bilby.core.prior.Categorical(3) in_prior = True for _ in range(1000): - s = categorical_prior.sample() + s = categorical_prior.sample(xp=self.xp) if s not in [0, 1, 2]: in_prior = False self.assertTrue(in_prior) @@ -97,7 +110,7 @@ def test_array_sample(self): ncat = 4 categorical_prior = bilby.core.prior.Categorical(ncat) N = 100000 - s = categorical_prior.sample(N) + s = categorical_prior.sample(N, xp=self.xp) zeros = np.sum(s == 0) ones = np.sum(s == 1) twos = np.sum(s == 2) @@ -107,41 +120,57 @@ def test_array_sample(self): self.assertAlmostEqual(ones / N, 1 / ncat, places=int(np.log10(np.sqrt(N)))) self.assertAlmostEqual(twos / N, 1 / ncat, places=int(np.log10(np.sqrt(N)))) self.assertAlmostEqual(threes / N, 1 / ncat, places=int(np.log10(np.sqrt(N)))) + self.assertEqual(s.__array_namespace__(), self.xp) def test_single_probability(self): N = 3 categorical_prior = bilby.core.prior.Categorical(N) - self.assertEqual(categorical_prior.prob(0), 1 / N) - self.assertEqual(categorical_prior.prob(1), 1 / N) - self.assertEqual(categorical_prior.prob(2), 1 / N) - self.assertEqual(categorical_prior.prob(0.5), 0) + self.assertEqual(categorical_prior.prob(self.xp.array(0)), 1 / N) + self.assertEqual(categorical_prior.prob(self.xp.array(1)), 1 / N) + self.assertEqual(categorical_prior.prob(self.xp.array(2)), 1 / N) + self.assertEqual(categorical_prior.prob(self.xp.array(0.5)), 0) + self.assertEqual( + categorical_prior.prob(self.xp.array(0.5)).__array_namespace__(), + self.xp, + ) def test_array_probability(self): N = 3 categorical_prior = bilby.core.prior.Categorical(N) - self.assertTrue(np.all(categorical_prior.prob([0, 1, 1, 2, 3]) == np.array([1 / N, 1 / N, 1 / N, 1 / N, 0]))) + self.assertTrue(np.all( + categorical_prior.prob(self.xp.array([0, 1, 1, 2, 3])) + == np.array([1 / N, 1 / N, 1 / N, 1 / N, 0]) + )) def test_single_lnprobability(self): N = 3 categorical_prior = bilby.core.prior.Categorical(N) - self.assertEqual(categorical_prior.ln_prob(0), -np.log(N)) - self.assertEqual(categorical_prior.ln_prob(1), -np.log(N)) - self.assertEqual(categorical_prior.ln_prob(2), -np.log(N)) - self.assertEqual(categorical_prior.ln_prob(0.5), -np.inf) + self.assertEqual(categorical_prior.ln_prob(self.xp.array(0)), -np.log(N)) + self.assertEqual(categorical_prior.ln_prob(self.xp.array(1)), -np.log(N)) + self.assertEqual(categorical_prior.ln_prob(self.xp.array(2)), -np.log(N)) + self.assertEqual(categorical_prior.ln_prob(self.xp.array(0.5)), -np.inf) + self.assertEqual( + categorical_prior.ln_prob(self.xp.array(0.5)).__array_namespace__(), + self.xp, + ) def test_array_lnprobability(self): N = 3 categorical_prior = bilby.core.prior.Categorical(N) - self.assertTrue(np.all(categorical_prior.ln_prob([0, 1, 1, 2, 3]) == np.array( - [-np.log(N), -np.log(N), -np.log(N), -np.log(N), -np.inf]))) + self.assertTrue(np.all( + categorical_prior.ln_prob(self.xp.array([0, 1, 1, 2, 3])) + == np.array([-np.log(N), -np.log(N), -np.log(N), -np.log(N), -np.inf]) + )) +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestWeightedCategoricalPrior(unittest.TestCase): def test_single_sample(self): categorical_prior = bilby.core.prior.WeightedCategorical(3, [1, 2, 3]) in_prior = True for _ in range(1000): - s = categorical_prior.sample() + s = categorical_prior.sample(xp=self.xp) if s not in [0, 1, 2]: in_prior = False self.assertTrue(in_prior) @@ -157,39 +186,49 @@ def test_array_sample(self): weights = np.arange(1, ncat + 1) categorical_prior = bilby.core.prior.WeightedCategorical(ncat, weights=weights) N = 100000 - s = categorical_prior.sample(N) + s = categorical_prior.sample(N, xp=self.xp) cases = 0 - for i in categorical_prior.values: + for i in self.xp.array(categorical_prior.values): case = np.sum(s == i) cases += case self.assertAlmostEqual(case / N, categorical_prior.prob(i), places=int(np.log10(np.sqrt(N)))) self.assertAlmostEqual(case / N, weights[i] / np.sum(weights), places=int(np.log10(np.sqrt(N)))) self.assertEqual(cases, N) + self.assertEqual(s.__array_namespace__(), self.xp) def test_single_probability(self): N = 3 weights = np.arange(1, N + 1) categorical_prior = bilby.core.prior.WeightedCategorical(N, weights=weights) - for i in categorical_prior.values: + for i in self.xp.array(categorical_prior.values): self.assertEqual(categorical_prior.prob(i), weights[i] / np.sum(weights)) - self.assertEqual(categorical_prior.prob(0.5), 0) + prob = categorical_prior.prob(self.xp.array(0.5)) + self.assertEqual(prob, 0) + self.assertEqual(prob.__array_namespace__(), self.xp) def test_array_probability(self): N = 3 - test_cases = [0, 1, 1, 2, 3] + test_cases = self.xp.array([0, 1, 1, 2, 3]) weights = np.arange(1, N + 1) categorical_prior = bilby.core.prior.WeightedCategorical(N, weights=weights) probs = np.arange(1, N + 2) / np.sum(weights) probs[-1] = 0 - self.assertTrue(np.all(categorical_prior.prob(test_cases) == probs[test_cases])) + new = categorical_prior.prob(test_cases) + self.assertTrue(np.all(new == probs[test_cases])) + self.assertEqual(new.__array_namespace__(), self.xp) def test_single_lnprobability(self): N = 3 weights = np.arange(1, N + 1) categorical_prior = bilby.core.prior.WeightedCategorical(N, weights=weights) - for i in categorical_prior.values: - self.assertEqual(categorical_prior.ln_prob(i), np.log(weights[i] / np.sum(weights))) - self.assertEqual(categorical_prior.prob(0.5), 0) + for i in self.xp.array(categorical_prior.values): + self.assertEqual( + categorical_prior.ln_prob(self.xp.array(i)), + np.log(weights[i] / np.sum(weights)), + ) + prob = categorical_prior.prob(self.xp.array(0.5)) + self.assertEqual(prob, 0) + self.assertEqual(prob.__array_namespace__(), self.xp) def test_array_lnprobability(self): N = 3 @@ -200,7 +239,9 @@ def test_array_lnprobability(self): ln_probs = np.log(np.arange(1, N + 2) / np.sum(weights)) ln_probs[-1] = -np.inf - self.assertTrue(np.all(categorical_prior.ln_prob(test_cases) == ln_probs[test_cases])) + new = categorical_prior.ln_prob(self.xp.array(test_cases)) + self.assertTrue(np.all(new == ln_probs[test_cases])) + self.assertEqual(new.__array_namespace__(), self.xp) def test_cdf(self): """ @@ -213,11 +254,12 @@ def test_cdf(self): categorical_prior = bilby.core.prior.WeightedCategorical(N, weights=weights) sample = categorical_prior.sample(size=10) - original = np.asarray(sample) - new = np.array(categorical_prior.rescale( + original = self.xp.asarray(sample) + new = self.xp.array(categorical_prior.rescale( categorical_prior.cdf(sample) )) np.testing.assert_array_equal(original, new) + self.assertEqual(type(new), type(original)) if __name__ == "__main__": diff --git a/test/core/prior/base_test.py b/test/core/prior/base_test.py index c9b788732..d83c3edd3 100644 --- a/test/core/prior/base_test.py +++ b/test/core/prior/base_test.py @@ -2,6 +2,7 @@ from unittest.mock import Mock import numpy as np +import pytest import bilby @@ -56,7 +57,7 @@ def test_base_prob(self): self.assertTrue(np.isnan(self.prior.prob(5))) def test_base_ln_prob(self): - self.prior.prob = lambda val: val + self.prior.prob = lambda val, *, xp=None: val self.assertEqual(np.log(5), self.prior.ln_prob(5)) def test_is_in_prior(self): @@ -139,6 +140,8 @@ def test_prob_inside(self): self.assertEqual(1, self.prior.prob(0.5)) +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestConstraintPriorNormalisation(unittest.TestCase): def setUp(self): self.priors = dict( @@ -154,7 +157,7 @@ def conversion_func(parameters): def test_prob_integrate_to_one(self): keys = ["a", "b", "c"] n_samples = 1000000 - samples = self.priors.sample_subset(keys=keys, size=n_samples) + samples = self.priors.sample_subset(keys=keys, size=n_samples, xp=self.xp) prob = self.priors.prob(samples, axis=0) dm1 = self.priors["a"].maximum - self.priors["a"].minimum dm2 = self.priors["b"].maximum - self.priors["b"].minimum @@ -169,5 +172,24 @@ def test_prob_integrate_to_one(self): self.assertAlmostEqual(1, integral, delta=7 * sigma_integral) +class TestPriorSubclassWithoutXpWarning(unittest.TestCase): + def test_custom_subclass_without_xp_issues_warning(self): + """Test that a custom prior subclass without xp parameter in rescale method issues a warning.""" + with pytest.warns( + DeprecationWarning, + match=r"rescale.*CustomPriorWithoutXp.*xp.*keyword argument", + ): + # Define a custom prior subclass that doesn't include xp in rescale method + class CustomPriorWithoutXp(bilby.core.prior.Prior): + def rescale(self, val): + """Custom rescale without xp parameter""" + return val * 2 + + prior = CustomPriorWithoutXp(name="custom_prior") + import jax.numpy as jnp + rescaled = prior.rescale(jnp.array([0.1, 0.2, 3])) + self.assertEqual(rescaled.__array_namespace__(), jnp) + + if __name__ == "__main__": unittest.main() diff --git a/test/core/prior/conditional_test.py b/test/core/prior/conditional_test.py index e7e5ec670..cafa0f73e 100644 --- a/test/core/prior/conditional_test.py +++ b/test/core/prior/conditional_test.py @@ -6,6 +6,7 @@ import numpy as np import pandas as pd import pickle +import pytest import bilby @@ -172,6 +173,8 @@ def test_cond_prior_instantiation_no_boundary_prior(self): self.assertIsNone(prior.boundary) +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestConditionalPriorDict(unittest.TestCase): def setUp(self): def condition_func_1(reference_parameters, var_0): @@ -208,7 +211,12 @@ def condition_func_3(reference_parameters, var_1, var_2): self.conditional_priors_manually_set_items = ( bilby.core.prior.ConditionalPriorDict() ) - self.test_sample = dict(var_0=0.7, var_1=0.6, var_2=0.5, var_3=0.4) + self.test_sample = dict( + var_0=self.xp.array(0.7), + var_1=self.xp.array(0.6), + var_2=self.xp.array(0.5), + var_3=self.xp.array(0.4), + ) self.test_value = 1 / np.prod([self.test_sample[f"var_{ii}"] for ii in range(3)]) for key, value in dict( var_0=self.prior_0, @@ -260,12 +268,14 @@ def test_conditional_keys_setting_items(self): ) def test_prob(self): - self.assertEqual(self.test_value, self.conditional_priors.prob(sample=self.test_sample)) + prob = self.conditional_priors.prob(sample=self.test_sample) + self.assertEqual(self.test_value, prob) + self.assertEqual(prob.__array_namespace__(), self.xp) def test_prob_illegal_conditions(self): del self.conditional_priors["var_0"] with self.assertRaises(bilby.core.prior.IllegalConditionsException): - self.conditional_priors.prob(sample=self.test_sample) + self.conditional_priors.prob(sample=self.test_sample, xp=self.xp) def test_ln_prob(self): self.assertEqual(np.log(self.test_value), self.conditional_priors.ln_prob(sample=self.test_sample)) @@ -356,7 +366,7 @@ def test_rescale_with_joint_prior(self): res = priordict.rescale(keys=keys, theta=ref_variables) self.assertEqual(np.shape(res), (6,)) - self.assertListEqual([isinstance(r, float) for r in res], 6 * [True]) + self.assertEqual(res.__array_namespace__(), self.xp) # check conditional values are still as expected expected = [self.test_sample["var_0"]] @@ -447,6 +457,8 @@ def _tp_conditional_uniform(ref_params, period): prior.sample_subset(["tp"], 1000) +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestDirichletPrior(unittest.TestCase): def setUp(self): @@ -456,6 +468,10 @@ def tearDown(self): if os.path.isdir("priors"): shutil.rmtree("priors") + def test_samples_correct_type(self): + samples = self.priors.sample(10, xp=self.xp) + self.assertEqual(samples["dirichlet_1"].__array_namespace__(), self.xp) + def test_samples_sum_to_less_than_one(self): """ Test that the samples sum to less than one as required for the diff --git a/test/core/prior/dict_test.py b/test/core/prior/dict_test.py index 089611aee..0e0c871e2 100644 --- a/test/core/prior/dict_test.py +++ b/test/core/prior/dict_test.py @@ -3,6 +3,7 @@ from unittest.mock import Mock, patch import numpy as np +import pytest import bilby @@ -22,6 +23,8 @@ def __init__(self, names, bounds=None): setattr(bilby.core.prior, "FakeJointPriorDist", FakeJointPriorDist) +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestPriorDict(unittest.TestCase): def setUp(self): @@ -268,30 +271,40 @@ def test_dict_argument_is_not_string_or_dict(self): def test_sample_subset_correct_size(self): size = 7 samples = self.prior_set_from_dict.sample_subset( - keys=self.prior_set_from_dict.keys(), size=size + keys=self.prior_set_from_dict.keys(), size=size, + xp=self.xp, ) self.assertEqual(len(self.prior_set_from_dict), len(samples)) for key in samples: self.assertEqual(size, len(samples[key])) + self.assertEqual(samples[key].__array_namespace__(), self.xp) def test_sample_subset_correct_size_when_non_priors_in_dict(self): self.prior_set_from_dict["asdf"] = "not_a_prior" samples = self.prior_set_from_dict.sample_subset( - keys=self.prior_set_from_dict.keys() + keys=self.prior_set_from_dict.keys(), + xp=self.xp, ) self.assertEqual(len(self.prior_set_from_dict) - 1, len(samples)) + for key in samples: + self.assertIsNotNone(samples[key].__array_namespace__(), self.xp) def test_sample_subset_with_actual_subset(self): size = 3 - samples = self.prior_set_from_dict.sample_subset(keys=["length"], size=size) - expected = dict(length=np.array([42.0, 42.0, 42.0])) + samples = self.prior_set_from_dict.sample_subset( + keys=["length"], size=size, xp=self.xp + ) + expected = dict(length=self.xp.array([42.0, 42.0, 42.0])) self.assertTrue(np.array_equal(expected["length"], samples["length"])) + self.assertEqual(samples["length"].__array_namespace__(), self.xp) def test_sample_subset_constrained_as_array(self): size = 3 keys = ["mass", "speed"] - out = self.prior_set_from_dict.sample_subset_constrained_as_array(keys, size) - self.assertTrue(isinstance(out, np.ndarray)) + out = self.prior_set_from_dict.sample_subset_constrained_as_array( + keys, size, xp=self.xp + ) + self.assertEqual(out.__array_namespace__(), self.xp) self.assertTrue(out.shape == (len(keys), size)) def test_sample_subset_constrained(self): @@ -312,7 +325,7 @@ def conversion_function(parameters): with patch("bilby.core.prior.logger.warning") as mock_warning: samples1 = priors1.sample_subset_constrained( - keys=list(priors1.keys()), size=N + keys=list(priors1.keys()), size=N, xp=self.xp ) self.assertEqual(len(priors1) - 1, len(samples1)) for key in samples1: @@ -325,7 +338,7 @@ def conversion_function(parameters): with patch("bilby.core.prior.logger.warning") as mock_warning: samples2 = priors2.sample_subset_constrained( - keys=list(priors2.keys()), size=N + keys=list(priors2.keys()), size=N, xp=self.xp ) self.assertEqual(len(priors2), len(samples2)) for key in samples2: @@ -336,27 +349,31 @@ def test_sample(self): size = 7 bilby.core.utils.random.seed(42) samples1 = self.prior_set_from_dict.sample_subset( - keys=self.prior_set_from_dict.keys(), size=size + keys=self.prior_set_from_dict.keys(), size=size, xp=self.xp ) bilby.core.utils.random.seed(42) - samples2 = self.prior_set_from_dict.sample(size=size) + samples2 = self.prior_set_from_dict.sample(size=size, xp=self.xp) self.assertEqual(set(samples1.keys()), set(samples2.keys())) for key in samples1: self.assertTrue(np.array_equal(samples1[key], samples2[key])) - + self.assertEqual(samples1[key].__array_namespace__(), self.xp) + self.assertEqual(samples2[key].__array_namespace__(), self.xp) + def test_prob(self): - samples = self.prior_set_from_dict.sample_subset(keys=["mass", "speed"]) + samples = self.prior_set_from_dict.sample_subset(keys=["mass", "speed"], xp=self.xp) expected = self.first_prior.prob(samples["mass"]) * self.second_prior.prob( samples["speed"] ) self.assertEqual(expected, self.prior_set_from_dict.prob(samples)) + self.assertEqual(expected.__array_namespace__(), self.xp) def test_ln_prob(self): - samples = self.prior_set_from_dict.sample_subset(keys=["mass", "speed"]) + samples = self.prior_set_from_dict.sample_subset(keys=["mass", "speed"], xp=self.xp) expected = self.first_prior.ln_prob( samples["mass"] ) + self.second_prior.ln_prob(samples["speed"]) self.assertEqual(expected, self.prior_set_from_dict.ln_prob(samples)) + self.assertEqual(expected.__array_namespace__(), self.xp) def test_rescale(self): theta = [0.5, 0.5, 0.5] @@ -380,13 +397,14 @@ def test_cdf(self): Note that the format of inputs/outputs is different between the two methods. """ - sample = self.prior_set_from_dict.sample() - original = np.array(list(sample.values())) - new = np.array(self.prior_set_from_dict.rescale( + sample = self.prior_set_from_dict.sample(xp=self.xp) + original = self.xp.array(list(sample.values())) + new = self.xp.array(self.prior_set_from_dict.rescale( sample.keys(), self.prior_set_from_dict.cdf(sample=sample).values() )) self.assertLess(max(abs(original - new)), 1e-10) + self.assertEqual(new.__array_namespace__(), self.xp) def test_redundancy(self): for key in self.prior_set_from_dict.keys(): diff --git a/test/core/prior/prior_test.py b/test/core/prior/prior_test.py index bed42cf19..c3fa1e865 100644 --- a/test/core/prior/prior_test.py +++ b/test/core/prior/prior_test.py @@ -2,6 +2,7 @@ import unittest import numpy as np import os +import pytest import scipy.stats as ss from scipy.integrate import trapezoid @@ -26,6 +27,8 @@ ) +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestPriorClasses(unittest.TestCase): def setUp(self): # set multivariate Gaussian @@ -265,24 +268,27 @@ def test_minimum_rescaling(self): # and so the rescale function doesn't quite return the lower bound continue elif bilby.core.prior.JointPrior in prior.__class__.__mro__: - minimum_sample = prior.rescale(0) + minimum_sample = prior.rescale(self.xp.array(0)) if prior.dist.filled_rescale(): self.assertAlmostEqual(minimum_sample[0], prior.minimum) self.assertAlmostEqual(minimum_sample[1], prior.minimum) else: - minimum_sample = prior.rescale(0) + minimum_sample = prior.rescale(self.xp.array(0)) self.assertAlmostEqual(minimum_sample, prior.minimum) def test_maximum_rescaling(self): """Test the the rescaling works as expected.""" for prior in self.priors: if bilby.core.prior.JointPrior in prior.__class__.__mro__: - maximum_sample = prior.rescale(0) + maximum_sample = prior.rescale(self.xp.array(0)) if prior.dist.filled_rescale(): self.assertAlmostEqual(maximum_sample[0], prior.maximum) self.assertAlmostEqual(maximum_sample[1], prior.maximum) + elif isinstance(prior, bilby.gw.prior.AlignedSpin): + maximum_sample = prior.rescale(self.xp.array(1)) + self.assertGreater(maximum_sample, 0.997) else: - maximum_sample = prior.rescale(1) + maximum_sample = prior.rescale(self.xp.array(1)) self.assertAlmostEqual(maximum_sample, prior.maximum) def test_many_sample_rescaling(self): @@ -291,20 +297,22 @@ def test_many_sample_rescaling(self): if isinstance(prior, bilby.core.prior.analytical.SymmetricLogUniform): # SymmetricLogUniform has support down to -maximum continue - many_samples = prior.rescale(np.random.uniform(0, 1, 1000)) + many_samples = prior.rescale(self.xp.array(np.random.uniform(0, 1, 1000))) if bilby.core.prior.JointPrior in prior.__class__.__mro__: if not prior.dist.filled_rescale(): continue self.assertTrue( all((many_samples >= prior.minimum) & (many_samples <= prior.maximum)) ) + self.assertEqual(many_samples.__array_namespace__(), self.xp) def test_least_recently_sampled(self): for prior in self.priors: - least_recently_sampled_expected = prior.sample() + least_recently_sampled_expected = prior.sample(xp=self.xp) self.assertEqual( least_recently_sampled_expected, prior.least_recently_sampled ) + self.assertEqual(least_recently_sampled_expected.__array_namespace__(), self.xp) def test_sampling_single(self): """Test that sampling from the prior always returns values within its domain.""" @@ -312,10 +320,11 @@ def test_sampling_single(self): if isinstance(prior, bilby.core.prior.analytical.SymmetricLogUniform): # SymmetricLogUniform has support down to -maximum continue - single_sample = prior.sample() + single_sample = prior.sample(xp=self.xp) self.assertTrue( (single_sample >= prior.minimum) & (single_sample <= prior.maximum) ) + self.assertEqual(single_sample.__array_namespace__(), self.xp) def test_sampling_many(self): """Test that sampling from the prior always returns values within its domain.""" @@ -323,17 +332,18 @@ def test_sampling_many(self): if isinstance(prior, bilby.core.prior.analytical.SymmetricLogUniform): # SymmetricLogUniform has support down to -maximum continue - many_samples = prior.sample(5000) + many_samples = prior.sample(5000, xp=self.xp) self.assertTrue( (all(many_samples >= prior.minimum)) & (all(many_samples <= prior.maximum)) ) + self.assertEqual(many_samples.__array_namespace__(), self.xp) def test_probability_above_domain(self): """Test that the prior probability is non-negative in domain of validity and zero outside.""" for prior in self.priors: if prior.maximum != np.inf: - outside_domain = np.linspace( + outside_domain = self.xp.linspace( prior.maximum + 1, prior.maximum + 1e4, 1000 ) if bilby.core.prior.JointPrior in prior.__class__.__mro__: @@ -349,7 +359,7 @@ def test_probability_below_domain(self): # SymmetricLogUniform has support down to -maximum continue if prior.minimum != -np.inf: - outside_domain = np.linspace( + outside_domain = self.xp.linspace( prior.minimum - 1e4, prior.minimum - 1, 1000 ) if bilby.core.prior.JointPrior in prior.__class__.__mro__: @@ -360,31 +370,39 @@ def test_probability_below_domain(self): def test_least_recently_sampled_2(self): for prior in self.priors: - lrs = prior.sample() + lrs = prior.sample(xp=self.xp) self.assertEqual(lrs, prior.least_recently_sampled) + self.assertEqual(lrs.__array_namespace__(), self.xp) def test_prob_and_ln_prob(self): for prior in self.priors: - sample = prior.sample() + sample = prior.sample(xp=self.xp) if not bilby.core.prior.JointPrior in prior.__class__.__mro__: # noqa # due to the way that the Multivariate Gaussian prior must sequentially call # the prob and ln_prob functions, it must be ignored in this test. + lnprob = prior.ln_prob(sample) + prob = prior.prob(sample) + # lower precision for jax running tests with float32 self.assertAlmostEqual( - np.log(prior.prob(sample)), prior.ln_prob(sample), 12 + self.xp.log(prob), lnprob, 6 ) + self.assertEqual(lnprob.__array_namespace__(), self.xp) + self.assertEqual(prob.__array_namespace__(), self.xp) def test_many_prob_and_many_ln_prob(self): for prior in self.priors: - samples = prior.sample(10) + samples = prior.sample(10, xp=self.xp) if not bilby.core.prior.JointPrior in prior.__class__.__mro__: # noqa ln_probs = prior.ln_prob(samples) probs = prior.prob(samples) for sample, logp, p in zip(samples, ln_probs, probs): self.assertAlmostEqual(prior.ln_prob(sample), logp) self.assertAlmostEqual(prior.prob(sample), p) + self.assertEqual(ln_probs.__array_namespace__(), self.xp) + self.assertEqual(probs.__array_namespace__(), self.xp) def test_cdf_is_inverse_of_rescaling(self): - domain = np.linspace(0, 1, 100) + domain = self.xp.linspace(0, 1, 100) threshold = 1e-9 for prior in self.priors: if ( @@ -392,6 +410,9 @@ def test_cdf_is_inverse_of_rescaling(self): or bilby.core.prior.JointPrior in prior.__class__.__mro__ ): continue + elif isinstance(prior, bilby.core.prior.StudentT) and "jax" in str(self.xp): + # JAX implementation of StudentT prior rescale is not accurate enough + continue elif isinstance(prior, bilby.core.prior.WeightedDiscreteValues): rescaled = prior.rescale(domain) cdf_vals = prior.cdf(rescaled) @@ -399,15 +420,18 @@ def test_cdf_is_inverse_of_rescaling(self): cdf_vals_2 = prior.cdf(rescaled_2) self.assertTrue(np.array_equal(rescaled, rescaled_2)) max_difference = max(np.abs(cdf_vals - cdf_vals_2)) + for arr in [rescaled, rescaled_2, cdf_vals, cdf_vals_2]: + self.assertEqual(arr.__array_namespace__(), self.xp) else: rescaled = prior.rescale(domain) max_difference = max(np.abs(domain - prior.cdf(rescaled))) + self.assertEqual(rescaled.__array_namespace__(), self.xp) self.assertLess(max_difference, threshold) def test_cdf_one_above_domain(self): for prior in self.priors: if prior.maximum != np.inf: - outside_domain = np.linspace( + outside_domain = self.xp.linspace( prior.maximum + 1, prior.maximum + 1e4, 1000 ) self.assertTrue(all(prior.cdf(outside_domain) == 1)) @@ -423,7 +447,7 @@ def test_cdf_zero_below_domain(self): ): continue if prior.minimum != -np.inf: - outside_domain = np.linspace( + outside_domain = self.xp.linspace( prior.minimum - 1e4, prior.minimum - 1, 1000 ) self.assertTrue(all(np.nan_to_num(prior.cdf(outside_domain)) == 0)) @@ -578,7 +602,7 @@ def test_probability_in_domain(self): maximum = 1e5 else: maximum = prior.maximum - domain = np.linspace(minimum, maximum, 1000) + domain = self.xp.linspace(minimum, maximum, 1000) self.assertTrue(all(prior.prob(domain) >= 0)) def test_probability_surrounding_domain(self): @@ -591,7 +615,7 @@ def test_probability_surrounding_domain(self): # SymmetricLogUniform has support down to -maximum continue with np.errstate(invalid="ignore"): - surround_domain = np.linspace(prior.minimum - 1, prior.maximum + 1, 1000) + surround_domain = self.xp.linspace(prior.minimum - 1, prior.maximum + 1, 1000) indomain = (surround_domain >= prior.minimum) | ( surround_domain <= prior.maximum ) @@ -645,11 +669,13 @@ def test_normalized(self): domain = np.linspace(prior.minimum, prior.maximum, 10000) elif isinstance(prior, bilby.core.prior.WeightedDiscreteValues): domain = prior.values - self.assertTrue(np.sum(prior.prob(domain)) == 1) + self.assertTrue(np.sum(prior.prob(self.xp.array(domain))) == 1) continue else: domain = np.linspace(prior.minimum, prior.maximum, 1000) - self.assertAlmostEqual(trapezoid(prior.prob(domain), domain), 1, 3) + probs = prior.prob(self.xp.array(domain)) + self.assertAlmostEqual(trapezoid(np.array(probs), domain), 1, 3) + self.assertEqual(probs.__array_namespace__(), self.xp) def test_accuracy(self): """Test that each of the priors' functions is calculated accurately, as compared to scipy's calculations""" @@ -744,11 +770,15 @@ def test_accuracy(self): bilby.core.prior.WeightedDiscreteValues, ) if isinstance(prior, (testTuple)): - np.testing.assert_almost_equal(prior.prob(domain), scipy_prob) - np.testing.assert_almost_equal(prior.ln_prob(domain), scipy_lnprob) - np.testing.assert_almost_equal(prior.cdf(domain), scipy_cdf) + print(prior) + np.testing.assert_almost_equal(prior.prob(self.xp.array(domain)), scipy_prob) + np.testing.assert_almost_equal(prior.ln_prob(self.xp.array(domain)), scipy_lnprob) + np.testing.assert_almost_equal(prior.cdf(self.xp.array(domain)), scipy_cdf) + if isinstance(prior, bilby.core.prior.StudentT) and "jax" in str(self.xp): + # JAX implementation of StudentT prior rescale is not accurate enough + continue np.testing.assert_almost_equal( - prior.rescale(rescale_domain), scipy_rescale + prior.rescale(self.xp.array(rescale_domain)), scipy_rescale ) def test_unit_setting(self): @@ -833,7 +863,7 @@ def test_set_maximum_setting(self): ): continue prior.maximum = (prior.maximum + prior.minimum) / 2 - self.assertTrue(max(prior.sample(10000)) < prior.maximum) + self.assertTrue(max(prior.sample(10000, xp=self.xp)) < prior.maximum) def test_set_minimum_setting(self): for prior in self.priors: @@ -859,25 +889,25 @@ def test_set_minimum_setting(self): ): continue prior.minimum = (prior.maximum + prior.minimum) / 2 - self.assertTrue(min(prior.sample(10000)) > prior.minimum) - - def test_jax_methods(self): - import jax - - points = jax.numpy.linspace(1e-3, 1 - 1e-3, 10) - for prior in self.priors: - if bilby.core.prior.JointPrior in prior.__class__.__mro__: - continue - scaled = prior.rescale(points) - assert isinstance(scaled, jax.Array) - if isinstance(prior, bilby.core.prior.DeltaFunction): - continue - probs = prior.prob(scaled) - assert min(probs) > 0 - assert max(abs(jax.numpy.log(probs) - prior.ln_prob(scaled))) < 1e-6 - if isinstance(prior, bilby.core.prior.WeightedDiscreteValues): - continue - assert max(abs(prior.cdf(scaled) - points)) < 1e-6 + self.assertTrue(min(prior.sample(10000, xp=self.xp)) > prior.minimum) + + # def test_jax_methods(self): + # import jax + + # points = jax.numpy.linspace(1e-3, 1 - 1e-3, 10) + # for prior in self.priors: + # if bilby.core.prior.JointPrior in prior.__class__.__mro__: + # continue + # scaled = prior.rescale(points) + # assert isinstance(scaled, jax.Array) + # if isinstance(prior, bilby.core.prior.DeltaFunction): + # continue + # probs = prior.prob(scaled) + # assert min(probs) > 0 + # assert max(abs(jax.numpy.log(probs) - prior.ln_prob(scaled))) < 1e-6 + # if isinstance(prior, bilby.core.prior.WeightedDiscreteValues): + # continue + # assert max(abs(prior.cdf(scaled) - points)) < 1e-6 if __name__ == "__main__": diff --git a/test/core/prior/slabspike_test.py b/test/core/prior/slabspike_test.py index 8cb2fcf1d..501f3e39b 100644 --- a/test/core/prior/slabspike_test.py +++ b/test/core/prior/slabspike_test.py @@ -1,6 +1,9 @@ -import numpy as np import unittest +import array_api_compat as aac +import numpy as np +import pytest + import bilby from bilby.core.prior.slabspike import SlabSpikePrior from bilby.core.prior.analytical import Uniform, PowerLaw, LogUniform, TruncatedGaussian, \ @@ -60,12 +63,14 @@ def test_set_spike_height_domain_edge(self): self.prior.spike_height = 1 +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestSlabSpikeClasses(unittest.TestCase): def setUp(self): self.minimum = 0.4 self.maximum = 2.4 - self.spike_loc = 1.5 + self.spike_loc = self.xp.array(1.5) self.spike_height = 0.3 self.slabs = [ @@ -80,15 +85,17 @@ def setUp(self): HalfGaussian(sigma=1), LogNormal(mu=1, sigma=2), Exponential(mu=2), - StudentT(df=2), Logistic(mu=2, scale=1), Cauchy(alpha=1, beta=2), Gamma(k=1, theta=1.), - ChiSquared(nu=2)] + ChiSquared(nu=2), + ] + if not aac.is_jax_namespace(self.xp): + StudentT(df=2), self.slab_spikes = [SlabSpikePrior(slab, spike_height=self.spike_height, spike_location=self.spike_loc) for slab in self.slabs] - self.test_nodes_finite_support = np.linspace(self.minimum, self.maximum, 1000) - self.test_nodes_infinite_support = np.linspace(-10, 10, 1000) + self.test_nodes_finite_support = self.xp.linspace(self.minimum, self.maximum, 1000) + self.test_nodes_infinite_support = self.xp.linspace(-10, 10, 1000) self.test_nodes = [self.test_nodes_finite_support if np.isinf(slab.minimum) or np.isinf(slab.maximum) else self.test_nodes_finite_support for slab in self.slabs] @@ -102,24 +109,12 @@ def tearDown(self): del self.test_nodes_finite_support del self.test_nodes_infinite_support - def test_jax_methods(self): - import jax - - points = jax.numpy.linspace(1e-3, 1 - 1e-3, 10) - for prior in self.slab_spikes: - scaled = prior.rescale(points) - assert isinstance(scaled, jax.Array) - if isinstance(prior, bilby.core.prior.DeltaFunction): - continue - probs = prior.prob(scaled) - assert min(probs) > 0 - assert max(abs(jax.numpy.log(probs) - prior.ln_prob(scaled))) < 1e-6 - def test_prob_on_slab(self): for slab, slab_spike, test_nodes in zip(self.slabs, self.slab_spikes, self.test_nodes): expected = slab.prob(test_nodes) * slab_spike.slab_fraction actual = slab_spike.prob(test_nodes) self.assertTrue(np.allclose(expected, actual, rtol=1e-5)) + self.assertEqual(expected.__array_namespace__(), self.xp) def test_prob_on_spike(self): for slab_spike in self.slab_spikes: @@ -130,10 +125,13 @@ def test_ln_prob_on_slab(self): expected = slab.ln_prob(test_nodes) + np.log(slab_spike.slab_fraction) actual = slab_spike.ln_prob(test_nodes) self.assertTrue(np.array_equal(expected, actual)) + self.assertEqual(expected.__array_namespace__(), self.xp) def test_ln_prob_on_spike(self): for slab_spike in self.slab_spikes: - self.assertEqual(np.inf, slab_spike.ln_prob(self.spike_loc)) + expected = slab_spike.ln_prob(self.spike_loc) + self.assertEqual(np.inf, expected) + self.assertEqual(expected.__array_namespace__(), self.xp) def test_inverse_cdf_below_spike_with_spike_at_minimum(self): for slab in self.slabs: @@ -156,19 +154,22 @@ def test_cdf_below_spike(self): expected = slab.cdf(test_nodes) * slab_spike.slab_fraction actual = slab_spike.cdf(test_nodes) self.assertTrue(np.allclose(expected, actual, rtol=1e-5)) + self.assertEqual(expected.__array_namespace__(), self.xp) def test_cdf_at_spike(self): for slab, slab_spike in zip(self.slabs, self.slab_spikes): expected = slab.cdf(self.spike_loc) * slab_spike.slab_fraction actual = slab_spike.cdf(self.spike_loc) self.assertTrue(np.allclose(expected, actual, rtol=1e-5)) + self.assertEqual(expected.__array_namespace__(), self.xp) def test_cdf_above_spike(self): for slab, slab_spike, test_nodes in zip(self.slabs, self.slab_spikes, self.test_nodes): test_nodes = test_nodes[np.where(test_nodes > self.spike_loc)] expected = slab.cdf(test_nodes) * slab_spike.slab_fraction + self.spike_height actual = slab_spike.cdf(test_nodes) - self.assertTrue(np.array_equal(expected, actual)) + np.testing.assert_allclose(expected, actual, rtol=1e-12) + self.assertEqual(expected.__array_namespace__(), self.xp) def test_cdf_at_minimum(self): for slab_spike in self.slab_spikes: @@ -185,31 +186,36 @@ def test_cdf_at_maximum(self): def test_rescale_no_spike(self): for slab in self.slabs: slab_spike = SlabSpikePrior(slab=slab, spike_height=0, spike_location=slab.minimum) - vals = np.linspace(0, 1, 1000) + vals = self.xp.linspace(0, 1, 1000) expected = slab.rescale(vals) actual = slab_spike.rescale(vals) - print(slab) self.assertTrue(np.allclose(expected, actual, rtol=1e-5)) + self.assertEqual(expected.__array_namespace__(), self.xp) def test_rescale_below_spike(self): for slab, slab_spike in zip(self.slabs, self.slab_spikes): - vals = np.linspace(0, slab_spike.inverse_cdf_below_spike, 1000) + vals = self.xp.linspace(0, slab_spike.inverse_cdf_below_spike, 1000) expected = slab.rescale(vals / slab_spike.slab_fraction) actual = slab_spike.rescale(vals) self.assertTrue(np.allclose(expected, actual, rtol=1e-5)) + self.assertEqual(expected.__array_namespace__(), self.xp) def test_rescale_at_spike(self): for slab, slab_spike in zip(self.slabs, self.slab_spikes): - vals = np.linspace(slab_spike.inverse_cdf_below_spike, - slab_spike.inverse_cdf_below_spike + slab_spike.spike_height, 1000) - expected = np.ones(len(vals)) * slab.rescale(vals[0] / slab_spike.slab_fraction) + vals = self.xp.linspace( + slab_spike.inverse_cdf_below_spike, + slab_spike.inverse_cdf_below_spike + slab_spike.spike_height, 1000 + ) + expected = self.xp.ones(len(vals)) * slab.rescale(vals[0] / slab_spike.slab_fraction) actual = slab_spike.rescale(vals) self.assertTrue(np.allclose(expected, actual, rtol=1e-5)) + self.assertEqual(expected.__array_namespace__(), self.xp) def test_rescale_above_spike(self): for slab, slab_spike in zip(self.slabs, self.slab_spikes): - vals = np.linspace(slab_spike.inverse_cdf_below_spike + self.spike_height, 1, 1000) - expected = np.ones(len(vals)) * slab.rescale( + vals = self.xp.linspace(slab_spike.inverse_cdf_below_spike + self.spike_height, 1, 1000) + expected = self.xp.ones(len(vals)) * slab.rescale( (vals - self.spike_height) / slab_spike.slab_fraction) actual = slab_spike.rescale(vals) self.assertTrue(np.allclose(expected, actual, rtol=1e-5)) + self.assertEqual(expected.__array_namespace__(), self.xp) diff --git a/test/core/result_test.py b/test/core/result_test.py index b8c1106a0..702ac23cc 100644 --- a/test/core/result_test.py +++ b/test/core/result_test.py @@ -13,6 +13,8 @@ from bilby.core.utils import logger +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestJson(unittest.TestCase): def setUp(self): @@ -28,12 +30,12 @@ def test_list_encoding(self): self.assertTrue(np.all(data["x"] == decoded["x"])) def test_array_encoding(self): - data = dict(x=np.array([1, 2, 3.4])) + data = dict(x=self.xp.array([1, 2, 3.4])) encoded = json.dumps(data, cls=self.encoder) decoded = json.loads(encoded, object_hook=self.decoder) self.assertEqual(data.keys(), decoded.keys()) self.assertEqual(type(data["x"]), type(decoded["x"])) - self.assertTrue(np.all(data["x"] == decoded["x"])) + self.assertTrue(self.xp.all(data["x"] == decoded["x"])) def test_complex_encoding(self): data = dict(x=1 + 3j) @@ -918,6 +920,8 @@ def test_reweight_different_likelihood_weights_correct(self): self.assertNotEqual(new.log_evidence, self.result.log_evidence) +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestResultSaveAndRead(unittest.TestCase): @pytest.fixture(autouse=True) @@ -943,7 +947,11 @@ def setUp(self): search_parameter_keys=["x", "y"], fixed_parameter_keys=["c", "d"], priors=priors, - sampler_kwargs=dict(test="test", func=lambda x: x), + sampler_kwargs=dict( + test="test", + func=lambda x: x, + some_array=self.xp.ones((5, 5)), + ), injection_parameters=dict(x=0.5, y=0.5), meta_data=dict(test="test"), sampling_time=100.0, diff --git a/test/gw/prior_test.py b/test/gw/prior_test.py index 2d35986cf..022bc0a56 100644 --- a/test/gw/prior_test.py +++ b/test/gw/prior_test.py @@ -10,6 +10,7 @@ from scipy.stats import ks_2samp import matplotlib.pyplot as plt import pandas as pd +import pytest import bilby from bilby.core.prior import Uniform, Constraint @@ -557,14 +558,16 @@ def test_luminosity_distance_to_comoving_distance(self): self.assertEqual(new_prior.name, "comoving_distance") +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestAlignedSpin(unittest.TestCase): def setUp(self): pass def test_default_prior_matches_analytic(self): prior = bilby.gw.prior.AlignedSpin() - chis = np.linspace(-1, 1, 20) - analytic = -np.log(np.abs(chis)) / 2 + chis = self.xp.linspace(-1, 1, 20) + analytic = -self.xp.log(self.xp.abs(chis)) / 2 max_difference = max(abs(analytic - prior.prob(chis))) self.assertAlmostEqual(max_difference, 0, 2) @@ -572,12 +575,15 @@ def test_non_analytic_form_has_correct_statistics(self): a_prior = bilby.core.prior.TruncatedGaussian(mu=0, sigma=0.1, minimum=0, maximum=1) z_prior = bilby.core.prior.TruncatedGaussian(mu=0.4, sigma=0.2, minimum=-1, maximum=1) chi_prior = bilby.gw.prior.AlignedSpin(a_prior, z_prior) - chis = chi_prior.sample(100000) - alts = a_prior.sample(100000) * z_prior.sample(100000) + chis = chi_prior.sample(100000, xp=self.xp) + alts = a_prior.sample(100000, xp=self.xp) * z_prior.sample(100000, xp=self.xp) self.assertAlmostEqual(np.mean(chis), np.mean(alts), 2) self.assertAlmostEqual(np.std(chis), np.std(alts), 2) + self.assertEqual(chis.__array_namespace__(), self.xp) +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestConditionalChiUniformSpinMagnitude(unittest.TestCase): def setUp(self): @@ -588,9 +594,10 @@ def test_marginalized_prior_is_uniform(self): priors["a_1"] = bilby.gw.prior.ConditionalChiUniformSpinMagnitude( minimum=0.1, maximum=priors["chi_1"].maximum, name="a_1" ) - samples = priors.sample(100000)["a_1"] + samples = priors.sample(100000, xp=self.xp)["a_1"] ks = ks_2samp(samples, np.random.uniform(0, priors["chi_1"].maximum, 100000)) self.assertTrue(ks.pvalue > 0.001) + self.assertEqual(samples.__array_namespace__(), self.xp) if __name__ == "__main__": From d9461be1af3b3d969ac50cd21749d9662d3f0ca7 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Sun, 1 Feb 2026 22:14:41 -0500 Subject: [PATCH 076/140] FMT: precommit fixes --- bilby/compat/patches.py | 1 - bilby/core/prior/base.py | 1 - test/core/prior/base_test.py | 2 +- test/core/prior/dict_test.py | 2 +- 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/bilby/compat/patches.py b/bilby/compat/patches.py index 7c497a24e..db18c3974 100644 --- a/bilby/compat/patches.py +++ b/bilby/compat/patches.py @@ -1,5 +1,4 @@ import array_api_compat as aac -import numpy as np from .utils import BackendNotImplementedError diff --git a/bilby/core/prior/base.py b/bilby/core/prior/base.py index ea6ef5475..671b46233 100644 --- a/bilby/core/prior/base.py +++ b/bilby/core/prior/base.py @@ -14,7 +14,6 @@ decode_bilby_json, logger, get_dict_with_properties, - WrappedInterp1d as interp1d, ) from ...compat.utils import xp_wrap diff --git a/test/core/prior/base_test.py b/test/core/prior/base_test.py index d83c3edd3..21e857fa2 100644 --- a/test/core/prior/base_test.py +++ b/test/core/prior/base_test.py @@ -184,7 +184,7 @@ class CustomPriorWithoutXp(bilby.core.prior.Prior): def rescale(self, val): """Custom rescale without xp parameter""" return val * 2 - + prior = CustomPriorWithoutXp(name="custom_prior") import jax.numpy as jnp rescaled = prior.rescale(jnp.array([0.1, 0.2, 3])) diff --git a/test/core/prior/dict_test.py b/test/core/prior/dict_test.py index 0e0c871e2..3a9a2c52d 100644 --- a/test/core/prior/dict_test.py +++ b/test/core/prior/dict_test.py @@ -358,7 +358,7 @@ def test_sample(self): self.assertTrue(np.array_equal(samples1[key], samples2[key])) self.assertEqual(samples1[key].__array_namespace__(), self.xp) self.assertEqual(samples2[key].__array_namespace__(), self.xp) - + def test_prob(self): samples = self.prior_set_from_dict.sample_subset(keys=["mass", "speed"], xp=self.xp) expected = self.first_prior.prob(samples["mass"]) * self.second_prior.prob( From c5f2127c95d8eb45ff04a6cb39fdeedcb8abe93a Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Sun, 1 Feb 2026 22:24:27 -0500 Subject: [PATCH 077/140] TEST: fix jax tests --- bilby/compat/utils.py | 12 +++++++----- bilby/core/grid.py | 1 - bilby/core/prior/dict.py | 1 - bilby/core/prior/slabspike.py | 2 -- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/bilby/compat/utils.py b/bilby/compat/utils.py index 5e00f8969..34b819ab9 100644 --- a/bilby/compat/utils.py +++ b/bilby/compat/utils.py @@ -55,7 +55,7 @@ def xp_wrap(func, no_xp=False): no_xp: bool If True, the decorator will not attempt to add the 'xp' keyword argument and so the wrapper is a no-op. - + Returns ======= function @@ -65,13 +65,15 @@ def xp_wrap(func, no_xp=False): def wrapped(self, *args, xp=None, **kwargs): if not no_xp and xp is None: try: + # if the user specified the target arrays in kwargs + # we need to be able to support this if len(args) > 0: - array_module = array_namespace(*args) + xp = array_module(*args) elif len(kwargs) > 0: - array_module = array_namespace(*kwargs.values()) + xp = array_module(*kwargs.values()) else: - array_module = np - kwargs["xp"] = array_module + xp = np + kwargs["xp"] = xp except TypeError: kwargs["xp"] = np elif not no_xp: diff --git a/bilby/core/grid.py b/bilby/core/grid.py index 3e5ef1fc3..a69590080 100644 --- a/bilby/core/grid.py +++ b/bilby/core/grid.py @@ -330,7 +330,6 @@ def _evaluate(self): self._ln_likelihood = vmap(self.likelihood.log_likelihood)( {key: self.mesh_grid[i].flatten() for i, key in enumerate(self.parameter_names)} ).reshape(self.mesh_grid[0].shape) - print(type(self._ln_likelihood)) else: self._ln_likelihood = xp.empty(self.mesh_grid[0].shape) diff --git a/bilby/core/prior/dict.py b/bilby/core/prior/dict.py index 579a2050a..8b16a1510 100644 --- a/bilby/core/prior/dict.py +++ b/bilby/core/prior/dict.py @@ -822,7 +822,6 @@ def prob(self, sample, *, xp=None, **kwargs): """ self._prepare_evaluation(*zip(*sample.items())) - print(sample, xp) res = xp.asarray([ self[key].prob(sample[key], **self.get_required_variables(key), xp=xp) for key in sample diff --git a/bilby/core/prior/slabspike.py b/bilby/core/prior/slabspike.py index 30ca16639..23aed86d3 100644 --- a/bilby/core/prior/slabspike.py +++ b/bilby/core/prior/slabspike.py @@ -96,13 +96,11 @@ def rescale(self, val, *, xp=None): slab_scaled = self._contracted_rescale( val - self.spike_height * higher_indices, xp=xp ) - print(type(slab_scaled)) res = xp.select( [lower_indices | higher_indices, intermediate_indices], [slab_scaled, self.spike_location], ) - print(type(res)) return res @xp_wrap From fc97f62949ad3814af1e4e5e00e1b9ff632719a9 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Mon, 2 Feb 2026 05:48:14 -0500 Subject: [PATCH 078/140] TEST: add basic gw conversion jax tests --- test/conftest.py | 1 + test/core/prior/slabspike_test.py | 3 ++ test/gw/conversion_test.py | 46 +++++++++++++++++++++++-------- 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index 355c3074b..d0d1ad79b 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -15,6 +15,7 @@ def pytest_addoption(parser): def pytest_configure(config): config.addinivalue_line("markers", "requires_roqs: mark a test that requires ROQs") + config.addinivalue_line("markers", "array_backend: mark that a test uses all array backends") def pytest_collection_modifyitems(config, items): diff --git a/test/core/prior/slabspike_test.py b/test/core/prior/slabspike_test.py index 501f3e39b..53eb5ebee 100644 --- a/test/core/prior/slabspike_test.py +++ b/test/core/prior/slabspike_test.py @@ -150,6 +150,7 @@ def test_inverse_cdf_below_spike_arbitrary_position(self): def test_cdf_below_spike(self): for slab, slab_spike, test_nodes in zip(self.slabs, self.slab_spikes, self.test_nodes): + print(slab) test_nodes = test_nodes[np.where(test_nodes < self.spike_loc)] expected = slab.cdf(test_nodes) * slab_spike.slab_fraction actual = slab_spike.cdf(test_nodes) @@ -158,6 +159,7 @@ def test_cdf_below_spike(self): def test_cdf_at_spike(self): for slab, slab_spike in zip(self.slabs, self.slab_spikes): + print(slab) expected = slab.cdf(self.spike_loc) * slab_spike.slab_fraction actual = slab_spike.cdf(self.spike_loc) self.assertTrue(np.allclose(expected, actual, rtol=1e-5)) @@ -165,6 +167,7 @@ def test_cdf_at_spike(self): def test_cdf_above_spike(self): for slab, slab_spike, test_nodes in zip(self.slabs, self.slab_spikes, self.test_nodes): + print(slab) test_nodes = test_nodes[np.where(test_nodes > self.spike_loc)] expected = slab.cdf(test_nodes) * slab_spike.slab_fraction + self.spike_height actual = slab_spike.cdf(test_nodes) diff --git a/test/gw/conversion_test.py b/test/gw/conversion_test.py index fc0f4321a..386933e18 100644 --- a/test/gw/conversion_test.py +++ b/test/gw/conversion_test.py @@ -2,24 +2,27 @@ import numpy as np import pandas as pd +import pytest import bilby from bilby.gw import conversion +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestBasicConversions(unittest.TestCase): def setUp(self): - self.mass_1 = 1.4 - self.mass_2 = 1.3 - self.mass_ratio = 13 / 14 - self.total_mass = 2.7 - self.chirp_mass = (1.4 * 1.3) ** 0.6 / 2.7 ** 0.2 - self.symmetric_mass_ratio = (1.4 * 1.3) / 2.7 ** 2 - self.cos_angle = -1 - self.angle = np.pi - self.lambda_1 = 300 - self.lambda_2 = 300 * (14 / 13) ** 5 - self.lambda_tilde = ( + self.mass_1 = self.xp.array(1.4) + self.mass_2 = self.xp.array(1.3) + self.mass_ratio = self.xp.array(13 / 14) + self.total_mass = self.xp.array(2.7) + self.chirp_mass = (self.mass_1 * self.mass_2) ** 0.6 / self.total_mass ** 0.2 + self.symmetric_mass_ratio = (self.mass_1 * self.mass_2) / self.total_mass ** 2 + self.cos_angle = self.xp.array(-1.0) + self.angle = self.xp.pi + self.lambda_1 = self.xp.array(300.0) + self.lambda_2 = self.xp.array(300.0 * (14 / 13) ** 5) + self.lambda_tilde = self.xp.array( 8 / 13 * ( @@ -38,7 +41,7 @@ def setUp(self): * (self.lambda_1 - self.lambda_2) ) ) - self.delta_lambda_tilde = ( + self.delta_lambda_tilde = self.xp.array( 1 / 2 * ( @@ -74,30 +77,36 @@ def test_total_mass_and_mass_ratio_to_component_masses(self): self.assertTrue( all([abs(mass_1 - self.mass_1) < 1e-5, abs(mass_2 - self.mass_2) < 1e-5]) ) + self.assertEqual(mass_1.__array_namespace__(), self.xp) + self.assertEqual(mass_2.__array_namespace__(), self.xp) def test_chirp_mass_and_primary_mass_to_mass_ratio(self): mass_ratio = conversion.chirp_mass_and_primary_mass_to_mass_ratio( self.chirp_mass, self.mass_1 ) self.assertAlmostEqual(self.mass_ratio, mass_ratio) + self.assertEqual(mass_ratio.__array_namespace__(), self.xp) def test_symmetric_mass_ratio_to_mass_ratio(self): mass_ratio = conversion.symmetric_mass_ratio_to_mass_ratio( self.symmetric_mass_ratio ) self.assertAlmostEqual(self.mass_ratio, mass_ratio) + self.assertEqual(mass_ratio.__array_namespace__(), self.xp) def test_chirp_mass_and_total_mass_to_symmetric_mass_ratio(self): symmetric_mass_ratio = conversion.chirp_mass_and_total_mass_to_symmetric_mass_ratio( self.chirp_mass, self.total_mass ) self.assertAlmostEqual(self.symmetric_mass_ratio, symmetric_mass_ratio) + self.assertEqual(symmetric_mass_ratio.__array_namespace__(), self.xp) def test_chirp_mass_and_mass_ratio_to_total_mass(self): total_mass = conversion.chirp_mass_and_mass_ratio_to_total_mass( self.chirp_mass, self.mass_ratio ) self.assertAlmostEqual(self.total_mass, total_mass) + self.assertEqual(total_mass.__array_namespace__(), self.xp) def test_chirp_mass_and_mass_ratio_to_component_masses(self): mass_1, mass_2 = \ @@ -105,30 +114,37 @@ def test_chirp_mass_and_mass_ratio_to_component_masses(self): self.chirp_mass, self.mass_ratio) self.assertAlmostEqual(self.mass_1, mass_1) self.assertAlmostEqual(self.mass_2, mass_2) + self.assertEqual(mass_1.__array_namespace__(), self.xp) + self.assertEqual(mass_2.__array_namespace__(), self.xp) def test_component_masses_to_chirp_mass(self): chirp_mass = conversion.component_masses_to_chirp_mass(self.mass_1, self.mass_2) self.assertAlmostEqual(self.chirp_mass, chirp_mass) + self.assertEqual(chirp_mass.__array_namespace__(), self.xp) def test_component_masses_to_total_mass(self): total_mass = conversion.component_masses_to_total_mass(self.mass_1, self.mass_2) self.assertAlmostEqual(self.total_mass, total_mass) + self.assertEqual(total_mass.__array_namespace__(), self.xp) def test_component_masses_to_symmetric_mass_ratio(self): symmetric_mass_ratio = conversion.component_masses_to_symmetric_mass_ratio( self.mass_1, self.mass_2 ) self.assertAlmostEqual(self.symmetric_mass_ratio, symmetric_mass_ratio) + self.assertEqual(symmetric_mass_ratio.__array_namespace__(), self.xp) def test_component_masses_to_mass_ratio(self): mass_ratio = conversion.component_masses_to_mass_ratio(self.mass_1, self.mass_2) self.assertAlmostEqual(self.mass_ratio, mass_ratio) + self.assertEqual(mass_ratio.__array_namespace__(), self.xp) def test_mass_1_and_chirp_mass_to_mass_ratio(self): mass_ratio = conversion.mass_1_and_chirp_mass_to_mass_ratio( self.mass_1, self.chirp_mass ) self.assertAlmostEqual(self.mass_ratio, mass_ratio) + self.assertEqual(mass_ratio.__array_namespace__(), self.xp) def test_lambda_tilde_to_lambda_1_lambda_2(self): lambda_1, lambda_2 = conversion.lambda_tilde_to_lambda_1_lambda_2( @@ -142,6 +158,8 @@ def test_lambda_tilde_to_lambda_1_lambda_2(self): ] ) ) + self.assertEqual(lambda_1.__array_namespace__(), self.xp) + self.assertEqual(lambda_2.__array_namespace__(), self.xp) def test_lambda_tilde_delta_lambda_tilde_to_lambda_1_lambda_2(self): ( @@ -158,18 +176,22 @@ def test_lambda_tilde_delta_lambda_tilde_to_lambda_1_lambda_2(self): ] ) ) + self.assertEqual(lambda_1.__array_namespace__(), self.xp) + self.assertEqual(lambda_2.__array_namespace__(), self.xp) def test_lambda_1_lambda_2_to_lambda_tilde(self): lambda_tilde = conversion.lambda_1_lambda_2_to_lambda_tilde( self.lambda_1, self.lambda_2, self.mass_1, self.mass_2 ) self.assertTrue((self.lambda_tilde - lambda_tilde) < 1e-5) + self.assertEqual(lambda_tilde.__array_namespace__(), self.xp) def test_lambda_1_lambda_2_to_delta_lambda_tilde(self): delta_lambda_tilde = conversion.lambda_1_lambda_2_to_delta_lambda_tilde( self.lambda_1, self.lambda_2, self.mass_1, self.mass_2 ) self.assertTrue((self.delta_lambda_tilde - delta_lambda_tilde) < 1e-5) + self.assertEqual(delta_lambda_tilde.__array_namespace__(), self.xp) def test_identity_conversion(self): original_samples = dict( From aa8d84633d0c21f44c2a363d71045e693d774707 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Mon, 2 Feb 2026 06:33:39 -0500 Subject: [PATCH 079/140] TEST: more debugging slab spike test --- .github/workflows/unit-tests.yml | 6 +++--- test/core/prior/slabspike_test.py | 10 ++++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 39aa3b1b8..870ed435d 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -63,13 +63,13 @@ jobs: # - name: Run precommits # run: | # pre-commit run --all-files --verbose --show-diff-on-failure - - name: Run unit tests - run: | - python -m pytest --cov=bilby --cov-branch --durations 10 -ra --color yes --cov-report=xml --junitxml=pytest.xml - name: Run jax-backend unit tests run: | python -m pip install .[jax] pytest --array-backend jax --durations 10 + - name: Run unit tests + run: | + python -m pytest --cov=bilby --cov-branch --durations 10 -ra --color yes --cov-report=xml --junitxml=pytest.xml - name: Run sampler tests run: | pytest test/integration/sampler_run_test.py --durations 10 -v diff --git a/test/core/prior/slabspike_test.py b/test/core/prior/slabspike_test.py index 53eb5ebee..6109e79f3 100644 --- a/test/core/prior/slabspike_test.py +++ b/test/core/prior/slabspike_test.py @@ -155,15 +155,17 @@ def test_cdf_below_spike(self): expected = slab.cdf(test_nodes) * slab_spike.slab_fraction actual = slab_spike.cdf(test_nodes) self.assertTrue(np.allclose(expected, actual, rtol=1e-5)) - self.assertEqual(expected.__array_namespace__(), self.xp) + self.assertEqual(actual.__array_namespace__(), self.xp) def test_cdf_at_spike(self): for slab, slab_spike in zip(self.slabs, self.slab_spikes): print(slab) expected = slab.cdf(self.spike_loc) * slab_spike.slab_fraction - actual = slab_spike.cdf(self.spike_loc) + actual = slab_spike.cdf(self.xp.asarray(self.spike_loc)) + if isinstance(slab, bilby.core.prior.Gaussian): + print(type(expected), type(actual)) self.assertTrue(np.allclose(expected, actual, rtol=1e-5)) - self.assertEqual(expected.__array_namespace__(), self.xp) + self.assertEqual(actual.__array_namespace__(), self.xp) def test_cdf_above_spike(self): for slab, slab_spike, test_nodes in zip(self.slabs, self.slab_spikes, self.test_nodes): @@ -172,7 +174,7 @@ def test_cdf_above_spike(self): expected = slab.cdf(test_nodes) * slab_spike.slab_fraction + self.spike_height actual = slab_spike.cdf(test_nodes) np.testing.assert_allclose(expected, actual, rtol=1e-12) - self.assertEqual(expected.__array_namespace__(), self.xp) + self.assertEqual(actual.__array_namespace__(), self.xp) def test_cdf_at_minimum(self): for slab_spike in self.slab_spikes: From fa71fc6b54fc88600cdfeed967d1ceac06666a94 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Mon, 2 Feb 2026 06:46:23 -0500 Subject: [PATCH 080/140] TEST: jax tests work again --- .github/workflows/unit-tests.yml | 6 +++--- test/core/prior/slabspike_test.py | 5 ----- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 870ed435d..39aa3b1b8 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -63,13 +63,13 @@ jobs: # - name: Run precommits # run: | # pre-commit run --all-files --verbose --show-diff-on-failure + - name: Run unit tests + run: | + python -m pytest --cov=bilby --cov-branch --durations 10 -ra --color yes --cov-report=xml --junitxml=pytest.xml - name: Run jax-backend unit tests run: | python -m pip install .[jax] pytest --array-backend jax --durations 10 - - name: Run unit tests - run: | - python -m pytest --cov=bilby --cov-branch --durations 10 -ra --color yes --cov-report=xml --junitxml=pytest.xml - name: Run sampler tests run: | pytest test/integration/sampler_run_test.py --durations 10 -v diff --git a/test/core/prior/slabspike_test.py b/test/core/prior/slabspike_test.py index 6109e79f3..cd066ef05 100644 --- a/test/core/prior/slabspike_test.py +++ b/test/core/prior/slabspike_test.py @@ -150,7 +150,6 @@ def test_inverse_cdf_below_spike_arbitrary_position(self): def test_cdf_below_spike(self): for slab, slab_spike, test_nodes in zip(self.slabs, self.slab_spikes, self.test_nodes): - print(slab) test_nodes = test_nodes[np.where(test_nodes < self.spike_loc)] expected = slab.cdf(test_nodes) * slab_spike.slab_fraction actual = slab_spike.cdf(test_nodes) @@ -159,17 +158,13 @@ def test_cdf_below_spike(self): def test_cdf_at_spike(self): for slab, slab_spike in zip(self.slabs, self.slab_spikes): - print(slab) expected = slab.cdf(self.spike_loc) * slab_spike.slab_fraction actual = slab_spike.cdf(self.xp.asarray(self.spike_loc)) - if isinstance(slab, bilby.core.prior.Gaussian): - print(type(expected), type(actual)) self.assertTrue(np.allclose(expected, actual, rtol=1e-5)) self.assertEqual(actual.__array_namespace__(), self.xp) def test_cdf_above_spike(self): for slab, slab_spike, test_nodes in zip(self.slabs, self.slab_spikes, self.test_nodes): - print(slab) test_nodes = test_nodes[np.where(test_nodes > self.spike_loc)] expected = slab.cdf(test_nodes) * slab_spike.slab_fraction + self.spike_height actual = slab_spike.cdf(test_nodes) From 1cf8b606c437434accb053fc5b8d84d8da54b712 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Mon, 2 Feb 2026 06:46:41 -0500 Subject: [PATCH 081/140] DOC: add initial doc page for array backend --- docs/array_api.rst | 528 +++++++++++++++++++++++++++++++++++++++++++++ docs/index.txt | 1 + 2 files changed, 529 insertions(+) create mode 100644 docs/array_api.rst diff --git a/docs/array_api.rst b/docs/array_api.rst new file mode 100644 index 000000000..57c187d72 --- /dev/null +++ b/docs/array_api.rst @@ -0,0 +1,528 @@ +===================== +Array API Support +===================== + +Bilby now supports the Python `Array API Standard `_, +enabling the use of different array backends (NumPy, JAX, CuPy, etc.) for improved performance +and hardware acceleration. This page describes how to use this functionality and how it works internally. + +For Users and Downstream Developers +==================================== + +Overview +-------- + +The Array API support allows you to use different array libraries with Bilby seamlessly. +This can significantly improve performance, especially when using hardware accelerators like GPUs +or when you need automatic differentiation capabilities. + +**Key principle**: In most cases, you don't need to explicitly specify which array backend to use. +Bilby automatically detects the array type you're working with and uses the appropriate backend. +Simply pass JAX arrays, CuPy arrays, or NumPy arrays to prior methods, and Bilby handles the rest. + +Supported Backends +------------------ + +Bilby is currently tested with the following array backends: + +- **NumPy** (default): Standard CPU-based computations +- **JAX**: GPU/TPU acceleration and automatic differentiation + +While :code:`Bilby` should be compatible with other Array API compliant libraries, +these are not currently tested or officially supported. +If you notice any issues when using other backends, +please report them on the `Bilby GitHub repository `. + +Using Different Array Backends +------------------------------- + +Basic Prior Usage (Automatic Detection) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The array backend is automatically detected from your input arrays. You typically don't need +to specify the ``xp`` parameter:: + +.. code-block:: python + + import bilby + import jax.numpy as jnp + import numpy as np + + prior = bilby.core.prior.Uniform(minimum=0, maximum=10) + + # Using JAX - backend automatically detected + val_jax = jnp.array([0.5, 1.5, 2.5]) + prob_jax = prior.prob(val_jax) # Returns JAX array + + # Using NumPy - backend automatically detected + val_np = np.array([0.5, 1.5, 2.5]) + prob_np = prior.prob(val_np) # Returns NumPy array + +Sampling with Array Backends (Explicit xp Required) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When sampling from priors, you **must** explicitly specify the array backend using the ``xp`` parameter, +as there's no input array to infer the backend from:: + +.. code-block:: python + + import bilby + import jax.numpy as jnp + + prior = bilby.core.prior.Uniform(minimum=0, maximum=10) + samples = prior.sample(size=1000, xp=jnp) # Returns JAX array + + # Or with NumPy (default) + samples_np = prior.sample(size=1000) # Or explicitly: xp=np + +.. note:: + + Currently, prior sampling is done by first generating uniform samples in [0, 1] + using :code:`NumPy`, then converting to the desired backend. + In future releases, this may be altered to generate samples directly in the specified backend. + +Prior Dictionaries +~~~~~~~~~~~~~~~~~~ + +Prior dictionaries work the same way - automatic detection for most methods, explicit ``xp`` for sampling:: + +.. code-block:: python + + import bilby + import jax.numpy as jnp + + priors = bilby.core.prior.PriorDict({ + 'x': bilby.core.prior.Uniform(0, 100), + 'y': bilby.core.prior.Uniform(0, 1) + }) + + # Sampling requires explicit xp + samples = priors.sample(size=1000, xp=jnp) + + # Evaluation automatically detects backend from input + theta = jnp.array([50.0, 0.5]) + prob = priors.prob(samples) # Automatically uses JAX + +Core Likelihoods and Sampling +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Core :code:`Bilby` likelihoods are compatible with the Array API. +When using :code:`JAX` arrays, you can take advantage of :code:`JAX`'s JIT compilation and automatic differentiation. +For :code:`JAX`-compatible samplers (e.g., :code:`numpyro`), +you can pass any :code:`JAX`-compatible :code:`Bilby` likelihood directly. +For non-:code:`JAX` samplers, you should wrap your likelihood with the +:code:`bilby.compat.jax.JittedLikelihood` class to enable JIT compilation. + +.. code-block:: python + + import bilby + import jax.numpy as jnp + from bilby.compat.jax import JittedLikelihood + + class MyLikelihood(bilby.Likelihood): + def log_likelihood(self, parameters): + # model returns a JAX array if passed a dictionary of JAX arrays + return -0.5 * xp.sum((self.data - model(parameters))**2) + + data = jnp.array([...]) # Your data as a JAX array + + priors = bilby.core.prior.PriorDict({ + 'param1': bilby.core.prior.Uniform(0, 10), + 'param2': bilby.core.prior.Uniform(-5, 5) + }) + + likelihood = MyLikelihood(data) + + # call the likelihood once in case any initial setup is needed + likelihood.log_likelihood(priors.sample()) + + # Wrap with JittedLikelihood for JAX + jitted_likelihood = JittedLikelihood(likelihood) + + # call the jitted likelihood once to trigger JIT compilation + # the JittedLikelihood automatically converts the parameters + # to JAX arrays + jitted_likelihood.log_likelihood(priors.sample()) + + # Use with a JAX-incompatible sampler + sampler = bilby.run_sampler(likelihood=jitted_likelihood, ...) + +Gravitational-Wave Likelihoods +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The :code:`Bilby` implementation of gravitational-wave likelihood is compatible with the Array API, +however this requires access to waveform models that support the provided array backend. +The desired array backend must be explicitly specified for the data, +using :code:`bilby.gw.detector.networks.InterferometerList.set_array_backend`. +Below is an example using the :code:`ripplegw` package for waveform generation. +Here, an injection is performed using the standard :code:`LALSimulation` waveform generator, +and the analysis is then performed using the JIT-compiled likelihood. + +.. code-block:: python + + import bilby + import jax.numpy as jnp + import ripplegw + + priors = bilby.gw.prior.BBHPriorDict() + priors["geocent_time"] = bilby.core.prior.Uniform(1126259462.4, 1126259462.6) + injection_parameters = priors.sample() + + # Create interferometers and inject signal using standard waveform generator + ifos = bilby.gw.detector.networks.InterferometerList(['H1', 'L1']) + ifos.set_strain_data_from_power_spectral_densities( + sampling_frequency=2048, + duration=4, + start_time=injection_parameters["geocent_time"] - 2 + ) + injection_wfg = bilby.gw.waveform_generator.WaveformGenerator( + duration=4, + sampling_frequency=2048, + frequency_domain_source_model=bilby.gw.source.lal_binary_black_hole, + waveform_arguments={"approximant": "IMRPhenomXODE"} + ) + ifos.inject_signal(parameters=injection_parameters, waveform_generator=injection_wfg) + + # set the array backend after the injection + ifos.set_array_backend(jnp) + + ripple_wfg = bilby.gw.waveform_generator.WaveformGenerator( + duration=4, + sampling_frequency=2048, + frequency_domain_source_model=ripplegw.get_fd_waveform + ) + + # Create gravitational-wave likelihood + likelihood = bilby.gw.likelihood.GravitationalWaveTransient( + interferometers=ifos, + waveform_generator=ripple_wfg, + priors=priors, + phase_marginalization=True, + ) + # call the likelihood once to do some initial setup + # this is needed for the gravitational-wave transient likelihoods + likelihood.log_likelihood_ratio(priors.sample()) + + # Wrap with JittedLikelihood for JAX and JIT compile + jitted_likelihood = bilby.compat.jax.JittedLikelihood(likelihood) + jitted_likelihood.log_likelihood_ratio(priors.sample()) + +.. note:: + + All of the likelihood marginalizations implemented in :code:`Bilby` are compatible with the Array API. + However, there is currently a performance issue with the distance marginalized likelihood + using the :code:`JAX` backend. + +Performance Considerations +-------------------------- + +**When to use JAX:** + +- GPU/TPU acceleration is available +- You need automatic differentiation +- Working with large datasets or many parameters +- Repeated evaluations benefit from JIT compilation + +**When to use NumPy:** + +- Simple CPU-based computations +- Small datasets +- Maximum compatibility +- Debugging (easier to inspect values) + +**Best Practices:** + +1. Let Bilby detect the array backend automatically - only specify ``xp`` when sampling +2. Use array backend consistently throughout your analysis +3. Avoid mixing array types in the same computation +4. For JAX, consider using ``jax.jit`` for repeated computations +5. Profile your code to ensure the chosen backend provides benefits + +Bilby and JIT compilation +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Currently, Bilby functions are not JIT-compiled by default. +Additionally, many Bilby types are not defined as :code:`JAX`` :code:`PyTrees`, +and so cannot be passed as arguments to JIT-compiled functions. +We plan to support JIT-compilation for at least some Bilby types in future releases. + +Custom Priors with Array API +----------------------------- + +When creating custom priors, ensure they support the Array API: + +Example Implementation +~~~~~~~~~~~~~~~~~~~~~~ + +Always include the ``xp`` parameter with a default value:: + +... code-block:: python + + from bilby.core.prior import Prior + + class MyCustomPrior(Prior): + def __init__(self, parameter, **kwargs): + super().__init__(**kwargs) + self.parameter = parameter + + def rescale(self, val, *, xp=None): + """Rescale method with xp parameter.""" + return self.minimum + val * (self.maximum - self.minimum) * self.parameter + + def prob(self, val, *, xp=None): + """Probability method with xp parameter.""" + in_range = (val >= self.minimum) & (val <= self.maximum) + return in_range / (self.maximum - self.minimum) * self.parameter + +The ``xp`` parameter should: + +- Be a keyword-only argument (after ``*``) +- Have a default value (``None`` if method is decorated with ``@xp_wrap``, ``np`` otherwise) +- Be passed through to any array operations if used directly + +**Note**: Users of your custom prior won't need to pass ``xp`` explicitly for evaluation methods - +it will be automatically inferred from their input arrays. They only need to specify ``xp`` when sampling. + +Using the :code:`xp_wrap`` Decorator +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For methods that perform array operations, use the ``@xp_wrap`` decorator:: + +.. code-block:: python + + from bilby.core.prior import Prior + from bilby.compat.utils import xp_wrap + import numpy as np + + class MyCustomPrior(Prior): + @xp_wrap + def prob(self, val, *, xp=None): + """The decorator handles xp=None automatically.""" + return xp.exp(-val) / self.normalization * self.is_in_prior_range(val) + + @xp_wrap + def ln_prob(self, val, *, xp=None): + """Works with logarithmic operations.""" + return -val - xp.log(self.normalization) + xp.log(self.is_in_prior_range(val)) + +The ``@xp_wrap`` decorator: + +- Automatically provides the appropriate array module when ``xp=None`` +- Infers the array backend from input arrays when they are :code:`JAX`/:code:`CuPy`/:code:`PyTorch` arrays +- Falls back to NumPy when the input is a standard Python type or NumPy array +- Handles the conversion seamlessly so users don't need to specify ``xp`` + +For Bilby Developers +===================== + +Architecture Overview +--------------------- + +The Array API support in Bilby is built around several key components: + +1. **The xp parameter**: A keyword-only parameter added to prior methods +2. **The @xp_wrap decorator**: Handles array module selection and injection +4. **Compatibility utilities**: Helper functions for array module detection + +Core Changes to Prior Base Class +--------------------------------- + +The ``Prior`` base class in ``bilby/core/prior/base.py`` includes these key changes: + +Method Signature Pattern +~~~~~~~~~~~~~~~~~~~~~~~~ + +All array-processing methods in prior classes follow this pattern: + +**For methods with @xp_wrap decorator**:: + +.. code-block:: python + + @xp_wrap + def prob(self, val, *, xp=None): + """Method that uses xp for array operations.""" + return xp.some_operation(val) * self.is_in_prior_range(val) + +**For methods without @xp_wrap (that use xp directly)**:: + +.. code-block:: python + + def sample(self, size=None, *, xp=np): + """Method that uses xp but isn't wrapped.""" + return xp.array(random.rng.uniform(0, 1, size)) + +Key rules: + +- ``xp`` is always keyword-only (after ``*``) +- Methods with ``@xp_wrap`` use ``xp=None`` as default +- Methods without ``@xp_wrap`` that use ``xp`` use ``xp=np`` as default +- Methods that don't use ``xp`` have ``xp=None`` as default + +The :code:`@xp_wrap`` Decorator +------------------------------- + +Located in ``bilby/compat/utils.py``, this decorator: + +1. **Inspects input arguments** to determine the array module in use +2. **Provides the appropriate xp** when ``xp=None`` +3. **Maintains backward compatibility** with code that doesn't pass ``xp`` + +Example implementation pattern:: + +... code-block:: python + + from bilby.compat.utils import xp_wrap + + @xp_wrap + def my_function(val, *, xp=None): + # When called: + # - If xp=None, decorator infers from val + # - If xp is provided, uses that + # - Returns results in the same array type as input + return xp.exp(val) / xp.mean(val) + +Testing Array API Support +------------------------- + +Test Structure +~~~~~~~~~~~~~~ + +When appropriate, tests should verify functionality across different +backends using the ``array_backend`` marker:: + + @pytest.mark.array_backend + @pytest.mark.usefixtures("xp_class") + class TestMyPrior: + def test_prob(self): + prior = MyPrior() + val = self.xp.array([0.5, 1.5, 2.5]) + # No need to pass xp - automatically detected + prob = prior.prob(val) + assert self.xp.all(prob >= 0) + assert prob.__array_namespace__() == self.xp + + def test_sample(self): + prior = MyPrior() + # Sampling requires explicit xp + samples = prior.sample(size=100, xp=self.xp) + assert samples.__array_namespace__() == self.xp + +The array_backend Marker +~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``@pytest.mark.array_backend`` marker is used to indicate that a test or test class should be run +with multiple array backends. When you run pytest with the ``--array-backend`` flag, only tests marked +with ``array_backend`` will be executed with that specific backend. + +Without the marker, tests run with the default NumPy backend only. With the marker: + +- Tests are parametrized to run with different backends +- The ``xp_class`` fixture is available, providing access to the array module via ``self.xp`` +- Tests verify that code works correctly regardless of the array backend + +Running Tests with Different Backends +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Use the ``--array-backend`` flag to test with specific backends:: + + # Test with NumPy (default) + pytest test/core/prior/analytical_test.py + + # Test with JAX backend + pytest --array-backend jax test/core/prior/analytical_test.py + + # Test with CuPy backend + pytest --array-backend cupy test/core/prior/analytical_test.py + +Bilby automatically sets ``SCIPY_ARRAY_API=1`` on import, so you don't need to set this +environment variable manually. The ``--array-backend`` flag controls which backend the +``xp_class`` fixture provides to your tests. + +Migration Guide from Previous Versions +-------------------------------------- + +Key Differences +~~~~~~~~~~~~~~~ + +1. **Method signatures changed**: All prior methods now include ``xp`` parameter +2. **Decorator added**: Many methods now use ``@xp_wrap`` +3. **Default values differ**: Methods with ``@xp_wrap`` use ``xp=None``, others use ``xp=np`` +4. **Validation added**: Custom priors are checked for ``xp`` support + +Best Practices for Contributors +-------------------------------- + +When adding or modifying prior methods: + +1. **Always include xp parameter** in prob, ln_prob, rescale, cdf, sample methods +2. **Use @xp_wrap decorator** for methods doing array operations +3. **Set correct default**: ``xp=None`` with decorator, ``xp=np`` without (for methods that use xp directly) +4. **Pass xp through**: When calling other methods, pass ``xp=xp`` +5. **Test with multiple backends**: Use ``@pytest.mark.array_backend`` and test with ``--array-backend jax`` +6. **Document xp parameter**: Note it in docstrings, but emphasize it's usually auto-detected +7. **Use array module functions**: Use ``xp.function()`` not ``np.function()`` in wrapped methods + +Handling Array Updates with :code:`array_api_extra.at`` +------------------------------------------------------- + +One key difference between array backends is how they handle array updates. +NumPy allows in-place modification of array slices, +while JAX requires functional updates since arrays are immutable. +The ``array_api_extra.at`` function provides a unified interface for array updates across backends. + +Usage Examples +~~~~~~~~~~~~~~ + +**Conditional update**:: + +.. code-block:: python + + @xp_wrap + def conditional_update(vals, *, xp=None): + """Update array elements where mask is True.""" + arr = vals**2 + mask = arr > 0.5 + # Instead of: arr[mask] = value + arr = xpx.at(arr)[mask].set(value) + return arr + +**Increment operation**:: + +.. code-block:: python + + @xp_wrap + def increment_slice(arr, *, xp=None): + """Add values to a slice of an array.""" + # Instead of: arr[2:5] += values + arr = xpx.at(arr)[2:5].add(values) + return arr + +Available Operations +~~~~~~~~~~~~~~~~~~~~ + +The ``at`` function supports several operations: + +- ``set(values)``: Replace values at specified indices +- ``add(values)``: Add values to specified indices +- ``multiply(values)``: Multiply specified indices by values +- ``min(values)``: Take element-wise minimum +- ``max(values)``: Take element-wise maximum + +Important Notes +~~~~~~~~~~~~~~~ + +1. **Return value**: Always use the returned array. The operation may create a new array (JAX) or modify in-place (NumPy). + +2. **Import**: Import ``array_api_extra`` at the module level:: + +.. code-block:: python + + import array_api_extra as xpx + +Further Resources +----------------- + +- `Array API Standard `_ +- `JAX Documentation `_ +- `array-api-compat Package `_ +- `array-api-extra Package `_ diff --git a/docs/index.txt b/docs/index.txt index ff6e12c85..d8fabb550 100644 --- a/docs/index.txt +++ b/docs/index.txt @@ -16,6 +16,7 @@ Welcome to bilby's documentation! prior likelihood samplers + array_api dynesty-guide bilby-mcmc-guide rng From feafa234cce1a3191bceb453d85b622709f3f5f2 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Mon, 2 Feb 2026 10:58:06 -0500 Subject: [PATCH 082/140] TEST: add a bunch of gw tests --- bilby/gw/utils.py | 27 +++-- bilby/gw/waveform_generator.py | 3 +- test/conftest.py | 15 +-- test/gw/conversion_test.py | 55 ++++++---- test/gw/detector/geometry_test.py | 23 +++- test/gw/likelihood_test.py | 165 +++++++++++++++++++++-------- test/gw/prior_test.py | 7 +- test/gw/utils_test.py | 87 +++++++++++---- test/gw/waveform_generator_test.py | 47 ++++++-- 9 files changed, 308 insertions(+), 121 deletions(-) diff --git a/bilby/gw/utils.py b/bilby/gw/utils.py index 9c2cc8015..5f9b14d16 100644 --- a/bilby/gw/utils.py +++ b/bilby/gw/utils.py @@ -8,7 +8,7 @@ from .geometry import zenith_azimuth_to_theta_phi from .time import greenwich_mean_sidereal_time -from ..compat.utils import array_module +from ..compat.utils import array_module, xp_wrap from ..core.utils import (logger, run_commandline, check_directory_exists_and_if_not_mkdir, SamplesSummary, theta_phi_to_ra_dec) @@ -31,7 +31,7 @@ def asd_from_freq_series(freq_data, df): array_like: array of real-valued normalized frequency domain ASD data """ - return np.absolute(freq_data) * 2 * df**0.5 + return abs(freq_data) * 2 * df**0.5 def psd_from_freq_series(freq_data, df): @@ -51,7 +51,7 @@ def psd_from_freq_series(freq_data, df): array_like: Real-valued normalized frequency domain PSD data """ - return np.power(asd_from_freq_series(freq_data, df), 2) + return asd_from_freq_series(freq_data, df) ** 2 def get_vertex_position_geocentric(latitude, longitude, elevation): @@ -221,7 +221,7 @@ def overlap(signal_a, signal_b, power_spectral_density=None, delta_frequency=Non """ low_index = int(lower_cut_off / delta_frequency) up_index = int(upper_cut_off / delta_frequency) - integrand = np.conj(signal_a) * signal_b + integrand = signal_a.conjugate() * signal_b integrand = integrand[low_index:up_index] / power_spectral_density[low_index:up_index] integral = (4 * delta_frequency * integrand) / norm_a / norm_b return sum(integral).real @@ -247,8 +247,6 @@ def zenith_azimuth_to_ra_dec(zenith, azimuth, geocent_time, ifos): ra, dec: float The zenith and azimuthal angles in the sky frame. """ - # delta_x = ifos[0].geometry.vertex - ifos[1].geometry.vertex - # theta, phi = zenith_azimuth_to_theta_phi(zenith, azimuth, delta_x) theta, phi = zenith_azimuth_to_theta_phi(zenith, azimuth, ifos) gmst = greenwich_mean_sidereal_time(geocent_time) ra, dec = theta_phi_to_ra_dec(theta, phi, gmst) @@ -924,7 +922,8 @@ def lalsim_SimNeutronStarLoveNumberK2(mass_in_SI, fam): return SimNeutronStarLoveNumberK2(mass_in_SI, fam) -def spline_angle_xform(delta_psi): +@xp_wrap +def spline_angle_xform(delta_psi, *, xp=None): """ Returns the angle in degrees corresponding to the spline calibration parameters delta_psi. @@ -941,7 +940,7 @@ def spline_angle_xform(delta_psi): """ rotation = (2.0 + 1.0j * delta_psi) / (2.0 - 1.0j * delta_psi) - return 180.0 / np.pi * np.arctan2(np.imag(rotation), np.real(rotation)) + return 180.0 / np.pi * xp.arctan2(xp.imag(rotation), xp.real(rotation)) def plot_spline_pos(log_freqs, samples, nfreqs=100, level=0.9, color='k', label=None, xform=None): @@ -1002,7 +1001,8 @@ def plot_spline_pos(log_freqs, samples, nfreqs=100, level=0.9, color='k', label= plt.xlim(freq_points.min() - .5, freq_points.max() + 50) -def ln_i0(value): +@xp_wrap +def ln_i0(value, *, xp=None): """ A numerically stable method to evaluate ln(I_0) a modified Bessel function of order 0 used in the phase-marginalized likelihood. @@ -1017,7 +1017,6 @@ def ln_i0(value): array-like: The natural logarithm of the bessel function """ - xp = array_module(value) return xp.log(i0e(value)) + xp.abs(value) @@ -1047,10 +1046,10 @@ def calculate_time_to_merger(frequency, mass_1, mass_2, chi=0, safety=1.1): import lalsimulation return safety * lalsimulation.SimInspiralTaylorF2ReducedSpinChirpTime( - frequency, - mass_1 * solar_mass, - mass_2 * solar_mass, - chi, + float(frequency), + float(mass_1 * solar_mass), + float(mass_2 * solar_mass), + float(chi), -1 ) diff --git a/bilby/gw/waveform_generator.py b/bilby/gw/waveform_generator.py index f1c5e6e2a..a27808738 100644 --- a/bilby/gw/waveform_generator.py +++ b/bilby/gw/waveform_generator.py @@ -1,3 +1,4 @@ +import array_api_compat as aac import numpy as np from ..core import utils @@ -206,7 +207,7 @@ def _strain_from_transformed_model( transformed_model_data_points, transformed_model, parameters ) - if isinstance(transformed_model_strain, np.ndarray): + if aac.is_array_api_obj(transformed_model_strain): return transformation_function(transformed_model_strain, self.sampling_frequency) model_strain = dict() diff --git a/test/conftest.py b/test/conftest.py index d0d1ad79b..b1668561a 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -21,14 +21,17 @@ def pytest_configure(config): def pytest_collection_modifyitems(config, items): if config.getoption("--skip-roqs"): skip_roqs = pytest.mark.skip(reason="Skipping tests that require ROQs") - for item in items: - if "requires_roqs" in item.keywords: - item.add_marker(skip_roqs) + else: + skip_roqs = pytest.mark.noop if config.getoption("--array-backend") is not None: array_only = pytest.mark.skip(reason="Only running backend dependent tests") - for item in items: - if "array_backend" not in item.keywords: - item.add_marker(array_only) + else: + array_only = pytest.mark.noop + for item in items: + if "requires_roqs" in item.keywords and config.getoption("--skip-roqs"): + item.add_marker(skip_roqs) + elif "array_backend" not in item.keywords: + item.add_marker(array_only) def _xp(request): diff --git a/test/gw/conversion_test.py b/test/gw/conversion_test.py index 386933e18..1610b4dcd 100644 --- a/test/gw/conversion_test.py +++ b/test/gw/conversion_test.py @@ -622,19 +622,20 @@ def test_comoving_luminosity_with_cosmology(self): self.assertAlmostEqual(max(abs(dl - self.distances)), 0, 4) +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestGenerateMassParameters(unittest.TestCase): def setUp(self): - self.expected_values = {'mass_1': 2.0, - 'mass_2': 1.0, - 'chirp_mass': 1.2167286837864113, - 'total_mass': 3.0, - 'mass_1_source': 4.0, - 'mass_2_source': 2.0, - 'chirp_mass_source': 2.433457367572823, - 'total_mass_source': 6, - 'symmetric_mass_ratio': 0.2222222222222222, - 'mass_ratio': 0.5} - + self.expected_values = {'mass_1': self.xp.array(2.0), + 'mass_2': self.xp.array(1.0), + 'chirp_mass': self.xp.array(1.2167286837864113), + 'total_mass': self.xp.array(3.0), + 'mass_1_source': self.xp.array(4.0), + 'mass_2_source': self.xp.array(2.0), + 'chirp_mass_source': self.xp.array(2.433457367572823), + 'total_mass_source': self.xp.array(6), + 'symmetric_mass_ratio': self.xp.array(0.2222222222222222), + 'mass_ratio': self.xp.array(0.5)} def helper_generation_from_keys(self, keys, expected_values, source=False): # Explicitly test the helper generate_component_masses local_test_vars = \ @@ -681,6 +682,10 @@ def helper_generation_from_keys(self, keys, expected_values, source=False): ) for key in local_all_mass_parameters.keys(): self.assertAlmostEqual(expected_values[key], local_all_mass_parameters[key]) + self.assertEqual( + local_all_mass_parameters[key].__array_namespace__(), + self.xp, + ) def test_from_mass_1_and_mass_2(self): self.helper_generation_from_keys(["mass_1", "mass_2"], @@ -747,6 +752,8 @@ def test_from_chirp_mass_source_and_symmetric_mass_2(self): self.expected_values, source=True) +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestEquationOfStateConversions(unittest.TestCase): ''' Class to test equation of state conversions. @@ -755,48 +762,48 @@ class TestEquationOfStateConversions(unittest.TestCase): ''' def setUp(self): - self.mass_1_source_spectral = [ + self.mass_1_source_spectral = self.xp.array([ 4.922542724434885, 4.350626907771598, 4.206155335439082, 1.7822696459661311, 1.3091740103047926 - ] - self.mass_2_source_spectral = [ + ]) + self.mass_2_source_spectral = self.xp.array([ 3.459974694590303, 1.2276461777181447, 3.7287707089639976, 0.3724016563531846, 1.055042934805801 - ] - self.spectral_pca_gamma_0 = [ + ]) + self.spectral_pca_gamma_0 = self.xp.array([ 0.7074873121348357, 0.05855931126849878, 0.7795329261793462, 1.467907561566463, 2.9066488405635624 - ] - self.spectral_pca_gamma_1 = [ + ]) + self.spectral_pca_gamma_1 = self.xp.array([ -0.29807111670823816, 2.027708558522935, -1.4415775226512115, -0.7104870098896858, -0.4913817181089619 - ] - self.spectral_pca_gamma_2 = [ + ]) + self.spectral_pca_gamma_2 = self.xp.array([ 0.25625095371021156, -0.19574096643220049, -0.2710238103460012, 0.22815820981582358, -0.1543413205016374 - ] - self.spectral_pca_gamma_3 = [ + ]) + self.spectral_pca_gamma_3 = self.xp.array([ -0.04030365100175101, 0.05698030777919032, -0.045595911403040264, -0.023480394227900117, -0.07114492992285618 - ] + ]) self.spectral_gamma_0 = [ 1.1259406796075457, 0.3191335618787259, @@ -901,6 +908,8 @@ def test_spectral_pca_to_spectral(self): self.assertAlmostEqual(spectral_gamma_1, self.spectral_gamma_1[i], places=5) self.assertAlmostEqual(spectral_gamma_2, self.spectral_gamma_2[i], places=5) self.assertAlmostEqual(spectral_gamma_3, self.spectral_gamma_3[i], places=5) + for val in [spectral_gamma_0, spectral_gamma_1, spectral_gamma_2, spectral_gamma_3]: + self.assertEqual(val.__array_namespace__(), self.xp) def test_spectral_params_to_lambda_1_lambda_2(self): ''' diff --git a/test/gw/detector/geometry_test.py b/test/gw/detector/geometry_test.py index 358825b23..4906f00cc 100644 --- a/test/gw/detector/geometry_test.py +++ b/test/gw/detector/geometry_test.py @@ -2,10 +2,13 @@ from unittest import mock import numpy as np +import pytest import bilby +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestInterferometerGeometry(unittest.TestCase): def setUp(self): self.length = 30 @@ -26,6 +29,7 @@ def setUp(self): xarm_tilt=self.xarm_tilt, yarm_tilt=self.yarm_tilt, ) + self.geometry.set_array_backend(self.xp) def tearDown(self): del self.length @@ -40,27 +44,35 @@ def tearDown(self): def test_length_setting(self): self.assertEqual(self.geometry.length, self.length) + self.assertEqual(self.geometry.length.__array_namespace__(), self.xp) def test_latitude_setting(self): self.assertEqual(self.geometry.latitude, self.latitude) + self.assertEqual(self.geometry.latitude.__array_namespace__(), self.xp) def test_longitude_setting(self): self.assertEqual(self.geometry.longitude, self.longitude) + self.assertEqual(self.geometry.longitude.__array_namespace__(), self.xp) def test_elevation_setting(self): self.assertEqual(self.geometry.elevation, self.elevation) + self.assertEqual(self.geometry.elevation.__array_namespace__(), self.xp) def test_xarm_azi_setting(self): self.assertEqual(self.geometry.xarm_azimuth, self.xarm_azimuth) + self.assertEqual(self.geometry.xarm_azimuth.__array_namespace__(), self.xp) def test_yarm_azi_setting(self): self.assertEqual(self.geometry.yarm_azimuth, self.yarm_azimuth) + self.assertEqual(self.geometry.yarm_azimuth.__array_namespace__(), self.xp) def test_xarm_tilt_setting(self): self.assertEqual(self.geometry.xarm_tilt, self.xarm_tilt) + self.assertEqual(self.geometry.xarm_tilt.__array_namespace__(), self.xp) def test_yarm_tilt_setting(self): self.assertEqual(self.geometry.yarm_tilt, self.yarm_tilt) + self.assertEqual(self.geometry.yarm_tilt.__array_namespace__(), self.xp) def test_vertex_without_update(self): _ = self.geometry.vertex @@ -142,31 +154,37 @@ def test_detector_tensor_with_x_azimuth_update(self): original = self.geometry.detector_tensor self.geometry.xarm_azimuth += 1 self.assertNotEqual(np.max(abs(self.geometry.detector_tensor - original)), 0) + self.assertEqual(self.geometry.detector_tensor.__array_namespace__(), self.xp) def test_detector_tensor_with_y_azimuth_update(self): original = self.geometry.detector_tensor self.geometry.yarm_azimuth += 1 self.assertNotEqual(np.max(abs(self.geometry.detector_tensor - original)), 0) + self.assertEqual(self.geometry.detector_tensor.__array_namespace__(), self.xp) def test_detector_tensor_with_x_tilt_update(self): original = self.geometry.detector_tensor self.geometry.xarm_tilt += 1 self.assertNotEqual(np.max(abs(self.geometry.detector_tensor - original)), 0) + self.assertEqual(self.geometry.detector_tensor.__array_namespace__(), self.xp) def test_detector_tensor_with_y_tilt_update(self): original = self.geometry.detector_tensor self.geometry.yarm_tilt += 1 self.assertNotEqual(np.max(abs(self.geometry.detector_tensor - original)), 0) + self.assertEqual(self.geometry.detector_tensor.__array_namespace__(), self.xp) def test_detector_tensor_with_longitude_update(self): original = self.geometry.detector_tensor self.geometry.longitude += 1 self.assertNotEqual(np.max(abs(self.geometry.detector_tensor - original)), 0) + self.assertEqual(self.geometry.detector_tensor.__array_namespace__(), self.xp) def test_detector_tensor_with_latitude_update(self): original = self.geometry.detector_tensor self.geometry.latitude += 1 self.assertNotEqual(np.max(abs(self.geometry.detector_tensor - original)), 0) + self.assertEqual(self.geometry.detector_tensor.__array_namespace__(), self.xp) def test_unit_vector_along_arm_default(self): with self.assertRaises(ValueError): @@ -177,17 +195,20 @@ def test_unit_vector_along_arm_x(self): self.geometry.latitude = 0 self.geometry.xarm_tilt = 0 self.geometry.xarm_azimuth = 0 + self.geometry.set_array_backend(self.xp) arm = self.geometry.unit_vector_along_arm("x") self.assertTrue(np.allclose(arm, np.array([0, 1, 0]))) + self.assertEqual(arm.__array_namespace__(), self.xp) def test_unit_vector_along_arm_y(self): self.geometry.longitude = 0 self.geometry.latitude = 0 self.geometry.yarm_tilt = 0 self.geometry.yarm_azimuth = 90 + self.geometry.set_array_backend(self.xp) arm = self.geometry.unit_vector_along_arm("y") - print(arm) self.assertTrue(np.allclose(arm, np.array([0, 0, 1]))) + self.assertEqual(arm.__array_namespace__(), self.xp) def test_repr(self): expected = ( diff --git a/test/gw/likelihood_test.py b/test/gw/likelihood_test.py index 38310d6df..fd3e3837f 100644 --- a/test/gw/likelihood_test.py +++ b/test/gw/likelihood_test.py @@ -10,9 +10,40 @@ import h5py import numpy as np import bilby +from array_api_compat import is_array_api_obj from bilby.gw.likelihood import BilbyROQParamsRangeError +class BackendWaveformGenerator(bilby.gw.waveform_generator.WaveformGenerator): + """A thin wrapper to emulate different backends in the waveform generator.""" + def __init__(self, wfg, xp): + self.wfg = wfg + self.xp = xp + + def __getattr__(self, name): + if name == "xp": + return self.xp + return getattr(self.wfg, name) + + def convert_nested_dict(self, data): + if is_array_api_obj(data): + return self.xp.array(data) + elif isinstance(data, dict): + return {key: self.convert_nested_dict(value) for key, value in data.items()} + else: + raise ValueError("Input must be an array API object or a dict of such objects.") + + def frequency_domain_strain(self, parameters): + wf = self.wfg.frequency_domain_strain(parameters) + return self.convert_nested_dict(wf) + + def time_domain_strain(self, parameters): + wf = self.wfg.time_domain_strain(parameters) + return self.convert_nested_dict(wf) + + +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestBasicGWTransient(unittest.TestCase): def setUp(self): bilby.core.utils.random.seed(500) @@ -37,11 +68,13 @@ def setUp(self): self.interferometers.set_strain_data_from_power_spectral_densities( sampling_frequency=2048, duration=4 ) - self.waveform_generator = bilby.gw.waveform_generator.GWSignalWaveformGenerator( - duration=4, - sampling_frequency=2048, + self.interferometers.set_array_backend(self.xp) + base_wfg = bilby.gw.waveform_generator.GWSignalWaveformGenerator( + duration=self.xp.array(4.0), + sampling_frequency=self.xp.array(2048.0), waveform_arguments=dict(waveform_approximant="IMRPhenomPv2"), ) + self.waveform_generator = BackendWaveformGenerator(base_wfg, self.xp) self.likelihood = bilby.gw.likelihood.BasicGravitationalWaveTransient( interferometers=self.interferometers, @@ -56,23 +89,27 @@ def tearDown(self): def test_noise_log_likelihood(self): """Test noise log likelihood matches precomputed value""" - self.likelihood.noise_log_likelihood() + nll = self.likelihood.noise_log_likelihood() self.assertAlmostEqual( - -4014.1787704539474, self.likelihood.noise_log_likelihood(), 3 + -4014.1787704539474, nll, 3 ) + self.assertEqual(nll.__array_namespace__(), self.xp) def test_log_likelihood(self): """Test log likelihood matches precomputed value""" - self.likelihood.log_likelihood(self.parameters) - self.assertAlmostEqual(self.likelihood.log_likelihood(self.parameters), -4032.4397343470005, 3) + logl = self.likelihood.log_likelihood(self.parameters) + self.assertAlmostEqual(logl, -4032.4397343470005, 3) + self.assertEqual(logl.__array_namespace__(), self.xp) def test_log_likelihood_ratio(self): """Test log likelihood ratio returns the correct value""" + llr = self.likelihood.log_likelihood_ratio(self.parameters) self.assertAlmostEqual( self.likelihood.log_likelihood(self.parameters) - self.likelihood.noise_log_likelihood(), - self.likelihood.log_likelihood_ratio(self.parameters), + llr, 3, ) + self.assertEqual(llr.__array_namespace__(), self.xp) def test_likelihood_zero_when_waveform_is_none(self): """Test log likelihood returns np.nan_to_num(-np.inf) when the @@ -87,11 +124,13 @@ def test_repr(self): self.assertEqual(expected, repr(self.likelihood)) +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestGWTransient(unittest.TestCase): def setUp(self): bilby.core.utils.random.seed(500) - self.duration = 4 - self.sampling_frequency = 2048 + self.duration = self.xp.array(4.0) + self.sampling_frequency = self.xp.array(2048.0) self.parameters = dict( mass_1=31.0, mass_2=29.0, @@ -113,11 +152,13 @@ def setUp(self): self.interferometers.set_strain_data_from_power_spectral_densities( sampling_frequency=self.sampling_frequency, duration=self.duration ) - self.waveform_generator = bilby.gw.waveform_generator.WaveformGenerator( + self.interferometers.set_array_backend(self.xp) + wfg = bilby.gw.waveform_generator.WaveformGenerator( duration=self.duration, sampling_frequency=self.sampling_frequency, frequency_domain_source_model=bilby.gw.source.lal_binary_black_hole, ) + self.waveform_generator = BackendWaveformGenerator(wfg, self.xp) self.prior = bilby.gw.prior.BBHPriorDict() self.prior["geocent_time"] = bilby.prior.Uniform( @@ -140,24 +181,27 @@ def tearDown(self): def test_noise_log_likelihood(self): """Test noise log likelihood matches precomputed value""" - self.likelihood.noise_log_likelihood() + nll = self.likelihood.noise_log_likelihood() self.assertAlmostEqual( - -4014.1787704539474, self.likelihood.noise_log_likelihood(), 3 + -4014.1787704539474, nll, 3 ) + self.assertEqual(nll.__array_namespace__(), self.xp) def test_log_likelihood(self): """Test log likelihood matches precomputed value""" - self.likelihood.log_likelihood(self.parameters) - self.assertAlmostEqual(self.likelihood.log_likelihood(self.parameters), - -4032.4397343470005, 3) + logl = self.likelihood.log_likelihood(self.parameters) + self.assertAlmostEqual(logl, -4032.4397343470005, 3) + self.assertEqual(logl.__array_namespace__(), self.xp) def test_log_likelihood_ratio(self): """Test log likelihood ratio returns the correct value""" + llr = self.likelihood.log_likelihood_ratio(self.parameters) self.assertAlmostEqual( self.likelihood.log_likelihood(self.parameters) - self.likelihood.noise_log_likelihood(), - self.likelihood.log_likelihood_ratio(self.parameters), + llr, 3, ) + self.assertEqual(llr.__array_namespace__(), self.xp) def test_likelihood_zero_when_waveform_is_none(self): """Test log likelihood returns np.nan_to_num(-np.inf) when the @@ -237,14 +281,16 @@ def test_reference_frame_agrees_with_default(self): ) parameters = self.parameters.copy() del parameters["ra"], parameters["dec"] - parameters["zenith"] = 1.0 - parameters["azimuth"] = 1.0 + parameters["zenith"] = self.xp.array(1.0) + parameters["azimuth"] = self.xp.array(1.0) parameters["ra"], parameters["dec"] = bilby.gw.utils.zenith_azimuth_to_ra_dec( zenith=parameters["zenith"], azimuth=parameters["azimuth"], geocent_time=parameters["geocent_time"], - ifos=bilby.gw.detector.InterferometerList(["H1", "L1"]) + ifos=new_likelihood.reference_frame, ) + self.assertEqual(parameters["ra"].__array_namespace__(), self.xp) + self.assertEqual(parameters["dec"].__array_namespace__(), self.xp) self.assertEqual( new_likelihood.log_likelihood_ratio(parameters), self.likelihood.log_likelihood_ratio(parameters) @@ -272,10 +318,12 @@ def test_time_reference_agrees_with_default(self): @pytest.mark.requires_roqs +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestROQLikelihood(unittest.TestCase): def setUp(self): - self.duration = 4 - self.sampling_frequency = 2048 + self.duration = self.xp.array(4.0) + self.sampling_frequency = self.xp.array(2048.0) self.test_parameters = dict( mass_1=36.0, @@ -299,6 +347,7 @@ def setUp(self): ifos.set_strain_data_from_power_spectral_densities( sampling_frequency=self.sampling_frequency, duration=self.duration ) + ifos.set_array_backend(self.xp) self.priors = bilby.gw.prior.BBHPriorDict() self.priors.pop("mass_1") @@ -318,6 +367,7 @@ def setUp(self): waveform_approximant="IMRPhenomPv2", ), ) + non_roq_wfg = BackendWaveformGenerator(non_roq_wfg, self.xp) ifos.inject_signal( parameters=self.test_parameters, waveform_generator=non_roq_wfg @@ -378,7 +428,7 @@ def roq_wfg(self): fnodes_quadratic_file = f"{self.roq_dir}/fnodes_quadratic.npy" fnodes_linear = np.load(fnodes_linear_file).T fnodes_quadratic = np.load(fnodes_quadratic_file).T - return bilby.gw.waveform_generator.WaveformGenerator( + wfg = bilby.gw.waveform_generator.WaveformGenerator( duration=self.duration, sampling_frequency=self.sampling_frequency, frequency_domain_source_model=bilby.gw.source.binary_black_hole_roq, @@ -389,6 +439,7 @@ def roq_wfg(self): waveform_approximant="IMRPhenomPv2", ), ) + return BackendWaveformGenerator(wfg, self.xp) @cached_property def roq(self): @@ -412,13 +463,13 @@ def roq_phase(self): ) def test_matches_non_roq(self): + roq_llr = self.roq.log_likelihood_ratio(self.test_parameters) + non_roq_llr = self.non_roq.log_likelihood_ratio(self.test_parameters) self.assertLess( - abs( - self.non_roq.log_likelihood_ratio(self.test_parameters) - - self.roq.log_likelihood_ratio(self.test_parameters) - ) / self.non_roq.log_likelihood_ratio(self.test_parameters), + abs(non_roq_llr - roq_llr) / non_roq_llr, 1e-3, ) + self.assertEqual(roq_llr.__array_namespace__(), self.xp) def test_time_prior_out_of_bounds_returns_zero(self): parameters = deepcopy(self.test_parameters) @@ -434,10 +485,12 @@ def test_create_roq_weights_with_params(self): quadratic_matrix=self.quadratic_matrix_file, priors=self.priors, ) + roq_llr = roq.log_likelihood_ratio(self.test_parameters) self.assertEqual( - roq.log_likelihood_ratio(self.test_parameters), + roq_llr, self.roq.log_likelihood_ratio(self.test_parameters) ) + self.assertEqual(roq_llr.__array_namespace__(), self.xp) def test_create_roq_weights_frequency_mismatch_works_with_params(self): @@ -621,6 +674,8 @@ def test_rescaling(self): @pytest.mark.requires_roqs +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestROQLikelihoodHDF5(unittest.TestCase): """ Test ROQ likelihood constructed from .hdf5 basis @@ -634,9 +689,9 @@ class TestROQLikelihoodHDF5(unittest.TestCase): _path_to_basis_mb = "/roq_basis/basis_multiband_addcal.hdf5" def setUp(self): - self.minimum_frequency = 20 - self.sampling_frequency = 2048 - self.duration = 16 + self.minimum_frequency = self.xp.array(20.0) + self.sampling_frequency = self.xp.array(2048.0) + self.duration = self.xp.array(16.0) self.reference_frequency = 20.0 self.waveform_approximant = "IMRPhenomD" # The SNRs of injections are 130-160 for roq_scale_factor=1 and 70-80 for roq_scale_factor=2 @@ -678,10 +733,11 @@ def test_fails_with_frequency_duration_mismatch( self.priors["chirp_mass"].maximum = 9 interferometers = bilby.gw.detector.InterferometerList(["H1"]) interferometers.set_strain_data_from_power_spectral_densities( - sampling_frequency=2 * maximum_frequency, - duration=duration, - start_time=self.injection_parameters["geocent_time"] - duration + 1 + sampling_frequency=self.xp.array(2 * maximum_frequency), + duration=self.xp.array(duration), + start_time=self.xp.array(self.injection_parameters["geocent_time"] - duration + 1) ) + interferometers.set_array_backend(self.xp) for ifo in interferometers: ifo.minimum_frequency = minimum_frequency ifo.maximum_frequency = maximum_frequency @@ -711,10 +767,11 @@ def test_fails_with_prior_mismatch(self, basis, chirp_mass_min, chirp_mass_max): self.priors["chirp_mass"].maximum = chirp_mass_max interferometers = bilby.gw.detector.InterferometerList(["H1"]) interferometers.set_strain_data_from_power_spectral_densities( - sampling_frequency=self.sampling_frequency, - duration=self.duration, - start_time=self.injection_parameters["geocent_time"] - self.duration + 1 + sampling_frequency=self.xp.array(self.sampling_frequency), + duration=self.xp.array(self.duration), + start_time=self.xp.array(self.injection_parameters["geocent_time"] - self.duration + 1) ) + interferometers.set_array_backend(self.xp) for ifo in interferometers: ifo.minimum_frequency = self.minimum_frequency search_waveform_generator = bilby.gw.waveform_generator.WaveformGenerator( @@ -853,6 +910,7 @@ def assertLess_likelihood_errors( self.priors["chirp_mass"].maximum = mc_max interferometers = bilby.gw.detector.InterferometerList(["H1", "L1"]) + interferometers.set_array_backend(self.xp) for ifo in interferometers: if minimum_frequency is None: ifo.minimum_frequency = self.minimum_frequency @@ -894,6 +952,7 @@ def assertLess_likelihood_errors( waveform_approximant=self.waveform_approximant ) ) + waveform_generator = BackendWaveformGenerator(waveform_generator, self.xp) interferometers.inject_signal(waveform_generator=waveform_generator, parameters=self.injection_parameters) likelihood = bilby.gw.GravitationalWaveTransient( @@ -911,6 +970,7 @@ def assertLess_likelihood_errors( waveform_approximant=self.waveform_approximant ) ) + search_waveform_generator = BackendWaveformGenerator(search_waveform_generator, self.xp) likelihood_roq = bilby.gw.likelihood.ROQGravitationalWaveTransient( interferometers=interferometers, priors=self.priors, @@ -925,6 +985,7 @@ def assertLess_likelihood_errors( llr = likelihood.log_likelihood_ratio(parameters) llr_roq = likelihood_roq.log_likelihood_ratio(parameters) self.assertLess(np.abs(llr - llr_roq), max_llr_error) + self.assertEqual(llr_roq.__array_namespace__(), self.xp) @pytest.mark.requires_roqs @@ -1252,11 +1313,13 @@ def test_instantiation(self): self.like = bilby.gw.likelihood.get_binary_black_hole_likelihood(self.ifos) +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestMBLikelihood(unittest.TestCase): def setUp(self): - self.duration = 16 - self.fmin = 20. - self.sampling_frequency = 2048. + self.duration = self.xp.array(16.0) + self.fmin = self.xp.array(20.0) + self.sampling_frequency = self.xp.array(2048.0) self.test_parameters = dict( chirp_mass=6.0, mass_ratio=0.5, @@ -1284,6 +1347,7 @@ def setUp(self): ) for ifo in self.ifos: ifo.minimum_frequency = self.fmin + self.ifos.set_array_backend(self.xp) spline_calibration_nodes = 10 self.calibration_parameters = {} @@ -1339,6 +1403,7 @@ def test_matches_original_likelihood( reference_frequency=self.fmin, waveform_approximant=waveform_approximant ) ) + wfg = BackendWaveformGenerator(wfg, self.xp) self.ifos.inject_signal(parameters=self.test_parameters, waveform_generator=wfg) wfg_mb = bilby.gw.WaveformGenerator( @@ -1348,6 +1413,7 @@ def test_matches_original_likelihood( reference_frequency=self.fmin, waveform_approximant=waveform_approximant ) ) + wfg_mb = BackendWaveformGenerator(wfg_mb, self.xp) likelihood = bilby.gw.likelihood.GravitationalWaveTransient( interferometers=self.ifos, waveform_generator=wfg ) @@ -1360,10 +1426,12 @@ def test_matches_original_likelihood( parameters = deepcopy(self.test_parameters) if add_cal_errors: parameters.update(self.calibration_parameters) + llmb = likelihood_mb.log_likelihood_ratio(parameters) self.assertLess( - abs(likelihood.log_likelihood_ratio(parameters) - likelihood_mb.log_likelihood_ratio(parameters)), + abs(likelihood.log_likelihood_ratio(parameters) - llmb), tolerance ) + self.assertEqual(llmb.__array_namespace__(), self.xp) def test_large_accuracy_factor(self): """ @@ -1377,6 +1445,7 @@ def test_large_accuracy_factor(self): reference_frequency=self.fmin, waveform_approximant=waveform_approximant ) ) + wfg = BackendWaveformGenerator(wfg, self.xp) self.ifos.inject_signal(parameters=self.test_parameters, waveform_generator=wfg) wfg_mb = bilby.gw.WaveformGenerator( @@ -1386,6 +1455,7 @@ def test_large_accuracy_factor(self): reference_frequency=self.fmin, waveform_approximant=waveform_approximant ) ) + wfg_mb = BackendWaveformGenerator(wfg_mb, self.xp) likelihood = bilby.gw.likelihood.GravitationalWaveTransient( interferometers=self.ifos, waveform_generator=wfg ) @@ -1514,11 +1584,13 @@ def test_inout_weights(self, linear_interpolation): reference_frequency=self.fmin, waveform_approximant=waveform_approximant ) ) + wfg_mb = BackendWaveformGenerator(wfg_mb, self.xp) likelihood_mb_from_weights = bilby.gw.likelihood.MBGravitationalWaveTransient( interferometers=self.ifos, waveform_generator=wfg_mb, weights=filepath ) llr_from_weights = likelihood_mb_from_weights.log_likelihood_ratio(self.test_parameters) + self.assertEqual(llr_from_weights.__array_namespace__(), self.xp) self.assertAlmostEqual(llr, llr_from_weights) @@ -1535,6 +1607,7 @@ def test_from_dict_weights(self, linear_interpolation): reference_frequency=self.fmin, waveform_approximant=waveform_approximant ) ) + wfg = BackendWaveformGenerator(wfg, self.xp) self.ifos.inject_signal( parameters=self.test_parameters, waveform_generator=wfg ) @@ -1546,12 +1619,14 @@ def test_from_dict_weights(self, linear_interpolation): reference_frequency=self.fmin, waveform_approximant=waveform_approximant ) ) + wfg_mb = BackendWaveformGenerator(wfg_mb, self.xp) likelihood_mb = bilby.gw.likelihood.MBGravitationalWaveTransient( interferometers=self.ifos, waveform_generator=wfg_mb, reference_chirp_mass=self.test_parameters['chirp_mass'], linear_interpolation=linear_interpolation, ) llr = likelihood_mb.log_likelihood_ratio(self.test_parameters) + self.assertEqual(llr.__array_namespace__(), self.xp) # reset waveform generator to check if likelihood recovered from the weights properly adds banded # frequency points to waveform arguments @@ -1562,11 +1637,13 @@ def test_from_dict_weights(self, linear_interpolation): reference_frequency=self.fmin, waveform_approximant=waveform_approximant ) ) + wfg_mb = BackendWaveformGenerator(wfg_mb, self.xp) weights = likelihood_mb.weights likelihood_mb_from_weights = bilby.gw.likelihood.MBGravitationalWaveTransient( interferometers=self.ifos, waveform_generator=wfg_mb, weights=weights ) llr_from_weights = likelihood_mb_from_weights.log_likelihood_ratio(self.test_parameters) + self.assertEqual(llr_from_weights.__array_namespace__(), self.xp) self.assertAlmostEqual(llr, llr_from_weights) @@ -1591,6 +1668,7 @@ def test_matches_original_likelihood_low_maximum_frequency( reference_frequency=self.fmin, waveform_approximant=waveform_approximant ) ) + wfg = BackendWaveformGenerator(wfg, self.xp) self.ifos.inject_signal(parameters=self.test_parameters, waveform_generator=wfg) wfg_mb = bilby.gw.WaveformGenerator( @@ -1600,6 +1678,7 @@ def test_matches_original_likelihood_low_maximum_frequency( reference_frequency=self.fmin, waveform_approximant=waveform_approximant ) ) + wfg_mb = BackendWaveformGenerator(wfg_mb, self.xp) likelihood = bilby.gw.likelihood.GravitationalWaveTransient( interferometers=self.ifos, waveform_generator=wfg ) @@ -1612,10 +1691,12 @@ def test_matches_original_likelihood_low_maximum_frequency( parameters = deepcopy(self.test_parameters) if add_cal_errors: parameters.update(self.calibration_parameters) + llrmb = likelihood_mb.log_likelihood_ratio(parameters) self.assertLess( - abs(likelihood.log_likelihood_ratio(parameters) - likelihood_mb.log_likelihood_ratio(parameters)), + abs(likelihood.log_likelihood_ratio(parameters) - llrmb), tolerance ) + self.assertEqual(llrmb.__array_namespace__(), self.xp) if __name__ == "__main__": diff --git a/test/gw/prior_test.py b/test/gw/prior_test.py index 022bc0a56..c4afa00f4 100644 --- a/test/gw/prior_test.py +++ b/test/gw/prior_test.py @@ -222,6 +222,8 @@ def test_pickle_prior(self): self.assertEqual(priors, priors_loaded) +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestPriorConversion(unittest.TestCase): def test_bilby_to_lalinference(self): mass_1 = [1, 20] @@ -256,10 +258,11 @@ def test_bilby_to_lalinference(self): ) nsamples = 5000 - bilby_samples = bilby_prior.sample(nsamples) + bilby_samples = bilby_prior.sample(nsamples, xp=self.xp) bilby_samples, _ = conversion.convert_to_lal_binary_black_hole_parameters( bilby_samples ) + bilby_samples = pd.DataFrame(bilby_samples) # Quicker way to generate LA prior samples (rather than specifying Constraint) lalinf_samples = [] @@ -279,7 +282,7 @@ def test_bilby_to_lalinference(self): result.search_parameter_keys = ["mass_ratio", "chirp_mass"] result.meta_data = dict() result.priors = bilby_prior - result.posterior = pd.DataFrame(bilby_samples) + result.posterior = bilby_samples result_converted = bilby.gw.prior.convert_to_flat_in_component_mass_prior( result, fraction=0.1 ) diff --git a/test/gw/utils_test.py b/test/gw/utils_test.py index cf78849c7..d082a01a1 100644 --- a/test/gw/utils_test.py +++ b/test/gw/utils_test.py @@ -15,6 +15,8 @@ from bilby.gw import utils as gwutils +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestGWUtils(unittest.TestCase): def setUp(self): self.outdir = "outdir" @@ -27,29 +29,32 @@ def tearDown(self): pass def test_asd_from_freq_series(self): - freq_data = np.array([1, 2, 3]) + freq_data = self.xp.array([1, 2, 3]) df = 0.1 asd = gwutils.asd_from_freq_series(freq_data, df) self.assertTrue(np.all(asd == freq_data * 2 * df ** 0.5)) + self.assertEqual(asd.__array_namespace__(), self.xp) def test_psd_from_freq_series(self): - freq_data = np.array([1, 2, 3]) + freq_data = self.xp.array([1, 2, 3]) df = 0.1 psd = gwutils.psd_from_freq_series(freq_data, df) self.assertTrue(np.all(psd == (freq_data * 2 * df ** 0.5) ** 2)) + self.assertEqual(psd.__array_namespace__(), self.xp) def test_inner_product(self): - aa = np.array([1, 2, 3]) - bb = np.array([5, 6, 7]) - frequency = np.array([0.2, 0.4, 0.6]) + aa = self.xp.array([1, 2, 3]) + bb = self.xp.array([5, 6, 7]) + frequency = self.xp.array([0.2, 0.4, 0.6]) PSD = bilby.gw.detector.PowerSpectralDensity.from_aligo() ip = gwutils.inner_product(aa, bb, frequency, PSD) self.assertEqual(ip, 0) + self.assertEqual(ip.__array_namespace__(), self.xp) def test_noise_weighted_inner_product(self): - aa = np.array([1e-23, 2e-23, 3e-23]) - bb = np.array([5e-23, 6e-23, 7e-23]) - frequency = np.array([100, 101, 102]) + aa = self.xp.array([1e-23, 2e-23, 3e-23]) + bb = self.xp.array([5e-23, 6e-23, 7e-23]) + frequency = self.xp.array([100, 101, 102]) PSD = bilby.gw.detector.PowerSpectralDensity.from_aligo() psd = PSD.power_spectral_density_interpolated(frequency) duration = 4 @@ -60,11 +65,12 @@ def test_noise_weighted_inner_product(self): gwutils.optimal_snr_squared(aa, psd, duration), gwutils.noise_weighted_inner_product(aa, aa, psd, duration), ) + self.assertEqual(nwip.__array_namespace__(), self.xp) def test_matched_filter_snr(self): - signal = np.array([1e-23, 2e-23, 3e-23]) - frequency_domain_strain = np.array([5e-23, 6e-23, 7e-23]) - frequency = np.array([100, 101, 102]) + signal = self.xp.array([1e-23, 2e-23, 3e-23]) + frequency_domain_strain = self.xp.array([5e-23, 6e-23, 7e-23]) + frequency = self.xp.array([100, 101, 102]) PSD = bilby.gw.detector.PowerSpectralDensity.from_aligo() psd = PSD.power_spectral_density_interpolated(frequency) duration = 4 @@ -73,6 +79,27 @@ def test_matched_filter_snr(self): signal, frequency_domain_strain, psd, duration ) self.assertEqual(mfsnr, 25.510869054168282) + self.assertEqual(mfsnr.__array_namespace__(), self.xp) + + def test_overlap(self): + signal = self.xp.linspace(1e-23, 21e-23, 21) + frequency_domain_strain = self.xp.linspace(5e-23, 25e-23, 21) + frequency = self.xp.linspace(100, 120, 21) + PSD = bilby.gw.detector.PowerSpectralDensity.from_aligo() + psd = PSD.power_spectral_density_interpolated(frequency) + duration = 4 + overlap = gwutils.overlap( + signal, + frequency_domain_strain, + psd, + delta_frequency=1 / duration, + lower_cut_off=3, + upper_cut_off=18, + norm_a=gwutils.optimal_snr_squared(signal, psd, duration), + norm_b=gwutils.optimal_snr_squared(frequency_domain_strain, psd, duration), + ) + self.assertAlmostEqual(overlap, 2.76914407e-05) + self.assertEqual(overlap.__array_namespace__(), self.xp) @pytest.mark.skip(reason="GWOSC unstable: avoiding this test") def test_get_event_time(self): @@ -264,6 +291,8 @@ def test_safe_cast_mode_to_int(self): gwutils.safe_cast_mode_to_int(None) +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestSkyFrameConversion(unittest.TestCase): def setUp(self) -> None: @@ -281,23 +310,37 @@ def tearDown(self) -> None: del self.ifos del self.samples + def test_conversion_single(self) -> None: + sample = self.priors.sample() + zenith = self.xp.asarray(sample["zenith"]) + azimuth = self.xp.asarray(sample["azimuth"]) + time = self.xp.asarray(sample["time"]) + self.ifos.set_array_backend(self.xp) + ra, dec = bilby.gw.utils.zenith_azimuth_to_ra_dec( + zenith, azimuth, time, self.ifos + ) + self.assertEqual(ra.__array_namespace__(), self.xp) + self.assertEqual(dec.__array_namespace__(), self.xp) + def test_conversion_gives_correct_prior(self) -> None: - zeniths = self.samples["zenith"] - azimuths = self.samples["azimuth"] - times = self.samples["time"] - args = zip(*[ - (zenith, azimuth, time, self.ifos) - for zenith, azimuth, time in zip(zeniths, azimuths, times) - ]) - ras, decs = zip(*map(bilby.gw.utils.zenith_azimuth_to_ra_dec, *args)) + zeniths = self.xp.asarray(self.samples["zenith"]) + azimuths = self.xp.asarray(self.samples["azimuth"]) + times = self.xp.asarray(self.samples["time"]) + self.ifos.set_array_backend(self.xp) + ras, decs = bilby.gw.utils.zenith_azimuth_to_ra_dec( + zeniths, azimuths, times, self.ifos + ) self.assertGreaterEqual(ks_2samp(self.samples["ra"], ras).pvalue, 0.01) self.assertGreaterEqual(ks_2samp(self.samples["dec"], decs).pvalue, 0.01) + self.assertEqual(ras.__array_namespace__(), self.xp) + self.assertEqual(decs.__array_namespace__(), self.xp) -def test_ln_i0_mathces_scipy(): +@pytest.mark.array_backend +def test_ln_i0_mathces_scipy(xp): from scipy.special import i0 - values = np.linspace(-10, 10, 101) - assert max(abs(gwutils.ln_i0(values) - np.log(i0(values)))) < 1e-10 + values = xp.linspace(-10, 10, 101) + assert max(abs(gwutils.ln_i0(values) - xp.log(i0(values)))) < 1e-10 if __name__ == "__main__": diff --git a/test/gw/waveform_generator_test.py b/test/gw/waveform_generator_test.py index a8f942ed0..57d2db7c1 100644 --- a/test/gw/waveform_generator_test.py +++ b/test/gw/waveform_generator_test.py @@ -4,6 +4,8 @@ import bilby import lalsimulation import numpy as np +import pytest +from bilby.compat.utils import xp_wrap def dummy_func_array_return_value( @@ -36,16 +38,21 @@ def dummy_func_dict_return_value( return ht +@xp_wrap def dummy_func_array_return_value_2( - array, amplitude, mu, sigma, ra, dec, geocent_time, psi + array, amplitude, mu, sigma, ra, dec, geocent_time, psi, *, xp=None ): - return dict(plus=np.array(array), cross=np.array(array)) + return dict(plus=xp.array(array), cross=xp.array(array)) +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestWaveformGeneratorInstantiationWithoutOptionalParameters(unittest.TestCase): def setUp(self): self.waveform_generator = bilby.gw.waveform_generator.WaveformGenerator( - 1, 4096, frequency_domain_source_model=dummy_func_dict_return_value + self.xp.array(1.0), + self.xp.array(4096.0), + frequency_domain_source_model=dummy_func_dict_return_value, ) self.simulation_parameters = dict( amplitude=1e-21, @@ -118,9 +125,11 @@ def conversion_func(): def test_duration(self): self.assertEqual(self.waveform_generator.duration, 1) + self.assertEqual(self.waveform_generator.duration.__array_namespace__(), self.xp) def test_sampling_frequency(self): self.assertEqual(self.waveform_generator.sampling_frequency, 4096) + self.assertEqual(self.waveform_generator.sampling_frequency.__array_namespace__(), self.xp) def test_source_model(self): self.assertEqual( @@ -129,10 +138,10 @@ def test_source_model(self): ) def test_frequency_array_type(self): - self.assertIsInstance(self.waveform_generator.frequency_array, np.ndarray) + self.assertIsInstance(self.waveform_generator.frequency_array, self.xp.ndarray) def test_time_array_type(self): - self.assertIsInstance(self.waveform_generator.time_array, np.ndarray) + self.assertIsInstance(self.waveform_generator.time_array, self.xp.ndarray) def test_source_model_parameters(self): formatted_parameters = self.waveform_generator._format_parameters( @@ -266,11 +275,13 @@ def conversion_func(): self.assertEqual(conversion_func, self.waveform_generator.parameter_conversion) +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestFrequencyDomainStrainMethod(unittest.TestCase): def setUp(self): self.waveform_generator = bilby.gw.waveform_generator.WaveformGenerator( - duration=1, - sampling_frequency=4096, + duration=self.xp.array(1.0), + sampling_frequency=self.xp.array(4096.0), frequency_domain_source_model=dummy_func_dict_return_value, ) self.simulation_parameters = dict( @@ -312,6 +323,8 @@ def test_frequency_domain_source_model_call(self): ) self.assertTrue(np.array_equal(expected["plus"], actual["plus"])) self.assertTrue(np.array_equal(expected["cross"], actual["cross"])) + self.assertEqual(actual["plus"].__array_namespace__(), self.xp) + self.assertEqual(actual["cross"].__array_namespace__(), self.xp) def test_time_domain_source_model_call_with_ndarray(self): self.waveform_generator.frequency_domain_source_model = None @@ -329,6 +342,7 @@ def side_effect(value, value2): parameters=self.simulation_parameters ) self.assertTrue(np.array_equal(expected, actual)) + self.assertEqual(actual.__array_namespace__(), self.xp) def test_time_domain_source_model_call_with_dict(self): self.waveform_generator.frequency_domain_source_model = None @@ -347,6 +361,8 @@ def side_effect(value, value2): ) self.assertTrue(np.array_equal(expected["plus"], actual["plus"])) self.assertTrue(np.array_equal(expected["cross"], actual["cross"])) + self.assertEqual(actual["plus"].__array_namespace__(), self.xp) + self.assertEqual(actual["cross"].__array_namespace__(), self.xp) def test_no_source_model_given(self): self.waveform_generator.time_domain_source_model = None @@ -456,8 +472,8 @@ def test_frequency_domain_caching_changing_model(self): def test_time_domain_caching_changing_model(self): self.waveform_generator = bilby.gw.waveform_generator.WaveformGenerator( - duration=1, - sampling_frequency=4096, + duration=self.xp.array(1.0), + sampling_frequency=self.xp.array(4096.0), time_domain_source_model=dummy_func_dict_return_value, ) original_waveform = self.waveform_generator.frequency_domain_strain( @@ -472,12 +488,18 @@ def test_time_domain_caching_changing_model(self): self.assertFalse( np.array_equal(original_waveform["plus"], new_waveform["plus"]) ) + self.assertEqual(new_waveform["plus"].__array_namespace__(), self.xp) + self.assertEqual(new_waveform["cross"].__array_namespace__(), self.xp) +@pytest.mark.array_backend +@pytest.mark.usefixtures("xp_class") class TestTimeDomainStrainMethod(unittest.TestCase): def setUp(self): self.waveform_generator = bilby.gw.waveform_generator.WaveformGenerator( - 1, 4096, time_domain_source_model=dummy_func_dict_return_value + self.xp.array(1.0), + self.xp.array(4096.0), + time_domain_source_model=dummy_func_dict_return_value, ) self.simulation_parameters = dict( amplitude=1e-21, @@ -518,6 +540,8 @@ def test_time_domain_source_model_call(self): ) self.assertTrue(np.array_equal(expected["plus"], actual["plus"])) self.assertTrue(np.array_equal(expected["cross"], actual["cross"])) + self.assertEqual(actual["plus"].__array_namespace__(), self.xp) + self.assertEqual(actual["cross"].__array_namespace__(), self.xp) def test_frequency_domain_source_model_call_with_ndarray(self): self.waveform_generator.time_domain_source_model = None @@ -537,6 +561,7 @@ def side_effect(value, value2): parameters=self.simulation_parameters ) self.assertTrue(np.array_equal(expected, actual)) + self.assertEqual(actual.__array_namespace__(), self.xp) def test_frequency_domain_source_model_call_with_dict(self): self.waveform_generator.time_domain_source_model = None @@ -557,6 +582,8 @@ def side_effect(value, value2): ) self.assertTrue(np.array_equal(expected["plus"], actual["plus"])) self.assertTrue(np.array_equal(expected["cross"], actual["cross"])) + self.assertEqual(actual["plus"].__array_namespace__(), self.xp) + self.assertEqual(actual["cross"].__array_namespace__(), self.xp) def test_no_source_model_given(self): self.waveform_generator.time_domain_source_model = None From b41df86c81f073e46af78dcf077afa18a386eee6 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Mon, 2 Feb 2026 10:58:17 -0500 Subject: [PATCH 083/140] DOC: fix doc page formatting --- docs/array_api.rst | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/docs/array_api.rst b/docs/array_api.rst index 57c187d72..f3d293b02 100644 --- a/docs/array_api.rst +++ b/docs/array_api.rst @@ -40,7 +40,7 @@ Basic Prior Usage (Automatic Detection) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The array backend is automatically detected from your input arrays. You typically don't need -to specify the ``xp`` parameter:: +to specify the ``xp`` parameter: .. code-block:: python @@ -62,7 +62,7 @@ Sampling with Array Backends (Explicit xp Required) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When sampling from priors, you **must** explicitly specify the array backend using the ``xp`` parameter, -as there's no input array to infer the backend from:: +as there's no input array to infer the backend from: .. code-block:: python @@ -84,7 +84,7 @@ as there's no input array to infer the backend from:: Prior Dictionaries ~~~~~~~~~~~~~~~~~~ -Prior dictionaries work the same way - automatic detection for most methods, explicit ``xp`` for sampling:: +Prior dictionaries work the same way - automatic detection for most methods, explicit ``xp`` for sampling: .. code-block:: python @@ -237,12 +237,14 @@ Performance Considerations 3. Avoid mixing array types in the same computation 4. For JAX, consider using ``jax.jit`` for repeated computations 5. Profile your code to ensure the chosen backend provides benefits +6. If you find :code:`xp_wrap` is a bottleneck in your code, you can explicitly pass + :code:`xp` to the function/method to skip the automatic backend detection step. Bilby and JIT compilation ~~~~~~~~~~~~~~~~~~~~~~~~~ Currently, Bilby functions are not JIT-compiled by default. -Additionally, many Bilby types are not defined as :code:`JAX`` :code:`PyTrees`, +Additionally, many Bilby types are not defined as :code:`JAX` :code:`PyTrees`, and so cannot be passed as arguments to JIT-compiled functions. We plan to support JIT-compilation for at least some Bilby types in future releases. @@ -254,9 +256,9 @@ When creating custom priors, ensure they support the Array API: Example Implementation ~~~~~~~~~~~~~~~~~~~~~~ -Always include the ``xp`` parameter with a default value:: +Always include the ``xp`` parameter with a default value: -... code-block:: python +.. code-block:: python from bilby.core.prior import Prior @@ -286,7 +288,7 @@ it will be automatically inferred from their input arrays. They only need to spe Using the :code:`xp_wrap`` Decorator ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -For methods that perform array operations, use the ``@xp_wrap`` decorator:: +For methods that perform array operations, use the ``@xp_wrap`` decorator: .. code-block:: python @@ -334,7 +336,7 @@ Method Signature Pattern All array-processing methods in prior classes follow this pattern: -**For methods with @xp_wrap decorator**:: +**For methods with @xp_wrap decorator**: .. code-block:: python @@ -343,7 +345,7 @@ All array-processing methods in prior classes follow this pattern: """Method that uses xp for array operations.""" return xp.some_operation(val) * self.is_in_prior_range(val) -**For methods without @xp_wrap (that use xp directly)**:: +**For methods without @xp_wrap (that use xp directly)**: .. code-block:: python @@ -367,9 +369,9 @@ Located in ``bilby/compat/utils.py``, this decorator: 2. **Provides the appropriate xp** when ``xp=None`` 3. **Maintains backward compatibility** with code that doesn't pass ``xp`` -Example implementation pattern:: +Example implementation pattern: -... code-block:: python +.. code-block:: python from bilby.compat.utils import xp_wrap @@ -388,7 +390,9 @@ Test Structure ~~~~~~~~~~~~~~ When appropriate, tests should verify functionality across different -backends using the ``array_backend`` marker:: +backends using the ``array_backend`` marker: + +.. code-block:: python @pytest.mark.array_backend @pytest.mark.usefixtures("xp_class") @@ -473,7 +477,7 @@ The ``array_api_extra.at`` function provides a unified interface for array updat Usage Examples ~~~~~~~~~~~~~~ -**Conditional update**:: +**Conditional update**: .. code-block:: python @@ -486,7 +490,7 @@ Usage Examples arr = xpx.at(arr)[mask].set(value) return arr -**Increment operation**:: +**Increment operation**: .. code-block:: python @@ -513,7 +517,7 @@ Important Notes 1. **Return value**: Always use the returned array. The operation may create a new array (JAX) or modify in-place (NumPy). -2. **Import**: Import ``array_api_extra`` at the module level:: +2. **Import**: Import ``array_api_extra`` at the module level: .. code-block:: python From 4bf5cd168a4264e5dd34e72706159d89129d3d7c Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Mon, 2 Feb 2026 11:13:07 -0500 Subject: [PATCH 084/140] FMT: fix formatting --- test/gw/conversion_test.py | 1 + test/gw/likelihood_test.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/test/gw/conversion_test.py b/test/gw/conversion_test.py index 1610b4dcd..dcef161e9 100644 --- a/test/gw/conversion_test.py +++ b/test/gw/conversion_test.py @@ -636,6 +636,7 @@ def setUp(self): 'total_mass_source': self.xp.array(6), 'symmetric_mass_ratio': self.xp.array(0.2222222222222222), 'mass_ratio': self.xp.array(0.5)} + def helper_generation_from_keys(self, keys, expected_values, source=False): # Explicitly test the helper generate_component_masses local_test_vars = \ diff --git a/test/gw/likelihood_test.py b/test/gw/likelihood_test.py index fd3e3837f..7cff69397 100644 --- a/test/gw/likelihood_test.py +++ b/test/gw/likelihood_test.py @@ -24,7 +24,7 @@ def __getattr__(self, name): if name == "xp": return self.xp return getattr(self.wfg, name) - + def convert_nested_dict(self, data): if is_array_api_obj(data): return self.xp.array(data) @@ -36,7 +36,7 @@ def convert_nested_dict(self, data): def frequency_domain_strain(self, parameters): wf = self.wfg.frequency_domain_strain(parameters) return self.convert_nested_dict(wf) - + def time_domain_strain(self, parameters): wf = self.wfg.time_domain_strain(parameters) return self.convert_nested_dict(wf) From 9329f7544afd00d654ff8ebc2cd58ec1fa135e44 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Mon, 2 Feb 2026 11:29:57 -0500 Subject: [PATCH 085/140] BUG: fix typo in bilby_cython call --- bilby/gw/compat/cython.py | 2 +- test/conftest.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bilby/gw/compat/cython.py b/bilby/gw/compat/cython.py index f42301875..9d0a69af0 100644 --- a/bilby/gw/compat/cython.py +++ b/bilby/gw/compat/cython.py @@ -47,7 +47,7 @@ def get_polarization_tensor(ra: Real, dec: Real, time: Real, psi: Real, mode: st @dispatch(precedence=1) def rotation_matrix_from_delta(delta: ArrayLike): - return _geometry.rotation_matrix_from_delta_x(delta) + return _geometry.rotation_matrix_from_delta(delta) @dispatch(precedence=1) diff --git a/test/conftest.py b/test/conftest.py index b1668561a..f7f5a17a3 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -22,15 +22,15 @@ def pytest_collection_modifyitems(config, items): if config.getoption("--skip-roqs"): skip_roqs = pytest.mark.skip(reason="Skipping tests that require ROQs") else: - skip_roqs = pytest.mark.noop + skip_roqs = None if config.getoption("--array-backend") is not None: array_only = pytest.mark.skip(reason="Only running backend dependent tests") else: - array_only = pytest.mark.noop + array_only = None for item in items: if "requires_roqs" in item.keywords and config.getoption("--skip-roqs"): item.add_marker(skip_roqs) - elif "array_backend" not in item.keywords: + elif "array_backend" not in item.keywords and array_only is not None: item.add_marker(array_only) From 4a6682959ea20c63a1faf471904f4cdaf241beb1 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Mon, 2 Feb 2026 11:32:49 -0500 Subject: [PATCH 086/140] BUG: fix list input for asd calculation --- bilby/gw/utils.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/bilby/gw/utils.py b/bilby/gw/utils.py index 5f9b14d16..38d3d1482 100644 --- a/bilby/gw/utils.py +++ b/bilby/gw/utils.py @@ -15,7 +15,8 @@ from ..core.utils.constants import solar_mass -def asd_from_freq_series(freq_data, df): +@xp_wrap +def asd_from_freq_series(freq_data, df, *, xp=None): """ Calculate the ASD from the frequency domain output of gaussian_noise() @@ -31,10 +32,11 @@ def asd_from_freq_series(freq_data, df): array_like: array of real-valued normalized frequency domain ASD data """ - return abs(freq_data) * 2 * df**0.5 + return xp.abs(freq_data) * 2 * df**0.5 -def psd_from_freq_series(freq_data, df): +@xp_wrap +def psd_from_freq_series(freq_data, df, *, xp=None): """ Calculate the PSD from the frequency domain output of gaussian_noise() Calls asd_from_freq_series() and squares the output @@ -51,7 +53,7 @@ def psd_from_freq_series(freq_data, df): array_like: Real-valued normalized frequency domain PSD data """ - return asd_from_freq_series(freq_data, df) ** 2 + return asd_from_freq_series(freq_data, df, xp=xp) ** 2 def get_vertex_position_geocentric(latitude, longitude, elevation): From 239613de23a321aac6b02718a49b90ac9d01b290 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Mon, 2 Feb 2026 12:02:16 -0500 Subject: [PATCH 087/140] FMT: fix syntax for array conversion and backend checks --- bilby/compat/patches.py | 4 +- bilby/core/grid.py | 15 ++-- bilby/core/likelihood.py | 13 ++-- bilby/core/prior/analytical.py | 22 +++--- bilby/core/prior/base.py | 2 +- bilby/core/prior/conditional.py | 2 +- bilby/core/prior/dict.py | 6 +- bilby/core/prior/joint.py | 12 ++-- bilby/core/utils/calculus.py | 2 +- bilby/core/utils/io.py | 3 +- bilby/gw/conversion.py | 8 +-- bilby/gw/detector/calibration.py | 9 +-- bilby/gw/detector/geometry.py | 16 ++--- bilby/gw/geometry.py | 22 +++--- bilby/gw/likelihood/roq.py | 3 +- bilby/gw/prior.py | 6 +- bilby/gw/source.py | 2 +- bilby/gw/time.py | 2 +- bilby/gw/utils.py | 2 +- bilby/hyper/likelihood.py | 2 +- docs/array_api.rst | 8 +-- test/conftest.py | 10 +-- test/core/grid_test.py | 6 +- test/core/likelihood_test.py | 51 +++++++------- test/core/prior/analytical_test.py | 105 +++++++++++++++------------- test/core/prior/base_test.py | 3 +- test/core/prior/conditional_test.py | 15 ++-- test/core/prior/dict_test.py | 25 +++---- test/core/prior/prior_test.py | 49 ++++++------- test/core/prior/slabspike_test.py | 22 +++--- test/core/result_test.py | 2 +- test/core/series_test.py | 24 +++---- test/core/utils_test.py | 67 +++++++++--------- test/gw/conversion_test.py | 93 ++++++++++++------------ test/gw/detector/geometry_test.py | 33 ++++----- test/gw/likelihood_test.py | 75 ++++++++++---------- test/gw/prior_test.py | 5 +- test/gw/utils_test.py | 43 ++++++------ test/gw/waveform_generator_test.py | 47 +++++++------ 39 files changed, 434 insertions(+), 402 deletions(-) diff --git a/bilby/compat/patches.py b/bilby/compat/patches.py index db18c3974..19ad0565a 100644 --- a/bilby/compat/patches.py +++ b/bilby/compat/patches.py @@ -30,7 +30,7 @@ def multivariate_logpdf(xp, mean, cov): elif aac.is_torch_namespace(xp): from torch.distributions.multivariate_normal import MultivariateNormal - mvn = MultivariateNormal(loc=mean, covariance_matrix=xp.array(cov)) + mvn = MultivariateNormal(loc=mean, covariance_matrix=xp.asarray(cov)) logpdf = mvn.log_prob else: raise BackendNotImplementedError @@ -39,7 +39,7 @@ def multivariate_logpdf(xp, mean, cov): def logsumexp(a, axis=None, b=None, keepdims=False, return_sign=False, *, xp=None): if xp is None: - xp = a.__array_namespace__() + xp = aac.get_namespace(a) if "jax" in xp.__name__: # the scipy version of logsumexp cannot be vmapped diff --git a/bilby/core/grid.py b/bilby/core/grid.py index a69590080..9fda20882 100644 --- a/bilby/core/grid.py +++ b/bilby/core/grid.py @@ -1,6 +1,7 @@ import json import os +import array_api_compat as aac import numpy as np from .prior import Prior, PriorDict @@ -87,7 +88,7 @@ def __init__( enumerate(self.parameter_names)}, axis=0).reshape( self.mesh_grid[0].shape) else: - self._ln_prior = xp.array(0.0) + self._ln_prior = xp.asarray(0.0) self._ln_likelihood = None # evaluate the likelihood on the grid points @@ -206,7 +207,7 @@ def _marginalize_single(self, log_array, name, non_marg_names=None): non_marg_names.remove(name) places = self.sample_points[name] - xp = log_array.__array_namespace__() + xp = aac.get_namespace(log_array) if len(places) > 1: dx = xp.diff(places) @@ -217,7 +218,7 @@ def _marginalize_single(self, log_array, name, non_marg_names=None): # no marginalisation required, just remove the singleton dimension z = log_array.shape q = xp.arange(0, len(z)).astype(int) != axis - out = xp.reshape(log_array, tuple((xp.array(list(z)))[q])) + out = xp.reshape(log_array, tuple((xp.asarray(list(z)))[q])) return out @@ -295,7 +296,7 @@ def marginalize_likelihood(self, parameters=None, not_parameters=None): """ ln_like = self.marginalize(self.ln_likelihood, parameters=parameters, not_parameters=not_parameters) - xp = ln_like.__array_namespace__() + xp = aac.get_namespace(ln_like) # NOTE: the output will not be properly normalised return xp.exp(ln_like - xp.max(ln_like)) @@ -320,11 +321,11 @@ def marginalize_posterior(self, parameters=None, not_parameters=None): ln_post = self.marginalize(self.ln_posterior, parameters=parameters, not_parameters=not_parameters) # NOTE: the output will not be properly normalised - xp = ln_post.__array_namespace__() + xp = aac.get_namespace(ln_post) return xp.exp(ln_post - xp.max(ln_post)) def _evaluate(self): - xp = self.mesh_grid[0].__array_namespace__() + xp = aac.get_namespace(self.mesh_grid[0]) if xp.__name__ == "jax.numpy": from jax import vmap self._ln_likelihood = vmap(self.likelihood.log_likelihood)( @@ -338,7 +339,7 @@ def _evaluate(self): def _evaluate_recursion(self, dimension, parameters): if dimension == self.n_dims: - xp = self.mesh_grid[0].__array_namespace__() + xp = aac.get_namespace(self.mesh_grid[0]) current_point = tuple([[xp.where( parameters[name] == self.sample_points[name])[0].item()] for name in self.parameter_names]) diff --git a/bilby/core/likelihood.py b/bilby/core/likelihood.py index 86e198e7c..8e1005a11 100644 --- a/bilby/core/likelihood.py +++ b/bilby/core/likelihood.py @@ -1,5 +1,6 @@ import copy +import array_api_compat as aac import numpy as np from array_api_compat import is_array_api_obj from scipy.special import gammaln, xlogy @@ -260,7 +261,7 @@ def log_likelihood(self, parameters): raise ValueError( "Poisson rate function returns wrong value type! " "Is {} when it should be numpy.ndarray".format(type(rate))) - xp = rate.__array_namespace__() + xp = aac.get_namespace(rate) if xp.any(rate < 0.): raise ValueError(("Poisson rate function returns a negative", " value!")) @@ -281,7 +282,7 @@ def y(self): def y(self, y): if not is_array_api_obj(y): y = np.atleast_1d(y) - xp = y.__array_namespace__() + xp = aac.get_namespace(y) # check array is a non-negative integer array if y.dtype.kind not in 'ui' or xp.any(y < 0): raise ValueError("Data must be non-negative integers") @@ -309,7 +310,7 @@ def __init__(self, x, y, func, **kwargs): def log_likelihood(self, parameters): mu = self.func(self.x, **self.model_parameters(parameters=parameters), **self.kwargs) - xp = mu.__array_namespace__() + xp = aac.get_namespace(mu) if xp.any(mu < 0.): return -np.inf return -xp.sum(xp.log(mu) + (self.y / mu)) @@ -326,7 +327,7 @@ def y(self): def y(self, y): if not is_array_api_obj(y): y = np.atleast_1d(y) - xp = y.__array_namespace__() + xp = aac.get_namespace(y) if xp.any(y < 0): raise ValueError("Data must be non-negative") self._y = y @@ -497,7 +498,7 @@ def dim(self): def log_likelihood(self, parameters): xp = array_module(self.cov) - x = xp.array([parameters["x{0}".format(i)] for i in range(self.dim)]) + x = xp.asarray([parameters["x{0}".format(i)] for i in range(self.dim)]) return self.logpdf(x) @@ -536,7 +537,7 @@ def dim(self): def log_likelihood(self, parameters): xp = array_module(self.cov) - x = xp.array([parameters["x{0}".format(i)] for i in range(self.dim)]) + x = xp.asarray([parameters["x{0}".format(i)] for i in range(self.dim)]) return -xp.log(2) + xp.logaddexp(self.logpdf_1(x), self.logpdf_2(x)) diff --git a/bilby/core/prior/analytical.py b/bilby/core/prior/analytical.py index 118e36ce8..9dd120964 100644 --- a/bilby/core/prior/analytical.py +++ b/bilby/core/prior/analytical.py @@ -1441,7 +1441,7 @@ def __init__( xp = array_module(values) nvalues = len(values) - values = xp.array(values) + values = xp.asarray(values) if values.shape != (nvalues,): raise ValueError( f"Shape of argument 'values' must be 1d array-like but has shape {values.shape}" @@ -1461,7 +1461,7 @@ def __init__( self.values = self._values_array.tolist() weights = ( - xp.array(weights) / xp.sum(weights) + xp.asarray(weights) / xp.sum(weights) if weights is not None else xp.ones(self.nvalues) / self.nvalues ) @@ -1497,7 +1497,7 @@ def rescale(self, val, *, xp=None): ======= Union[float, array_like]: Rescaled probability """ - index = xp.searchsorted(self._cumulative_weights_array[1:], val) + index = xp.searchsorted(xp.asarray(self._cumulative_weights_array[1:]), val) return xp.asarray(self._values_array)[index] @xp_wrap @@ -1512,7 +1512,7 @@ def cdf(self, val, *, xp=None): ======= float: cumulative prior probability of val """ - index = xp.searchsorted(self._values_array, val, side="right") + index = xp.searchsorted(xp.asarray(self._values_array), val, side="right") return xp.asarray(self._cumulative_weights_array)[index] @xp_wrap @@ -1527,9 +1527,13 @@ def prob(self, val, *, xp=None): ======= float: Prior probability of val """ - index = xp.searchsorted(self._values_array, val) + index = xp.searchsorted(xp.asarray(self._values_array), val) index = xp.clip(index, 0, self.nvalues - 1) - p = xp.where(self._values_array[index] == val, self._weights_array[index], 0) + p = xp.where( + xp.asarray(self._values_array[index])== val, + xp.asarray(self._weights_array[index]), + xp.asarray(0.0), + ) # turn 0d numpy array to scalar return p[()] @@ -1546,10 +1550,12 @@ def ln_prob(self, val, xp=None): float: """ - index = xp.searchsorted(self._values_array, val) + index = xp.searchsorted(xp.asarray(self._values_array), val) index = xp.clip(index, 0, self.nvalues - 1) lnp = xp.where( - self._values_array[index] == val, self._lnweights_array[index], -np.inf + xp.asarray(self._values_array[index]) == val, + xp.asarray(self._lnweights_array[index]), + -np.inf, ) # turn 0d array to scalar return lnp[()] diff --git a/bilby/core/prior/base.py b/bilby/core/prior/base.py index 671b46233..137f6df5d 100644 --- a/bilby/core/prior/base.py +++ b/bilby/core/prior/base.py @@ -155,7 +155,7 @@ def sample(self, size=None, *, xp=np): from ..utils import random self.least_recently_sampled = self.rescale( - xp.array(random.rng.uniform(0, 1, size)) + xp.asarray(random.rng.uniform(0, 1, size)) ) return self.least_recently_sampled diff --git a/bilby/core/prior/conditional.py b/bilby/core/prior/conditional.py index c221939b8..f42f83239 100644 --- a/bilby/core/prior/conditional.py +++ b/bilby/core/prior/conditional.py @@ -80,7 +80,7 @@ def sample(self, size=None, *, xp=np, **required_variables): from ..utils import random self.least_recently_sampled = self.rescale( - xp.array(random.rng.uniform(0, 1, size)), + xp.asarray(random.rng.uniform(0, 1, size)), xp=xp, **required_variables, ) diff --git a/bilby/core/prior/dict.py b/bilby/core/prior/dict.py index 8b16a1510..47d92b663 100644 --- a/bilby/core/prior/dict.py +++ b/bilby/core/prior/dict.py @@ -385,7 +385,7 @@ def sample_subset_constrained_as_array(self, keys=iter([]), size=None, *, xp=np) samples_dict = self.sample_subset_constrained(keys=keys, size=size, xp=xp) samples_dict = {key: xp.atleast_1d(val) for key, val in samples_dict.items()} samples_list = [samples_dict[key] for key in keys] - return xp.array(samples_list) + return xp.asarray(samples_list) def sample_subset(self, keys=iter([]), size=None, *, xp=np): """Draw samples from the prior set for parameters which are not a DeltaFunction @@ -850,7 +850,7 @@ def ln_prob(self, sample, axis=None, normalized=True): """ self._prepare_evaluation(*zip(*sample.items())) xp = array_module(sample.values()) - res = xp.array([ + res = xp.asarray([ self[key].ln_prob(sample[key], **self.get_required_variables(key)) for key in sample ]) @@ -903,7 +903,7 @@ def rescale(self, keys, theta): else: self[key].least_recently_sampled = result[key] - return xp.array([result[key] for key in keys]) + return xp.asarray([result[key] for key in keys]) def _update_rescale_keys(self, keys): if not keys == self._least_recently_rescaled_keys: diff --git a/bilby/core/prior/joint.py b/bilby/core/prior/joint.py index 0e8e8abfa..924a24c6e 100644 --- a/bilby/core/prior/joint.py +++ b/bilby/core/prior/joint.py @@ -317,7 +317,7 @@ def rescale(self, value, *, xp=None, **kwargs): An vector sample drawn from the multivariate Gaussian distribution. """ - samp = xp.array(value) + samp = xp.asarray(value) if len(samp.shape) == 1: samp = samp.reshape(1, self.num_vars) @@ -627,7 +627,7 @@ def _rescale(self, samp, *, xp=None, **kwargs): samp = erfinv(2.0 * samp - 1) * 2.0 ** 0.5 # rotate and scale to the multivariate normal shape - samp = xp.array(self.mus[mode]) + self.sigmas[mode] * xp.einsum( + samp = xp.asarray(self.mus[mode]) + self.sigmas[mode] * xp.einsum( "ij,kj->ik", samp * self.sqeigvalues[mode], self.eigvectors[mode] ) return samp @@ -674,7 +674,7 @@ def _sample(self, size, *, xp=np, **kwargs): if not outbound: inbound = True - return xp.array(samps) + return xp.asarray(samps) @xp_wrap def _ln_prob(self, samp, lnprob, outbounds, *, xp=None): @@ -801,7 +801,7 @@ def rescale(self, val, *, xp=None, **kwargs): self.dist.rescale_parameters[self.name] = val if self.dist.filled_rescale(): - values = xp.array(list(self.dist.rescale_parameters.values())).T + values = xp.asarray(list(self.dist.rescale_parameters.values())).T samples = self.dist.rescale(values, **kwargs) self.dist.reset_rescale() return samples @@ -871,14 +871,14 @@ def ln_prob(self, val, *, xp=None): # check for the same number of values for each parameter shapes = set() for v in values: - shapes.add(xp.array(v).shape) + shapes.add(xp.asarray(v).shape) if len(shapes) > 1: raise ValueError( "Each parameter must have the same " "number of requested values." ) - lnp = self.dist.ln_prob(xp.array(values).T) + lnp = self.dist.ln_prob(xp.asarray(values).T) # reset the requested parameters self.dist.reset_request() diff --git a/bilby/core/utils/calculus.py b/bilby/core/utils/calculus.py index 889e086b0..6852299ec 100644 --- a/bilby/core/utils/calculus.py +++ b/bilby/core/utils/calculus.py @@ -194,7 +194,7 @@ def logtrapzexp(lnf, dx, *, xp=np): else: raise TypeError("Step size must be a single value or array-like") - return C + logsumexp(xp.array([logsumexp(lnfdx1), logsumexp(lnfdx2)])) + return C + logsumexp(xp.asarray([logsumexp(lnfdx1), logsumexp(lnfdx2)])) class interp1d(_interp1d): diff --git a/bilby/core/utils/io.py b/bilby/core/utils/io.py index a5502a1a6..d24b16fe2 100644 --- a/bilby/core/utils/io.py +++ b/bilby/core/utils/io.py @@ -8,6 +8,7 @@ from pathlib import Path from datetime import timedelta +import array_api_compat as aac import numpy as np import pandas as pd @@ -62,7 +63,7 @@ def default(self, obj): if hasattr(obj, "__array_namespace__"): return { "__array__": True, - "__array_namespace__": obj.__array_namespace__().__name__, + "__array_namespace__": aac.get_namespace(obj).__name__, "content": obj.tolist(), } if isinstance(obj, complex): diff --git a/bilby/gw/conversion.py b/bilby/gw/conversion.py index 3dcb6962c..af3faa96c 100644 --- a/bilby/gw/conversion.py +++ b/bilby/gw/conversion.py @@ -632,8 +632,8 @@ def spectral_pca_to_spectral(gamma_pca_0, gamma_pca_1, gamma_pca_2, gamma_pca_3) ''' xp = array_module(gamma_pca_0) - sampled_pca_gammas = xp.array([gamma_pca_0, gamma_pca_1, gamma_pca_2, gamma_pca_3]) - transformation_matrix = xp.array( + sampled_pca_gammas = xp.asarray([gamma_pca_0, gamma_pca_1, gamma_pca_2, gamma_pca_3]) + transformation_matrix = xp.asarray( [ [0.43801, -0.76705, 0.45143, 0.12646], [-0.53573, 0.17169, 0.67968, 0.47070], @@ -642,8 +642,8 @@ def spectral_pca_to_spectral(gamma_pca_0, gamma_pca_1, gamma_pca_2, gamma_pca_3) ] ) - model_space_mean = xp.array([0.89421, 0.33878, -0.07894, 0.00393]) - model_space_standard_deviation = xp.array([0.35700, 0.25769, 0.05452, 0.00312]) + model_space_mean = xp.asarray([0.89421, 0.33878, -0.07894, 0.00393]) + model_space_standard_deviation = xp.asarray([0.35700, 0.25769, 0.05452, 0.00312]) converted_gamma_parameters = \ model_space_mean + model_space_standard_deviation * xp.dot(transformation_matrix, sampled_pca_gammas) diff --git a/bilby/gw/detector/calibration.py b/bilby/gw/detector/calibration.py index 6f7390bde..883275016 100644 --- a/bilby/gw/detector/calibration.py +++ b/bilby/gw/detector/calibration.py @@ -42,6 +42,7 @@ import copy import os +import array_api_compat as aac import numpy as np import pandas as pd from array_api_compat import is_jax_namespace @@ -333,9 +334,9 @@ def __repr__(self): def _evaluate_spline(self, kind, a, b, c, d, previous_nodes): """Evaluate Eq. (1) in https://dcc.ligo.org/LIGO-T2300140""" xp = array_module(self.params[f"{kind}_0"]) - parameters = xp.array([self.params[f"{kind}_{ii}"] for ii in range(self.n_points)]) + parameters = xp.asarray([self.params[f"{kind}_{ii}"] for ii in range(self.n_points)]) next_nodes = previous_nodes + 1 - nodes = xp.array(self.nodes_to_spline_coefficients) + nodes = xp.asarray(self.nodes_to_spline_coefficients) spline_coefficients = nodes.dot(parameters) return ( a * parameters[previous_nodes] @@ -377,7 +378,7 @@ def get_calibration_factor(self, frequency_array, **params): delta_amplitude = self._evaluate_spline("amplitude", a, b, c, d, previous_nodes) delta_phase = self._evaluate_spline("phase", a, b, c, d, previous_nodes) calibration_factor = (1 + delta_amplitude) * (2 + 1j * delta_phase) / (2 - 1j * delta_phase) - xp = calibration_factor.__array_namespace__() + xp = aac.get_namespace(calibration_factor) return xp.nan_to_num(calibration_factor) @@ -412,7 +413,7 @@ def get_calibration_factor(self, frequency_array, **params): if idx is None: raise KeyError(f"Calibration index for {self.label} not found.") - xp = frequency_array.__array_namespace__() + xp = aac.get_namespace(frequency_array) if not xp.array_equal(frequency_array, self.frequency_array): intersection, mask, _ = xp.intersect1d( frequency_array, self.frequency_array, return_indices=True diff --git a/bilby/gw/detector/geometry.py b/bilby/gw/detector/geometry.py index 5d0de9b9f..a6c2df168 100644 --- a/bilby/gw/detector/geometry.py +++ b/bilby/gw/detector/geometry.py @@ -306,11 +306,11 @@ def unit_vector_along_arm(self, arm): raise ValueError("Arm must either be 'x' or 'y'.") def set_array_backend(self, xp): - self.length = xp.array(self.length) - self.latitude = xp.array(self.latitude) - self.longitude = xp.array(self.longitude) - self.elevation = xp.array(self.elevation) - self.xarm_azimuth = xp.array(self.xarm_azimuth) - self.yarm_azimuth = xp.array(self.yarm_azimuth) - self.xarm_tilt = xp.array(self.xarm_tilt) - self.yarm_tilt = xp.array(self.yarm_tilt) + self.length = xp.asarray(self.length) + self.latitude = xp.asarray(self.latitude) + self.longitude = xp.asarray(self.longitude) + self.elevation = xp.asarray(self.elevation) + self.xarm_azimuth = xp.asarray(self.xarm_azimuth) + self.yarm_azimuth = xp.asarray(self.yarm_azimuth) + self.xarm_tilt = xp.asarray(self.xarm_tilt) + self.yarm_tilt = xp.asarray(self.yarm_tilt) diff --git a/bilby/gw/geometry.py b/bilby/gw/geometry.py index c07ec5c0c..54d2f3a1d 100644 --- a/bilby/gw/geometry.py +++ b/bilby/gw/geometry.py @@ -30,15 +30,15 @@ def antenna_response(detector_tensor, ra, dec, time, psi, mode): def calculate_arm(arm_tilt, arm_azimuth, longitude, latitude): """""" xp = array_module(arm_tilt) - e_long = xp.array([-xp.sin(longitude), xp.cos(longitude), longitude * 0]) - e_lat = xp.array( + e_long = xp.asarray([-xp.sin(longitude), xp.cos(longitude), longitude * 0]) + e_lat = xp.asarray( [ -xp.sin(latitude) * xp.cos(longitude), -xp.sin(latitude) * xp.sin(longitude), xp.cos(latitude), ] ) - e_h = xp.array( + e_h = xp.asarray( [ xp.cos(latitude) * xp.cos(longitude), xp.cos(latitude) * xp.sin(longitude), @@ -70,17 +70,17 @@ def get_polarization_tensor(ra, dec, time, psi, mode): gmst = greenwich_mean_sidereal_time(time) % (2 * xp.pi) phi = ra - gmst theta = xp.atleast_1d(xp.pi / 2 - dec).squeeze() - u = xp.array( + u = xp.asarray( [ xp.cos(phi) * xp.cos(theta), xp.cos(theta) * xp.sin(phi), -xp.sin(theta) * xp.ones_like(phi), ] ) - v = xp.array([ + v = xp.asarray([ -xp.sin(phi), xp.cos(phi), xp.zeros_like(phi) ]) * xp.ones_like(theta) - omega = xp.array([ + omega = xp.asarray([ xp.sin(xp.pi - theta) * xp.cos(xp.pi + phi), xp.sin(xp.pi - theta) * xp.sin(xp.pi + phi), xp.cos(xp.pi - theta) * xp.ones_like(phi), @@ -124,21 +124,21 @@ def rotation_matrix_from_delta(delta_x): alpha = xp.arctan2(-delta_x[1] * delta_x[2], delta_x[0]) beta = xp.arccos(delta_x[2]) gamma = xp.arctan2(delta_x[1], delta_x[0]) - rotation_1 = xp.array( + rotation_1 = xp.asarray( [ [xp.cos(alpha), -xp.sin(alpha), xp.zeros(alpha.shape)], [xp.sin(alpha), xp.cos(alpha), xp.zeros(alpha.shape)], [xp.zeros(alpha.shape), xp.zeros(alpha.shape), xp.ones(alpha.shape)], ] ) - rotation_2 = xp.array( + rotation_2 = xp.asarray( [ [xp.cos(beta), xp.zeros(beta.shape), xp.sin(beta)], [xp.zeros(beta.shape), xp.ones(beta.shape), xp.zeros(beta.shape)], [-xp.sin(beta), xp.zeros(beta.shape), xp.cos(beta)], ] ) - rotation_3 = xp.array( + rotation_3 = xp.asarray( [ [xp.cos(gamma), -xp.sin(gamma), xp.zeros(gamma.shape)], [xp.sin(gamma), xp.cos(gamma), xp.zeros(gamma.shape)], @@ -163,7 +163,7 @@ def time_delay_geocentric(detector1, detector2, ra, dec, time): speed_of_light = 299792458.0 phi = ra - gmst theta = xp.pi / 2 - dec - omega = xp.array( + omega = xp.asarray( [xp.sin(theta) * xp.cos(phi), xp.sin(theta) * xp.sin(phi), xp.cos(theta)] ) delta_d = detector2 - detector1 @@ -181,7 +181,7 @@ def time_delay_from_geocenter(detector1, ra, dec, time): def zenith_azimuth_to_theta_phi(zenith, azimuth, delta_x): """""" xp = array_module(delta_x) - omega_prime = xp.array( + omega_prime = xp.asarray( [ xp.sin(zenith) * xp.cos(azimuth), xp.sin(zenith) * xp.sin(azimuth), diff --git a/bilby/gw/likelihood/roq.py b/bilby/gw/likelihood/roq.py index f85b72668..dbe4d58db 100644 --- a/bilby/gw/likelihood/roq.py +++ b/bilby/gw/likelihood/roq.py @@ -1,4 +1,5 @@ +import array_api_compat as aac import numpy as np from .base import GravitationalWaveTransient @@ -565,7 +566,7 @@ def _interp_five_samples(time_samples, values, time): value: float The value of the function at the input time """ - xp = time_samples.__array_namespace__() + xp = aac.get_namespace(time_samples) r1 = (-values[0] + 8. * values[1] - 14. * values[2] + 8. * values[3] - values[4]) / 4. r2 = values[2] - 2. * values[3] + values[4] a = (time_samples[3] - time) / xp.maximum(time_samples[1] - time_samples[0], 1e-12) diff --git a/bilby/gw/prior.py b/bilby/gw/prior.py index 3fe9ed242..cd0cf182d 100644 --- a/bilby/gw/prior.py +++ b/bilby/gw/prior.py @@ -1554,11 +1554,11 @@ def _rescale(self, samp, *, xp=np, **kwargs): for i, val in enumerate(pix_rescale): theta, ra = self.hp.pix2ang(self.nside, int(round(val))) dec = 0.5 * np.pi - theta - sample = xpx.at(sample, i).set(xp.array(self.draw_from_pixel(ra, dec, int(round(val))))) + sample = xpx.at(sample, i).set(xp.asarray(self.draw_from_pixel(ra, dec, int(round(val))))) if self.distance: self.update_distance(int(round(val))) dist_samples = xpx.at(dist_samples, i).set( - xp.array(self.distance_icdf(dist_samp[i])) + xp.asarray(self.distance_icdf(dist_samp[i])) ) if self.distance: sample = xp.vstack([sample[:, 0], sample[:, 1], dist_samples]) @@ -1638,7 +1638,7 @@ def _sample(self, size, *, xp=np, **kwargs): sample[samp, :] = [ra_dec[0], ra_dec[1], dist] else: sample[samp, :] = self.draw_from_pixel(ra, dec, sample_pix[samp]) - return xp.array(sample.reshape((-1, self.num_vars))) + return xp.asarray(sample.reshape((-1, self.num_vars))) def draw_distance(self, pix): """ diff --git a/bilby/gw/source.py b/bilby/gw/source.py index 21ba19eac..11411b468 100644 --- a/bilby/gw/source.py +++ b/bilby/gw/source.py @@ -1293,7 +1293,7 @@ def supernova_pca_model( coefficients = [pc_coeff1, pc_coeff2, pc_coeff3, pc_coeff4, pc_coeff5] strain = xp.sum( - xp.array([coeff * principal_components[:, ii] for ii, coeff in enumerate(coefficients)]), + xp.asarray([coeff * principal_components[:, ii] for ii, coeff in enumerate(coefficients)]), axis=0 ) diff --git a/bilby/gw/time.py b/bilby/gw/time.py index 996bad070..3c115646b 100644 --- a/bilby/gw/time.py +++ b/bilby/gw/time.py @@ -189,7 +189,7 @@ def n_leap_seconds(gps_time, leap_seconds): @dispatch def n_leap_seconds(gps_time: np.ndarray | float | int): # noqa F811 xp = array_module(gps_time) - return n_leap_seconds(gps_time, xp.array(LEAP_SECONDS)) + return n_leap_seconds(gps_time, xp.asarray(LEAP_SECONDS)) @dispatch diff --git a/bilby/gw/utils.py b/bilby/gw/utils.py index 38d3d1482..a5fbfe59a 100644 --- a/bilby/gw/utils.py +++ b/bilby/gw/utils.py @@ -85,7 +85,7 @@ def get_vertex_position_geocentric(latitude, longitude, elevation): x_comp = (radius + elevation) * xp.cos(latitude) * xp.cos(longitude) y_comp = (radius + elevation) * xp.cos(latitude) * xp.sin(longitude) z_comp = ((semi_minor_axis / semi_major_axis)**2 * radius + elevation) * xp.sin(latitude) - return xp.array([x_comp, y_comp, z_comp]) + return xp.asarray([x_comp, y_comp, z_comp]) def inner_product(aa, bb, frequency, PSD): diff --git a/bilby/hyper/likelihood.py b/bilby/hyper/likelihood.py index fbde98f38..f37ba1333 100644 --- a/bilby/hyper/likelihood.py +++ b/bilby/hyper/likelihood.py @@ -110,5 +110,5 @@ def resample_posteriors(self, max_samples=None, xp=np): for key in data: data[key].append(temp[key]) for key in data: - data[key] = xp.array(data[key]) + data[key] = xp.asarray(data[key]) return data diff --git a/docs/array_api.rst b/docs/array_api.rst index f3d293b02..6ce77c28a 100644 --- a/docs/array_api.rst +++ b/docs/array_api.rst @@ -351,7 +351,7 @@ All array-processing methods in prior classes follow this pattern: def sample(self, size=None, *, xp=np): """Method that uses xp but isn't wrapped.""" - return xp.array(random.rng.uniform(0, 1, size)) + return xp.asarray(random.rng.uniform(0, 1, size)) Key rules: @@ -399,17 +399,17 @@ backends using the ``array_backend`` marker: class TestMyPrior: def test_prob(self): prior = MyPrior() - val = self.xp.array([0.5, 1.5, 2.5]) + val = self.xp.asarray([0.5, 1.5, 2.5]) # No need to pass xp - automatically detected prob = prior.prob(val) assert self.xp.all(prob >= 0) - assert prob.__array_namespace__() == self.xp + assert aac.get_namespace(prob) == self.xp def test_sample(self): prior = MyPrior() # Sampling requires explicit xp samples = prior.sample(size=100, xp=self.xp) - assert samples.__array_namespace__() == self.xp + assert aac.get_namespace(samples) == self.xp The array_backend Marker ~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/test/conftest.py b/test/conftest.py index f7f5a17a3..2bcd416cd 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -1,4 +1,6 @@ import importlib + +import array_api_compat as aac import pytest @@ -38,20 +40,20 @@ def _xp(request): backend = request.config.getoption("--array-backend") match backend: case None | "numpy": - import numpy - return numpy + import numpy as xp case "jax" | "jax.numpy": import os import jax os.environ["SCIPY_ARRAY_API"] = "1" jax.config.update("jax_enable_x64", True) - return jax.numpy + xp = jax.numpy case _: try: - importlib.import_module(backend) + xp = importlib.import_module(backend) except ImportError: raise ValueError(f"Unknown backend for testing: {backend}") + return aac.get_namespace(xp.ones(1)) @pytest.fixture diff --git a/test/core/grid_test.py b/test/core/grid_test.py index d99af8327..009ab2c15 100644 --- a/test/core/grid_test.py +++ b/test/core/grid_test.py @@ -14,8 +14,8 @@ class MultiGaussian(bilby.Likelihood): def __init__(self, mean, cov, *, xp=np): super(MultiGaussian, self).__init__() self.xp = xp - self.cov = xp.array(cov) - self.mean = xp.array(mean) + self.cov = xp.asarray(cov) + self.mean = xp.asarray(mean) self.sigma = xp.sqrt(xp.diag(self.cov)) self.logpdf = multivariate_logpdf(xp=xp, mean=self.mean, cov=self.cov) @@ -24,7 +24,7 @@ def dim(self): return len(self.cov[0]) def log_likelihood(self, parameters): - x = self.xp.array([parameters["x{0}".format(i)] for i in range(self.dim)]) + x = self.xp.asarray([parameters["x{0}".format(i)] for i in range(self.dim)]) return self.logpdf(x) diff --git a/test/core/likelihood_test.py b/test/core/likelihood_test.py index 3406cc857..aa1f92d59 100644 --- a/test/core/likelihood_test.py +++ b/test/core/likelihood_test.py @@ -1,6 +1,7 @@ import unittest from unittest import mock +import array_api_compat as aac import numpy as np import pytest import array_api_extra as xpx @@ -165,13 +166,13 @@ def setUp(self): self.N = 100 self.sigma = 0.1 self.x = self.xp.linspace(0, 1, self.N) - self.y = 2 * self.x + 1 + self.xp.array(np.random.normal(0, self.sigma, self.N)) + self.y = 2 * self.x + 1 + self.xp.asarray(np.random.normal(0, self.sigma, self.N)) def test_function(x, m, c): return m * x + c self.function = test_function - self.parameters = dict(m=self.xp.array(2.0), c=self.xp.array(0.0)) + self.parameters = dict(m=self.xp.asarray(2.0), c=self.xp.asarray(0.0)) def tearDown(self): del self.N @@ -221,7 +222,7 @@ def test_repr(self): def test_return_class(self): likelihood = GaussianLikelihood(self.x, self.y, self.function, self.sigma) logl = likelihood.log_likelihood(self.parameters) - self.assertEqual(logl.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(logl), self.xp) @pytest.mark.array_backend @@ -232,13 +233,13 @@ def setUp(self): self.nu = self.N - 2 self.sigma = 1 self.x = self.xp.linspace(0, 1, self.N) - self.y = 2 * self.x + 1 + self.xp.array(np.random.normal(0, self.sigma, self.N)) + self.y = 2 * self.x + 1 + self.xp.asarray(np.random.normal(0, self.sigma, self.N)) def test_function(x, m, c): return m * x + c self.function = test_function - self.parameters = dict(m=self.xp.array(2.0), c=self.xp.array(0.0)) + self.parameters = dict(m=self.xp.asarray(2.0), c=self.xp.asarray(0.0)) def tearDown(self): del self.N @@ -306,7 +307,7 @@ def setUp(self): self.N = 100 self.mu = 5 self.x = self.xp.linspace(0, 1, self.N) - self.y = self.xp.array(np.random.poisson(self.mu, self.N)) + self.y = self.xp.asarray(np.random.poisson(self.mu, self.N)) self.yfloat = self.y.copy() * 1.0 self.yneg = self.y.copy() self.yneg = xpx.at(self.yneg, 0).set(-1) @@ -320,7 +321,7 @@ def test_function_array(x, c): self.function = test_function self.function_array = test_function_array self.poisson_likelihood = PoissonLikelihood(self.x, self.y, self.function) - self.bad_parameters = dict(c=self.xp.array(-2.0)) + self.bad_parameters = dict(c=self.xp.asarray(-2.0)) def tearDown(self): del self.N @@ -383,14 +384,14 @@ def test_log_likelihood_wrong_func_return_type(self): def test_log_likelihood_negative_func_return_element(self): poisson_likelihood = PoissonLikelihood( - x=self.x, y=self.y, func=lambda x: self.xp.array([3, 6, -2]) + x=self.x, y=self.y, func=lambda x: self.xp.asarray([3, 6, -2]) ) with self.assertRaises(ValueError): poisson_likelihood.log_likelihood(dict()) def test_log_likelihood_zero_func_return_element(self): poisson_likelihood = PoissonLikelihood( - x=self.x, y=self.y, func=lambda x: self.xp.array([3, 6, 0]) + x=self.x, y=self.y, func=lambda x: self.xp.asarray([3, 6, 0]) ) self.assertEqual(-np.inf, poisson_likelihood.log_likelihood(dict())) @@ -418,7 +419,7 @@ def setUp(self): self.N = 100 self.mu = 5 self.x = self.xp.linspace(0, 1, self.N) - self.y = self.xp.array(np.random.exponential(self.mu, self.N)) + self.y = self.xp.asarray(np.random.exponential(self.mu, self.N)) self.yneg = self.y.copy() self.yneg = xpx.at(self.yneg, 0).set(-1.0) @@ -433,7 +434,7 @@ def test_function_array(x, c): self.exponential_likelihood = ExponentialLikelihood( x=self.x, y=self.y, func=self.function ) - self.bad_parameters = dict(c=self.xp.array(-1.0)) + self.bad_parameters = dict(c=self.xp.asarray(-1.0)) def tearDown(self): del self.N @@ -487,12 +488,12 @@ def test_set_y_to_negative_float(self): def test_set_y_to_nd_array_with_negative_element(self): with self.assertRaises(ValueError): - self.exponential_likelihood.y = self.xp.array([4.3, -1.2, 4]) + self.exponential_likelihood.y = self.xp.asarray([4.3, -1.2, 4]) def test_log_likelihood_default(self): """ Merely tests that it ends up at the right place in the code """ exponential_likelihood = ExponentialLikelihood( - x=self.x, y=self.y, func=lambda x: self.xp.array([4.2]) + x=self.x, y=self.y, func=lambda x: self.xp.asarray([4.2]) ) with mock.patch(f"{self.xp.__name__}.sum") as m: m.return_value = 3 @@ -513,9 +514,9 @@ def setUp(self): self.sigma = [1, 2, 3] self.mean = [10, 11, 12] if self.xp != np: - self.cov = self.xp.array(self.cov) - self.sigma = self.xp.array(self.sigma) - self.mean = self.xp.array(self.mean) + self.cov = self.xp.asarray(self.cov) + self.sigma = self.xp.asarray(self.sigma) + self.mean = self.xp.asarray(self.mean) self.likelihood = AnalyticalMultidimensionalCovariantGaussian( mean=self.mean, cov=self.cov ) @@ -540,14 +541,14 @@ def test_dim(self): def test_log_likelihood(self): likelihood = AnalyticalMultidimensionalCovariantGaussian( - mean=self.xp.array([0]), cov=self.xp.array([1]) + mean=self.xp.asarray([0]), cov=self.xp.asarray([1]) ) - logl = likelihood.log_likelihood(dict(x0=self.xp.array(0.0))) + logl = likelihood.log_likelihood(dict(x0=self.xp.asarray(0.0))) self.assertEqual( -np.log(2 * np.pi) / 2, - likelihood.log_likelihood(dict(x0=self.xp.array(0.0))), + likelihood.log_likelihood(dict(x0=self.xp.asarray(0.0))), ) - self.assertEqual(logl.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(logl), self.xp) @pytest.mark.array_backend @@ -559,10 +560,10 @@ def setUp(self): self.mean_1 = [10, 11, 12] self.mean_2 = [20, 21, 22] if self.xp != np: - self.cov = self.xp.array(self.cov) - self.sigma = self.xp.array(self.sigma) - self.mean_1 = self.xp.array(self.mean_1) - self.mean_2 = self.xp.array(self.mean_2) + self.cov = self.xp.asarray(self.cov) + self.sigma = self.xp.asarray(self.sigma) + self.mean_1 = self.xp.asarray(self.mean_1) + self.mean_2 = self.xp.asarray(self.mean_2) self.likelihood = AnalyticalMultidimensionalBimodalCovariantGaussian( mean_1=self.mean_1, mean_2=self.mean_2, cov=self.cov ) @@ -595,7 +596,7 @@ def test_log_likelihood(self): ) self.assertEqual( -np.log(2 * np.pi) / 2, - likelihood.log_likelihood(dict(x0=self.xp.array(0.0))), + likelihood.log_likelihood(dict(x0=self.xp.asarray(0.0))), ) diff --git a/test/core/prior/analytical_test.py b/test/core/prior/analytical_test.py index 8325be232..dddc0dfba 100644 --- a/test/core/prior/analytical_test.py +++ b/test/core/prior/analytical_test.py @@ -1,5 +1,6 @@ import unittest +import array_api_compat as aac import bilby import numpy as np import pytest @@ -38,23 +39,23 @@ def test_single_probability(self): N = 3 values = [1.1, 2.2, 300.0] discrete_value_prior = bilby.core.prior.DiscreteValues(values) - self.assertEqual(discrete_value_prior.prob(self.xp.array(1.1)), 1 / N) - self.assertEqual(discrete_value_prior.prob(self.xp.array(2.2)), 1 / N) - self.assertEqual(discrete_value_prior.prob(self.xp.array(300.0)), 1 / N) - self.assertEqual(discrete_value_prior.prob(self.xp.array(0.5)), 0) - self.assertEqual(discrete_value_prior.prob(self.xp.array(200)), 0) + self.assertEqual(discrete_value_prior.prob(self.xp.asarray(1.1)), 1 / N) + self.assertEqual(discrete_value_prior.prob(self.xp.asarray(2.2)), 1 / N) + self.assertEqual(discrete_value_prior.prob(self.xp.asarray(300.0)), 1 / N) + self.assertEqual(discrete_value_prior.prob(self.xp.asarray(0.5)), 0) + self.assertEqual(discrete_value_prior.prob(self.xp.asarray(200)), 0) def test_single_probability_unsorted(self): N = 3 values = [1.1, 300, 2.2] discrete_value_prior = bilby.core.prior.DiscreteValues(values) - self.assertEqual(discrete_value_prior.prob(self.xp.array(1.1)), 1 / N) - self.assertEqual(discrete_value_prior.prob(self.xp.array(2.2)), 1 / N) - self.assertEqual(discrete_value_prior.prob(self.xp.array(300.0)), 1 / N) - self.assertEqual(discrete_value_prior.prob(self.xp.array(0.5)), 0) - self.assertEqual(discrete_value_prior.prob(self.xp.array(200)), 0) + self.assertEqual(discrete_value_prior.prob(self.xp.asarray(1.1)), 1 / N) + self.assertEqual(discrete_value_prior.prob(self.xp.asarray(2.2)), 1 / N) + self.assertEqual(discrete_value_prior.prob(self.xp.asarray(300.0)), 1 / N) + self.assertEqual(discrete_value_prior.prob(self.xp.asarray(0.5)), 0) + self.assertEqual(discrete_value_prior.prob(self.xp.asarray(200)), 0) self.assertEqual( - discrete_value_prior.prob(self.xp.array(0.5)).__array_namespace__(), + aac.get_namespace(discrete_value_prior.prob(self.xp.asarray(0.5))), self.xp, ) @@ -64,7 +65,7 @@ def test_array_probability(self): discrete_value_prior = bilby.core.prior.DiscreteValues(values) self.assertTrue( np.all( - discrete_value_prior.prob(self.xp.array([1.1, 2.2, 2.2, 300.0, 200.0])) + discrete_value_prior.prob(self.xp.asarray([1.1, 2.2, 2.2, 300.0, 200.0])) == np.array([1 / N, 1 / N, 1 / N, 1 / N, 0]) ) ) @@ -73,12 +74,12 @@ def test_single_lnprobability(self): N = 3 values = [1.1, 2.2, 300.0] discrete_value_prior = bilby.core.prior.DiscreteValues(values) - self.assertEqual(discrete_value_prior.ln_prob(self.xp.array(1.1)), -np.log(N)) - self.assertEqual(discrete_value_prior.ln_prob(self.xp.array(2.2)), -np.log(N)) - self.assertEqual(discrete_value_prior.ln_prob(self.xp.array(300)), -np.log(N)) - self.assertEqual(discrete_value_prior.ln_prob(self.xp.array(150)), -np.inf) + self.assertEqual(discrete_value_prior.ln_prob(self.xp.asarray(1.1)), -np.log(N)) + self.assertEqual(discrete_value_prior.ln_prob(self.xp.asarray(2.2)), -np.log(N)) + self.assertEqual(discrete_value_prior.ln_prob(self.xp.asarray(300)), -np.log(N)) + self.assertEqual(discrete_value_prior.ln_prob(self.xp.asarray(150)), -np.inf) self.assertEqual( - discrete_value_prior.ln_prob(self.xp.array(0.5)).__array_namespace__(), + aac.get_namespace(discrete_value_prior.ln_prob(self.xp.asarray(0.5))), self.xp, ) @@ -88,7 +89,7 @@ def test_array_lnprobability(self): discrete_value_prior = bilby.core.prior.DiscreteValues(values) self.assertTrue( np.all( - discrete_value_prior.ln_prob(self.xp.array([1.1, 2.2, 2.2, 300, 150])) + discrete_value_prior.ln_prob(self.xp.asarray([1.1, 2.2, 2.2, 300, 150])) == np.array([-np.log(N), -np.log(N), -np.log(N), -np.log(N), -np.inf]) ) ) @@ -111,6 +112,8 @@ def test_array_sample(self): categorical_prior = bilby.core.prior.Categorical(ncat) N = 100000 s = categorical_prior.sample(N, xp=self.xp) + self.assertEqual(aac.get_namespace(s), self.xp) + s = np.asarray(s) zeros = np.sum(s == 0) ones = np.sum(s == 1) twos = np.sum(s == 2) @@ -120,46 +123,48 @@ def test_array_sample(self): self.assertAlmostEqual(ones / N, 1 / ncat, places=int(np.log10(np.sqrt(N)))) self.assertAlmostEqual(twos / N, 1 / ncat, places=int(np.log10(np.sqrt(N)))) self.assertAlmostEqual(threes / N, 1 / ncat, places=int(np.log10(np.sqrt(N)))) - self.assertEqual(s.__array_namespace__(), self.xp) def test_single_probability(self): N = 3 categorical_prior = bilby.core.prior.Categorical(N) - self.assertEqual(categorical_prior.prob(self.xp.array(0)), 1 / N) - self.assertEqual(categorical_prior.prob(self.xp.array(1)), 1 / N) - self.assertEqual(categorical_prior.prob(self.xp.array(2)), 1 / N) - self.assertEqual(categorical_prior.prob(self.xp.array(0.5)), 0) + self.assertEqual(categorical_prior.prob(self.xp.asarray(0)), 1 / N) + self.assertEqual(categorical_prior.prob(self.xp.asarray(1)), 1 / N) + self.assertEqual(categorical_prior.prob(self.xp.asarray(2)), 1 / N) + self.assertEqual(categorical_prior.prob(self.xp.asarray(0.5)), 0) self.assertEqual( - categorical_prior.prob(self.xp.array(0.5)).__array_namespace__(), + aac.get_namespace(categorical_prior.prob(self.xp.asarray(0.5))), self.xp, ) def test_array_probability(self): N = 3 categorical_prior = bilby.core.prior.Categorical(N) + probs = categorical_prior.prob(self.xp.asarray([0, 1, 1, 2, 3])) + self.assertEqual(aac.get_namespace(probs), self.xp) + self.assertTrue(np.all( - categorical_prior.prob(self.xp.array([0, 1, 1, 2, 3])) - == np.array([1 / N, 1 / N, 1 / N, 1 / N, 0]) + np.asarray(probs) == np.array([1 / N, 1 / N, 1 / N, 1 / N, 0]) )) def test_single_lnprobability(self): N = 3 categorical_prior = bilby.core.prior.Categorical(N) - self.assertEqual(categorical_prior.ln_prob(self.xp.array(0)), -np.log(N)) - self.assertEqual(categorical_prior.ln_prob(self.xp.array(1)), -np.log(N)) - self.assertEqual(categorical_prior.ln_prob(self.xp.array(2)), -np.log(N)) - self.assertEqual(categorical_prior.ln_prob(self.xp.array(0.5)), -np.inf) + self.assertEqual(categorical_prior.ln_prob(self.xp.asarray(0)), -np.log(N)) + self.assertEqual(categorical_prior.ln_prob(self.xp.asarray(1)), -np.log(N)) + self.assertEqual(categorical_prior.ln_prob(self.xp.asarray(2)), -np.log(N)) + self.assertEqual(categorical_prior.ln_prob(self.xp.asarray(0.5)), -np.inf) self.assertEqual( - categorical_prior.ln_prob(self.xp.array(0.5)).__array_namespace__(), + aac.get_namespace(categorical_prior.ln_prob(self.xp.asarray(0.5))), self.xp, ) def test_array_lnprobability(self): N = 3 categorical_prior = bilby.core.prior.Categorical(N) + ln_prob = categorical_prior.ln_prob(self.xp.asarray([0, 1, 1, 2, 3])) + self.assertEqual(aac.get_namespace(ln_prob), self.xp) self.assertTrue(np.all( - categorical_prior.ln_prob(self.xp.array([0, 1, 1, 2, 3])) - == np.array([-np.log(N), -np.log(N), -np.log(N), -np.log(N), -np.inf]) + np.asarray(ln_prob) == np.array([-np.log(N)] * 4 + [-np.inf]) )) @@ -187,48 +192,50 @@ def test_array_sample(self): categorical_prior = bilby.core.prior.WeightedCategorical(ncat, weights=weights) N = 100000 s = categorical_prior.sample(N, xp=self.xp) + self.assertEqual(aac.get_namespace(s), self.xp) + s = np.asarray(s) cases = 0 - for i in self.xp.array(categorical_prior.values): + for i in categorical_prior.values: case = np.sum(s == i) cases += case self.assertAlmostEqual(case / N, categorical_prior.prob(i), places=int(np.log10(np.sqrt(N)))) self.assertAlmostEqual(case / N, weights[i] / np.sum(weights), places=int(np.log10(np.sqrt(N)))) self.assertEqual(cases, N) - self.assertEqual(s.__array_namespace__(), self.xp) + def test_single_probability(self): N = 3 weights = np.arange(1, N + 1) categorical_prior = bilby.core.prior.WeightedCategorical(N, weights=weights) - for i in self.xp.array(categorical_prior.values): + for i in self.xp.asarray(categorical_prior.values): self.assertEqual(categorical_prior.prob(i), weights[i] / np.sum(weights)) - prob = categorical_prior.prob(self.xp.array(0.5)) + prob = categorical_prior.prob(self.xp.asarray(0.5)) self.assertEqual(prob, 0) - self.assertEqual(prob.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(prob), self.xp) def test_array_probability(self): N = 3 - test_cases = self.xp.array([0, 1, 1, 2, 3]) + test_cases = self.xp.asarray([0, 1, 1, 2, 3]) weights = np.arange(1, N + 1) categorical_prior = bilby.core.prior.WeightedCategorical(N, weights=weights) probs = np.arange(1, N + 2) / np.sum(weights) probs[-1] = 0 new = categorical_prior.prob(test_cases) - self.assertTrue(np.all(new == probs[test_cases])) - self.assertEqual(new.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(new), self.xp) + self.assertTrue(np.all(np.asarray(new) == probs[test_cases])) def test_single_lnprobability(self): N = 3 weights = np.arange(1, N + 1) categorical_prior = bilby.core.prior.WeightedCategorical(N, weights=weights) - for i in self.xp.array(categorical_prior.values): + for i in self.xp.asarray(categorical_prior.values): self.assertEqual( - categorical_prior.ln_prob(self.xp.array(i)), + categorical_prior.ln_prob(self.xp.asarray(i)), np.log(weights[i] / np.sum(weights)), ) - prob = categorical_prior.prob(self.xp.array(0.5)) + prob = categorical_prior.prob(self.xp.asarray(0.5)) self.assertEqual(prob, 0) - self.assertEqual(prob.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(prob), self.xp) def test_array_lnprobability(self): N = 3 @@ -239,9 +246,9 @@ def test_array_lnprobability(self): ln_probs = np.log(np.arange(1, N + 2) / np.sum(weights)) ln_probs[-1] = -np.inf - new = categorical_prior.ln_prob(self.xp.array(test_cases)) - self.assertTrue(np.all(new == ln_probs[test_cases])) - self.assertEqual(new.__array_namespace__(), self.xp) + new = categorical_prior.ln_prob(self.xp.asarray(test_cases)) + self.assertEqual(aac.get_namespace(new), self.xp) + self.assertTrue(np.all(np.asarray(new) == ln_probs[test_cases])) def test_cdf(self): """ @@ -255,7 +262,7 @@ def test_cdf(self): categorical_prior = bilby.core.prior.WeightedCategorical(N, weights=weights) sample = categorical_prior.sample(size=10) original = self.xp.asarray(sample) - new = self.xp.array(categorical_prior.rescale( + new = self.xp.asarray(categorical_prior.rescale( categorical_prior.cdf(sample) )) np.testing.assert_array_equal(original, new) diff --git a/test/core/prior/base_test.py b/test/core/prior/base_test.py index 21e857fa2..84999d42c 100644 --- a/test/core/prior/base_test.py +++ b/test/core/prior/base_test.py @@ -1,6 +1,7 @@ import unittest from unittest.mock import Mock +import array_api_compat as aac import numpy as np import pytest @@ -188,7 +189,7 @@ def rescale(self, val): prior = CustomPriorWithoutXp(name="custom_prior") import jax.numpy as jnp rescaled = prior.rescale(jnp.array([0.1, 0.2, 3])) - self.assertEqual(rescaled.__array_namespace__(), jnp) + self.assertEqual(aac.get_namespace(rescaled), jnp) if __name__ == "__main__": diff --git a/test/core/prior/conditional_test.py b/test/core/prior/conditional_test.py index cafa0f73e..5850f5ecf 100644 --- a/test/core/prior/conditional_test.py +++ b/test/core/prior/conditional_test.py @@ -3,6 +3,7 @@ import unittest from unittest import mock +import array_api_compat as aac import numpy as np import pandas as pd import pickle @@ -212,10 +213,10 @@ def condition_func_3(reference_parameters, var_1, var_2): bilby.core.prior.ConditionalPriorDict() ) self.test_sample = dict( - var_0=self.xp.array(0.7), - var_1=self.xp.array(0.6), - var_2=self.xp.array(0.5), - var_3=self.xp.array(0.4), + var_0=self.xp.asarray(0.7), + var_1=self.xp.asarray(0.6), + var_2=self.xp.asarray(0.5), + var_3=self.xp.asarray(0.4), ) self.test_value = 1 / np.prod([self.test_sample[f"var_{ii}"] for ii in range(3)]) for key, value in dict( @@ -270,7 +271,7 @@ def test_conditional_keys_setting_items(self): def test_prob(self): prob = self.conditional_priors.prob(sample=self.test_sample) self.assertEqual(self.test_value, prob) - self.assertEqual(prob.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(prob), self.xp) def test_prob_illegal_conditions(self): del self.conditional_priors["var_0"] @@ -366,7 +367,7 @@ def test_rescale_with_joint_prior(self): res = priordict.rescale(keys=keys, theta=ref_variables) self.assertEqual(np.shape(res), (6,)) - self.assertEqual(res.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(res), self.xp) # check conditional values are still as expected expected = [self.test_sample["var_0"]] @@ -470,7 +471,7 @@ def tearDown(self): def test_samples_correct_type(self): samples = self.priors.sample(10, xp=self.xp) - self.assertEqual(samples["dirichlet_1"].__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(samples["dirichlet_1"]), self.xp) def test_samples_sum_to_less_than_one(self): """ diff --git a/test/core/prior/dict_test.py b/test/core/prior/dict_test.py index 3a9a2c52d..425e1ff49 100644 --- a/test/core/prior/dict_test.py +++ b/test/core/prior/dict_test.py @@ -2,6 +2,7 @@ import unittest from unittest.mock import Mock, patch +import array_api_compat as aac import numpy as np import pytest @@ -277,7 +278,7 @@ def test_sample_subset_correct_size(self): self.assertEqual(len(self.prior_set_from_dict), len(samples)) for key in samples: self.assertEqual(size, len(samples[key])) - self.assertEqual(samples[key].__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(samples[key]), self.xp) def test_sample_subset_correct_size_when_non_priors_in_dict(self): self.prior_set_from_dict["asdf"] = "not_a_prior" @@ -287,16 +288,16 @@ def test_sample_subset_correct_size_when_non_priors_in_dict(self): ) self.assertEqual(len(self.prior_set_from_dict) - 1, len(samples)) for key in samples: - self.assertIsNotNone(samples[key].__array_namespace__(), self.xp) + self.assertIsNotNone(aac.get_namespace(samples[key]), self.xp) def test_sample_subset_with_actual_subset(self): size = 3 samples = self.prior_set_from_dict.sample_subset( keys=["length"], size=size, xp=self.xp ) - expected = dict(length=self.xp.array([42.0, 42.0, 42.0])) + expected = dict(length=self.xp.asarray([42.0, 42.0, 42.0])) self.assertTrue(np.array_equal(expected["length"], samples["length"])) - self.assertEqual(samples["length"].__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(samples["length"]), self.xp) def test_sample_subset_constrained_as_array(self): size = 3 @@ -304,7 +305,7 @@ def test_sample_subset_constrained_as_array(self): out = self.prior_set_from_dict.sample_subset_constrained_as_array( keys, size, xp=self.xp ) - self.assertEqual(out.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(out), self.xp) self.assertTrue(out.shape == (len(keys), size)) def test_sample_subset_constrained(self): @@ -356,8 +357,8 @@ def test_sample(self): self.assertEqual(set(samples1.keys()), set(samples2.keys())) for key in samples1: self.assertTrue(np.array_equal(samples1[key], samples2[key])) - self.assertEqual(samples1[key].__array_namespace__(), self.xp) - self.assertEqual(samples2[key].__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(samples1[key]), self.xp) + self.assertEqual(aac.get_namespace(samples2[key]), self.xp) def test_prob(self): samples = self.prior_set_from_dict.sample_subset(keys=["mass", "speed"], xp=self.xp) @@ -365,7 +366,7 @@ def test_prob(self): samples["speed"] ) self.assertEqual(expected, self.prior_set_from_dict.prob(samples)) - self.assertEqual(expected.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(expected), self.xp) def test_ln_prob(self): samples = self.prior_set_from_dict.sample_subset(keys=["mass", "speed"], xp=self.xp) @@ -373,7 +374,7 @@ def test_ln_prob(self): samples["mass"] ) + self.second_prior.ln_prob(samples["speed"]) self.assertEqual(expected, self.prior_set_from_dict.ln_prob(samples)) - self.assertEqual(expected.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(expected), self.xp) def test_rescale(self): theta = [0.5, 0.5, 0.5] @@ -398,13 +399,13 @@ def test_cdf(self): Note that the format of inputs/outputs is different between the two methods. """ sample = self.prior_set_from_dict.sample(xp=self.xp) - original = self.xp.array(list(sample.values())) - new = self.xp.array(self.prior_set_from_dict.rescale( + original = self.xp.asarray(list(sample.values())) + new = self.xp.asarray(self.prior_set_from_dict.rescale( sample.keys(), self.prior_set_from_dict.cdf(sample=sample).values() )) self.assertLess(max(abs(original - new)), 1e-10) - self.assertEqual(new.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(new), self.xp) def test_redundancy(self): for key in self.prior_set_from_dict.keys(): diff --git a/test/core/prior/prior_test.py b/test/core/prior/prior_test.py index c3fa1e865..c24ae1118 100644 --- a/test/core/prior/prior_test.py +++ b/test/core/prior/prior_test.py @@ -1,3 +1,4 @@ +import array_api_compat as aac import bilby import unittest import numpy as np @@ -268,27 +269,27 @@ def test_minimum_rescaling(self): # and so the rescale function doesn't quite return the lower bound continue elif bilby.core.prior.JointPrior in prior.__class__.__mro__: - minimum_sample = prior.rescale(self.xp.array(0)) + minimum_sample = prior.rescale(self.xp.asarray(0)) if prior.dist.filled_rescale(): self.assertAlmostEqual(minimum_sample[0], prior.minimum) self.assertAlmostEqual(minimum_sample[1], prior.minimum) else: - minimum_sample = prior.rescale(self.xp.array(0)) + minimum_sample = prior.rescale(self.xp.asarray(0)) self.assertAlmostEqual(minimum_sample, prior.minimum) def test_maximum_rescaling(self): """Test the the rescaling works as expected.""" for prior in self.priors: if bilby.core.prior.JointPrior in prior.__class__.__mro__: - maximum_sample = prior.rescale(self.xp.array(0)) + maximum_sample = prior.rescale(self.xp.asarray(0)) if prior.dist.filled_rescale(): self.assertAlmostEqual(maximum_sample[0], prior.maximum) self.assertAlmostEqual(maximum_sample[1], prior.maximum) elif isinstance(prior, bilby.gw.prior.AlignedSpin): - maximum_sample = prior.rescale(self.xp.array(1)) + maximum_sample = prior.rescale(self.xp.asarray(1)) self.assertGreater(maximum_sample, 0.997) else: - maximum_sample = prior.rescale(self.xp.array(1)) + maximum_sample = prior.rescale(self.xp.asarray(1)) self.assertAlmostEqual(maximum_sample, prior.maximum) def test_many_sample_rescaling(self): @@ -297,14 +298,14 @@ def test_many_sample_rescaling(self): if isinstance(prior, bilby.core.prior.analytical.SymmetricLogUniform): # SymmetricLogUniform has support down to -maximum continue - many_samples = prior.rescale(self.xp.array(np.random.uniform(0, 1, 1000))) + many_samples = prior.rescale(self.xp.asarray(np.random.uniform(0, 1, 1000))) if bilby.core.prior.JointPrior in prior.__class__.__mro__: if not prior.dist.filled_rescale(): continue self.assertTrue( all((many_samples >= prior.minimum) & (many_samples <= prior.maximum)) ) - self.assertEqual(many_samples.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(many_samples), self.xp) def test_least_recently_sampled(self): for prior in self.priors: @@ -312,7 +313,7 @@ def test_least_recently_sampled(self): self.assertEqual( least_recently_sampled_expected, prior.least_recently_sampled ) - self.assertEqual(least_recently_sampled_expected.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(least_recently_sampled_expected), self.xp) def test_sampling_single(self): """Test that sampling from the prior always returns values within its domain.""" @@ -324,7 +325,7 @@ def test_sampling_single(self): self.assertTrue( (single_sample >= prior.minimum) & (single_sample <= prior.maximum) ) - self.assertEqual(single_sample.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(single_sample), self.xp) def test_sampling_many(self): """Test that sampling from the prior always returns values within its domain.""" @@ -337,7 +338,7 @@ def test_sampling_many(self): (all(many_samples >= prior.minimum)) & (all(many_samples <= prior.maximum)) ) - self.assertEqual(many_samples.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(many_samples), self.xp) def test_probability_above_domain(self): """Test that the prior probability is non-negative in domain of validity and zero outside.""" @@ -372,7 +373,7 @@ def test_least_recently_sampled_2(self): for prior in self.priors: lrs = prior.sample(xp=self.xp) self.assertEqual(lrs, prior.least_recently_sampled) - self.assertEqual(lrs.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(lrs), self.xp) def test_prob_and_ln_prob(self): for prior in self.priors: @@ -386,8 +387,8 @@ def test_prob_and_ln_prob(self): self.assertAlmostEqual( self.xp.log(prob), lnprob, 6 ) - self.assertEqual(lnprob.__array_namespace__(), self.xp) - self.assertEqual(prob.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(lnprob), self.xp) + self.assertEqual(aac.get_namespace(prob), self.xp) def test_many_prob_and_many_ln_prob(self): for prior in self.priors: @@ -398,8 +399,8 @@ def test_many_prob_and_many_ln_prob(self): for sample, logp, p in zip(samples, ln_probs, probs): self.assertAlmostEqual(prior.ln_prob(sample), logp) self.assertAlmostEqual(prior.prob(sample), p) - self.assertEqual(ln_probs.__array_namespace__(), self.xp) - self.assertEqual(probs.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(ln_probs), self.xp) + self.assertEqual(aac.get_namespace(probs), self.xp) def test_cdf_is_inverse_of_rescaling(self): domain = self.xp.linspace(0, 1, 100) @@ -421,11 +422,11 @@ def test_cdf_is_inverse_of_rescaling(self): self.assertTrue(np.array_equal(rescaled, rescaled_2)) max_difference = max(np.abs(cdf_vals - cdf_vals_2)) for arr in [rescaled, rescaled_2, cdf_vals, cdf_vals_2]: - self.assertEqual(arr.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(arr), self.xp) else: rescaled = prior.rescale(domain) max_difference = max(np.abs(domain - prior.cdf(rescaled))) - self.assertEqual(rescaled.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(rescaled), self.xp) self.assertLess(max_difference, threshold) def test_cdf_one_above_domain(self): @@ -669,13 +670,13 @@ def test_normalized(self): domain = np.linspace(prior.minimum, prior.maximum, 10000) elif isinstance(prior, bilby.core.prior.WeightedDiscreteValues): domain = prior.values - self.assertTrue(np.sum(prior.prob(self.xp.array(domain))) == 1) + self.assertTrue(np.sum(prior.prob(self.xp.asarray(domain))) == 1) continue else: domain = np.linspace(prior.minimum, prior.maximum, 1000) - probs = prior.prob(self.xp.array(domain)) + probs = prior.prob(self.xp.asarray(domain)) self.assertAlmostEqual(trapezoid(np.array(probs), domain), 1, 3) - self.assertEqual(probs.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(probs), self.xp) def test_accuracy(self): """Test that each of the priors' functions is calculated accurately, as compared to scipy's calculations""" @@ -771,14 +772,14 @@ def test_accuracy(self): ) if isinstance(prior, (testTuple)): print(prior) - np.testing.assert_almost_equal(prior.prob(self.xp.array(domain)), scipy_prob) - np.testing.assert_almost_equal(prior.ln_prob(self.xp.array(domain)), scipy_lnprob) - np.testing.assert_almost_equal(prior.cdf(self.xp.array(domain)), scipy_cdf) + np.testing.assert_almost_equal(prior.prob(self.xp.asarray(domain)), scipy_prob) + np.testing.assert_almost_equal(prior.ln_prob(self.xp.asarray(domain)), scipy_lnprob) + np.testing.assert_almost_equal(prior.cdf(self.xp.asarray(domain)), scipy_cdf) if isinstance(prior, bilby.core.prior.StudentT) and "jax" in str(self.xp): # JAX implementation of StudentT prior rescale is not accurate enough continue np.testing.assert_almost_equal( - prior.rescale(self.xp.array(rescale_domain)), scipy_rescale + prior.rescale(self.xp.asarray(rescale_domain)), scipy_rescale ) def test_unit_setting(self): diff --git a/test/core/prior/slabspike_test.py b/test/core/prior/slabspike_test.py index cd066ef05..c8a07aae8 100644 --- a/test/core/prior/slabspike_test.py +++ b/test/core/prior/slabspike_test.py @@ -70,7 +70,7 @@ class TestSlabSpikeClasses(unittest.TestCase): def setUp(self): self.minimum = 0.4 self.maximum = 2.4 - self.spike_loc = self.xp.array(1.5) + self.spike_loc = self.xp.asarray(1.5) self.spike_height = 0.3 self.slabs = [ @@ -114,7 +114,7 @@ def test_prob_on_slab(self): expected = slab.prob(test_nodes) * slab_spike.slab_fraction actual = slab_spike.prob(test_nodes) self.assertTrue(np.allclose(expected, actual, rtol=1e-5)) - self.assertEqual(expected.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(actual), self.xp) def test_prob_on_spike(self): for slab_spike in self.slab_spikes: @@ -125,13 +125,13 @@ def test_ln_prob_on_slab(self): expected = slab.ln_prob(test_nodes) + np.log(slab_spike.slab_fraction) actual = slab_spike.ln_prob(test_nodes) self.assertTrue(np.array_equal(expected, actual)) - self.assertEqual(expected.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(actual), self.xp) def test_ln_prob_on_spike(self): for slab_spike in self.slab_spikes: expected = slab_spike.ln_prob(self.spike_loc) self.assertEqual(np.inf, expected) - self.assertEqual(expected.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(actual), self.xp) def test_inverse_cdf_below_spike_with_spike_at_minimum(self): for slab in self.slabs: @@ -154,14 +154,14 @@ def test_cdf_below_spike(self): expected = slab.cdf(test_nodes) * slab_spike.slab_fraction actual = slab_spike.cdf(test_nodes) self.assertTrue(np.allclose(expected, actual, rtol=1e-5)) - self.assertEqual(actual.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(actual), self.xp) def test_cdf_at_spike(self): for slab, slab_spike in zip(self.slabs, self.slab_spikes): expected = slab.cdf(self.spike_loc) * slab_spike.slab_fraction actual = slab_spike.cdf(self.xp.asarray(self.spike_loc)) self.assertTrue(np.allclose(expected, actual, rtol=1e-5)) - self.assertEqual(actual.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(actual), self.xp) def test_cdf_above_spike(self): for slab, slab_spike, test_nodes in zip(self.slabs, self.slab_spikes, self.test_nodes): @@ -169,7 +169,7 @@ def test_cdf_above_spike(self): expected = slab.cdf(test_nodes) * slab_spike.slab_fraction + self.spike_height actual = slab_spike.cdf(test_nodes) np.testing.assert_allclose(expected, actual, rtol=1e-12) - self.assertEqual(actual.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(actual), self.xp) def test_cdf_at_minimum(self): for slab_spike in self.slab_spikes: @@ -190,7 +190,7 @@ def test_rescale_no_spike(self): expected = slab.rescale(vals) actual = slab_spike.rescale(vals) self.assertTrue(np.allclose(expected, actual, rtol=1e-5)) - self.assertEqual(expected.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(actual), self.xp) def test_rescale_below_spike(self): for slab, slab_spike in zip(self.slabs, self.slab_spikes): @@ -198,7 +198,7 @@ def test_rescale_below_spike(self): expected = slab.rescale(vals / slab_spike.slab_fraction) actual = slab_spike.rescale(vals) self.assertTrue(np.allclose(expected, actual, rtol=1e-5)) - self.assertEqual(expected.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(actual), self.xp) def test_rescale_at_spike(self): for slab, slab_spike in zip(self.slabs, self.slab_spikes): @@ -209,7 +209,7 @@ def test_rescale_at_spike(self): expected = self.xp.ones(len(vals)) * slab.rescale(vals[0] / slab_spike.slab_fraction) actual = slab_spike.rescale(vals) self.assertTrue(np.allclose(expected, actual, rtol=1e-5)) - self.assertEqual(expected.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(actual), self.xp) def test_rescale_above_spike(self): for slab, slab_spike in zip(self.slabs, self.slab_spikes): @@ -218,4 +218,4 @@ def test_rescale_above_spike(self): (vals - self.spike_height) / slab_spike.slab_fraction) actual = slab_spike.rescale(vals) self.assertTrue(np.allclose(expected, actual, rtol=1e-5)) - self.assertEqual(expected.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(actual), self.xp) diff --git a/test/core/result_test.py b/test/core/result_test.py index 702ac23cc..96095c0bb 100644 --- a/test/core/result_test.py +++ b/test/core/result_test.py @@ -30,7 +30,7 @@ def test_list_encoding(self): self.assertTrue(np.all(data["x"] == decoded["x"])) def test_array_encoding(self): - data = dict(x=self.xp.array([1, 2, 3.4])) + data = dict(x=self.xp.asarray([1, 2, 3.4])) encoded = json.dumps(data, cls=self.encoder) decoded = json.loads(encoded, object_hook=self.decoder) self.assertEqual(data.keys(), decoded.keys()) diff --git a/test/core/series_test.py b/test/core/series_test.py index 7b85c2bc5..aec2ff42f 100644 --- a/test/core/series_test.py +++ b/test/core/series_test.py @@ -11,9 +11,9 @@ @pytest.mark.usefixtures("xp_class") class TestCoupledTimeAndFrequencySeries(unittest.TestCase): def setUp(self): - self.duration = self.xp.array(2.0) - self.sampling_frequency = self.xp.array(4096.0) - self.start_time = self.xp.array(-1.0) + self.duration = self.xp.asarray(2.0) + self.sampling_frequency = self.xp.asarray(4096.0) + self.start_time = self.xp.asarray(-1.0) self.series = CoupledTimeAndFrequencySeries( duration=self.duration, sampling_frequency=self.sampling_frequency, @@ -67,8 +67,8 @@ def test_time_array_from_init(self): self.assertTrue(np.array_equal(expected, self.series.time_array)) def test_frequency_array_setter(self): - new_sampling_frequency = self.xp.array(100.0) - new_duration = self.xp.array(3.0) + new_sampling_frequency = self.xp.asarray(100.0) + new_duration = self.xp.asarray(3.0) new_frequency_array = create_frequency_series( sampling_frequency=new_sampling_frequency, duration=new_duration ) @@ -83,9 +83,9 @@ def test_frequency_array_setter(self): self.assertAlmostEqual(self.start_time, self.series.start_time) def test_time_array_setter(self): - new_sampling_frequency = self.xp.array(100.0) - new_duration = self.xp.array(3.0) - new_start_time = self.xp.array(4.0) + new_sampling_frequency = self.xp.asarray(100.0) + new_duration = self.xp.asarray(3.0) + new_start_time = self.xp.asarray(4.0) new_time_array = create_time_series( sampling_frequency=new_sampling_frequency, duration=new_duration, @@ -101,24 +101,24 @@ def test_time_array_setter(self): def test_time_array_without_sampling_frequency(self): self.series.sampling_frequency = None - self.series.duration = self.xp.array(4) + self.series.duration = self.xp.asarray(4) with self.assertRaises(ValueError): _ = self.series.time_array def test_time_array_without_duration(self): - self.series.sampling_frequency = self.xp.array(4096) + self.series.sampling_frequency = self.xp.asarray(4096) self.series.duration = None with self.assertRaises(ValueError): _ = self.series.time_array def test_frequency_array_without_sampling_frequency(self): self.series.sampling_frequency = None - self.series.duration = self.xp.array(4) + self.series.duration = self.xp.asarray(4) with self.assertRaises(ValueError): _ = self.series.frequency_array def test_frequency_array_without_duration(self): - self.series.sampling_frequency = self.xp.array(4096) + self.series.sampling_frequency = self.xp.asarray(4096) self.series.duration = None with self.assertRaises(ValueError): _ = self.series.frequency_array diff --git a/test/core/utils_test.py b/test/core/utils_test.py index fdd4afeef..bd1869a6a 100644 --- a/test/core/utils_test.py +++ b/test/core/utils_test.py @@ -1,6 +1,7 @@ import unittest import os +import array_api_compat as aac import array_api_extra as xpx import dill import numpy as np @@ -54,16 +55,16 @@ def test_gravitational_constant(self): @pytest.mark.usefixtures("xp_class") class TestFFT(unittest.TestCase): def setUp(self): - self.sampling_frequency = self.xp.array(10) + self.sampling_frequency = self.xp.asarray(10) def tearDown(self): del self.sampling_frequency def test_nfft_sine_function(self): xp = self.xp - injected_frequency = xp.array(2.7324) - duration = xp.array(100) - times = utils.create_time_series(xp.array(self.sampling_frequency), duration) + injected_frequency = xp.asarray(2.7324) + duration = xp.asarray(100) + times = utils.create_time_series(xp.asarray(self.sampling_frequency), duration) time_domain_strain = xp.sin(2 * np.pi * times * injected_frequency + 0.4) @@ -75,7 +76,7 @@ def test_nfft_sine_function(self): def test_nfft_infft(self): xp = self.xp - time_domain_strain = xp.array(np.random.normal(0, 1, 10)) + time_domain_strain = xp.asarray(np.random.normal(0, 1, 10)) frequency_domain_strain, _ = bilby.core.utils.nfft( time_domain_strain, self.sampling_frequency ) @@ -128,9 +129,9 @@ def test_self_handling_method_as_function(self): @pytest.mark.usefixtures("xp_class") class TestTimeAndFrequencyArrays(unittest.TestCase): def setUp(self): - self.start_time = self.xp.array(1.3) - self.sampling_frequency = self.xp.array(5) - self.duration = self.xp.array(1.6) + self.start_time = self.xp.asarray(1.3) + self.sampling_frequency = self.xp.asarray(5) + self.duration = self.xp.asarray(1.6) self.frequency_array = utils.create_frequency_series( sampling_frequency=self.sampling_frequency, duration=self.duration ) @@ -148,13 +149,13 @@ def tearDown(self): del self.time_array def test_create_time_array(self): - expected_time_array = self.xp.array([1.3, 1.5, 1.7, 1.9, 2.1, 2.3, 2.5, 2.7]) + expected_time_array = self.xp.asarray([1.3, 1.5, 1.7, 1.9, 2.1, 2.3, 2.5, 2.7]) time_array = utils.create_time_series( sampling_frequency=self.sampling_frequency, duration=self.duration, starting_time=self.start_time, ) - self.assertEqual(time_array.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(time_array), self.xp) self.assertTrue(np.allclose(expected_time_array, time_array)) def test_create_frequency_array(self): @@ -243,9 +244,9 @@ def test_consistency_frequency_array_to_frequency_array(self): def test_illegal_sampling_frequency_and_duration(self): with self.assertRaises(utils.IllegalDurationAndSamplingFrequencyException): _ = utils.create_time_series( - sampling_frequency=self.xp.array(7.7), - duration=self.xp.array(1.3), - starting_time=self.xp.array(0), + sampling_frequency=self.xp.asarray(7.7), + duration=self.xp.asarray(1.3), + starting_time=self.xp.asarray(0), ) @@ -253,28 +254,28 @@ def test_illegal_sampling_frequency_and_duration(self): @pytest.mark.usefixtures("xp_class") class TestReflect(unittest.TestCase): def test_in_range(self): - xprime = self.xp.array([0.1, 0.5, 0.9]) - x = self.xp.array([0.1, 0.5, 0.9]) + xprime = self.xp.asarray([0.1, 0.5, 0.9]) + x = self.xp.asarray([0.1, 0.5, 0.9]) self.assertTrue(np.testing.assert_allclose(utils.reflect(xprime), x) is None) def test_in_one_to_two(self): - xprime = self.xp.array([1.1, 1.5, 1.9]) - x = self.xp.array([0.9, 0.5, 0.1]) + xprime = self.xp.asarray([1.1, 1.5, 1.9]) + x = self.xp.asarray([0.9, 0.5, 0.1]) self.assertTrue(np.testing.assert_allclose(utils.reflect(xprime), x) is None) def test_in_two_to_three(self): - xprime = self.xp.array([2.1, 2.5, 2.9]) - x = self.xp.array([0.1, 0.5, 0.9]) + xprime = self.xp.asarray([2.1, 2.5, 2.9]) + x = self.xp.asarray([0.1, 0.5, 0.9]) self.assertTrue(np.testing.assert_allclose(utils.reflect(xprime), x) is None) def test_in_minus_one_to_zero(self): - xprime = self.xp.array([-0.9, -0.5, -0.1]) - x = self.xp.array([0.9, 0.5, 0.1]) + xprime = self.xp.asarray([-0.9, -0.5, -0.1]) + x = self.xp.asarray([0.9, 0.5, 0.1]) self.assertTrue(np.testing.assert_allclose(utils.reflect(xprime), x) is None) def test_in_minus_two_to_minus_one(self): - xprime = self.xp.array([-1.9, -1.5, -1.1]) - x = self.xp.array([0.1, 0.5, 0.9]) + xprime = self.xp.asarray([-1.9, -1.5, -1.1]) + x = self.xp.asarray([0.1, 0.5, 0.9]) self.assertTrue(np.testing.assert_allclose(utils.reflect(xprime), x) is None) @@ -359,23 +360,23 @@ def test_returns_none_for_floats_outside_range(self): self.assertIsNone(self.interpolant(-0.5, 0.5)) def test_returns_float_for_float_and_array(self): - input_array = self.xp.array(np.random.random(10)) - self.assertEqual(self.interpolant(input_array, 0.5).__array_namespace__(), self.xp) - self.assertEqual( - self.interpolant(input_array, input_array).__array_namespace__(), self.xp + input_array = self.xp.asarray(np.random.random(10)) + aac.get_namespace(self.assertEqual(self.interpolant(input_array, 0.5)), self.xp) + aac.get_namespace(self.assertEqual( + self.interpolant(input_array, input_array)), self.xp ) - self.assertEqual(self.interpolant(0.5, input_array).__array_namespace__(), self.xp) + aac.get_namespace(self.assertEqual(self.interpolant(0.5, input_array)), self.xp) def test_raises_for_mismatched_arrays(self): with self.assertRaises(ValueError): self.interpolant( - self.xp.array(np.random.random(10)), - self.xp.array(np.random.random(20)), + 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.array(np.random.random(10)) - y_data = self.xp.array(np.random.random(10)) + 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])) @@ -394,7 +395,7 @@ def setUp(self): self.lnfunc2 = self.xp.log(self.x ** 2) self.func2int = (self.x[-1] ** 3 - self.x[0] ** 3) / 3 - self.irregularx = self.xp.array( + self.irregularx = self.xp.asarray( [ self.x[0], self.x[12], diff --git a/test/gw/conversion_test.py b/test/gw/conversion_test.py index dcef161e9..17243bfe3 100644 --- a/test/gw/conversion_test.py +++ b/test/gw/conversion_test.py @@ -1,5 +1,6 @@ import unittest +import array_api_compat as aac import numpy as np import pandas as pd import pytest @@ -12,17 +13,17 @@ @pytest.mark.usefixtures("xp_class") class TestBasicConversions(unittest.TestCase): def setUp(self): - self.mass_1 = self.xp.array(1.4) - self.mass_2 = self.xp.array(1.3) - self.mass_ratio = self.xp.array(13 / 14) - self.total_mass = self.xp.array(2.7) + self.mass_1 = self.xp.asarray(1.4) + self.mass_2 = self.xp.asarray(1.3) + self.mass_ratio = self.xp.asarray(13 / 14) + self.total_mass = self.xp.asarray(2.7) self.chirp_mass = (self.mass_1 * self.mass_2) ** 0.6 / self.total_mass ** 0.2 self.symmetric_mass_ratio = (self.mass_1 * self.mass_2) / self.total_mass ** 2 - self.cos_angle = self.xp.array(-1.0) + self.cos_angle = self.xp.asarray(-1.0) self.angle = self.xp.pi - self.lambda_1 = self.xp.array(300.0) - self.lambda_2 = self.xp.array(300.0 * (14 / 13) ** 5) - self.lambda_tilde = self.xp.array( + self.lambda_1 = self.xp.asarray(300.0) + self.lambda_2 = self.xp.asarray(300.0 * (14 / 13) ** 5) + self.lambda_tilde = self.xp.asarray( 8 / 13 * ( @@ -41,7 +42,7 @@ def setUp(self): * (self.lambda_1 - self.lambda_2) ) ) - self.delta_lambda_tilde = self.xp.array( + self.delta_lambda_tilde = self.xp.asarray( 1 / 2 * ( @@ -77,36 +78,36 @@ def test_total_mass_and_mass_ratio_to_component_masses(self): self.assertTrue( all([abs(mass_1 - self.mass_1) < 1e-5, abs(mass_2 - self.mass_2) < 1e-5]) ) - self.assertEqual(mass_1.__array_namespace__(), self.xp) - self.assertEqual(mass_2.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(mass_1), self.xp) + self.assertEqual(aac.get_namespace(mass_2), self.xp) def test_chirp_mass_and_primary_mass_to_mass_ratio(self): mass_ratio = conversion.chirp_mass_and_primary_mass_to_mass_ratio( self.chirp_mass, self.mass_1 ) self.assertAlmostEqual(self.mass_ratio, mass_ratio) - self.assertEqual(mass_ratio.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(mass_ratio), self.xp) def test_symmetric_mass_ratio_to_mass_ratio(self): mass_ratio = conversion.symmetric_mass_ratio_to_mass_ratio( self.symmetric_mass_ratio ) self.assertAlmostEqual(self.mass_ratio, mass_ratio) - self.assertEqual(mass_ratio.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(mass_ratio), self.xp) def test_chirp_mass_and_total_mass_to_symmetric_mass_ratio(self): symmetric_mass_ratio = conversion.chirp_mass_and_total_mass_to_symmetric_mass_ratio( self.chirp_mass, self.total_mass ) self.assertAlmostEqual(self.symmetric_mass_ratio, symmetric_mass_ratio) - self.assertEqual(symmetric_mass_ratio.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(symmetric_mass_ratio), self.xp) def test_chirp_mass_and_mass_ratio_to_total_mass(self): total_mass = conversion.chirp_mass_and_mass_ratio_to_total_mass( self.chirp_mass, self.mass_ratio ) self.assertAlmostEqual(self.total_mass, total_mass) - self.assertEqual(total_mass.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(total_mass), self.xp) def test_chirp_mass_and_mass_ratio_to_component_masses(self): mass_1, mass_2 = \ @@ -114,37 +115,37 @@ def test_chirp_mass_and_mass_ratio_to_component_masses(self): self.chirp_mass, self.mass_ratio) self.assertAlmostEqual(self.mass_1, mass_1) self.assertAlmostEqual(self.mass_2, mass_2) - self.assertEqual(mass_1.__array_namespace__(), self.xp) - self.assertEqual(mass_2.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(mass_1), self.xp) + self.assertEqual(aac.get_namespace(mass_2), self.xp) def test_component_masses_to_chirp_mass(self): chirp_mass = conversion.component_masses_to_chirp_mass(self.mass_1, self.mass_2) self.assertAlmostEqual(self.chirp_mass, chirp_mass) - self.assertEqual(chirp_mass.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(chirp_mass), self.xp) def test_component_masses_to_total_mass(self): total_mass = conversion.component_masses_to_total_mass(self.mass_1, self.mass_2) self.assertAlmostEqual(self.total_mass, total_mass) - self.assertEqual(total_mass.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(total_mass), self.xp) def test_component_masses_to_symmetric_mass_ratio(self): symmetric_mass_ratio = conversion.component_masses_to_symmetric_mass_ratio( self.mass_1, self.mass_2 ) self.assertAlmostEqual(self.symmetric_mass_ratio, symmetric_mass_ratio) - self.assertEqual(symmetric_mass_ratio.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(symmetric_mass_ratio), self.xp) def test_component_masses_to_mass_ratio(self): mass_ratio = conversion.component_masses_to_mass_ratio(self.mass_1, self.mass_2) self.assertAlmostEqual(self.mass_ratio, mass_ratio) - self.assertEqual(mass_ratio.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(mass_ratio), self.xp) def test_mass_1_and_chirp_mass_to_mass_ratio(self): mass_ratio = conversion.mass_1_and_chirp_mass_to_mass_ratio( self.mass_1, self.chirp_mass ) self.assertAlmostEqual(self.mass_ratio, mass_ratio) - self.assertEqual(mass_ratio.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(mass_ratio), self.xp) def test_lambda_tilde_to_lambda_1_lambda_2(self): lambda_1, lambda_2 = conversion.lambda_tilde_to_lambda_1_lambda_2( @@ -158,8 +159,8 @@ def test_lambda_tilde_to_lambda_1_lambda_2(self): ] ) ) - self.assertEqual(lambda_1.__array_namespace__(), self.xp) - self.assertEqual(lambda_2.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(lambda_1), self.xp) + self.assertEqual(aac.get_namespace(lambda_2), self.xp) def test_lambda_tilde_delta_lambda_tilde_to_lambda_1_lambda_2(self): ( @@ -176,22 +177,22 @@ def test_lambda_tilde_delta_lambda_tilde_to_lambda_1_lambda_2(self): ] ) ) - self.assertEqual(lambda_1.__array_namespace__(), self.xp) - self.assertEqual(lambda_2.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(lambda_1), self.xp) + self.assertEqual(aac.get_namespace(lambda_2), self.xp) def test_lambda_1_lambda_2_to_lambda_tilde(self): lambda_tilde = conversion.lambda_1_lambda_2_to_lambda_tilde( self.lambda_1, self.lambda_2, self.mass_1, self.mass_2 ) self.assertTrue((self.lambda_tilde - lambda_tilde) < 1e-5) - self.assertEqual(lambda_tilde.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(lambda_tilde), self.xp) def test_lambda_1_lambda_2_to_delta_lambda_tilde(self): delta_lambda_tilde = conversion.lambda_1_lambda_2_to_delta_lambda_tilde( self.lambda_1, self.lambda_2, self.mass_1, self.mass_2 ) self.assertTrue((self.delta_lambda_tilde - delta_lambda_tilde) < 1e-5) - self.assertEqual(delta_lambda_tilde.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(delta_lambda_tilde), self.xp) def test_identity_conversion(self): original_samples = dict( @@ -626,16 +627,16 @@ def test_comoving_luminosity_with_cosmology(self): @pytest.mark.usefixtures("xp_class") class TestGenerateMassParameters(unittest.TestCase): def setUp(self): - self.expected_values = {'mass_1': self.xp.array(2.0), - 'mass_2': self.xp.array(1.0), - 'chirp_mass': self.xp.array(1.2167286837864113), - 'total_mass': self.xp.array(3.0), - 'mass_1_source': self.xp.array(4.0), - 'mass_2_source': self.xp.array(2.0), - 'chirp_mass_source': self.xp.array(2.433457367572823), - 'total_mass_source': self.xp.array(6), - 'symmetric_mass_ratio': self.xp.array(0.2222222222222222), - 'mass_ratio': self.xp.array(0.5)} + self.expected_values = {'mass_1': self.xp.asarray(2.0), + 'mass_2': self.xp.asarray(1.0), + 'chirp_mass': self.xp.asarray(1.2167286837864113), + 'total_mass': self.xp.asarray(3.0), + 'mass_1_source': self.xp.asarray(4.0), + 'mass_2_source': self.xp.asarray(2.0), + 'chirp_mass_source': self.xp.asarray(2.433457367572823), + 'total_mass_source': self.xp.asarray(6), + 'symmetric_mass_ratio': self.xp.asarray(0.2222222222222222), + 'mass_ratio': self.xp.asarray(0.5)} def helper_generation_from_keys(self, keys, expected_values, source=False): # Explicitly test the helper generate_component_masses @@ -684,7 +685,7 @@ def helper_generation_from_keys(self, keys, expected_values, source=False): for key in local_all_mass_parameters.keys(): self.assertAlmostEqual(expected_values[key], local_all_mass_parameters[key]) self.assertEqual( - local_all_mass_parameters[key].__array_namespace__(), + aac.get_namespace(local_all_mass_parameters[key]), self.xp, ) @@ -763,42 +764,42 @@ class TestEquationOfStateConversions(unittest.TestCase): ''' def setUp(self): - self.mass_1_source_spectral = self.xp.array([ + self.mass_1_source_spectral = self.xp.asarray([ 4.922542724434885, 4.350626907771598, 4.206155335439082, 1.7822696459661311, 1.3091740103047926 ]) - self.mass_2_source_spectral = self.xp.array([ + self.mass_2_source_spectral = self.xp.asarray([ 3.459974694590303, 1.2276461777181447, 3.7287707089639976, 0.3724016563531846, 1.055042934805801 ]) - self.spectral_pca_gamma_0 = self.xp.array([ + self.spectral_pca_gamma_0 = self.xp.asarray([ 0.7074873121348357, 0.05855931126849878, 0.7795329261793462, 1.467907561566463, 2.9066488405635624 ]) - self.spectral_pca_gamma_1 = self.xp.array([ + self.spectral_pca_gamma_1 = self.xp.asarray([ -0.29807111670823816, 2.027708558522935, -1.4415775226512115, -0.7104870098896858, -0.4913817181089619 ]) - self.spectral_pca_gamma_2 = self.xp.array([ + self.spectral_pca_gamma_2 = self.xp.asarray([ 0.25625095371021156, -0.19574096643220049, -0.2710238103460012, 0.22815820981582358, -0.1543413205016374 ]) - self.spectral_pca_gamma_3 = self.xp.array([ + self.spectral_pca_gamma_3 = self.xp.asarray([ -0.04030365100175101, 0.05698030777919032, -0.045595911403040264, @@ -910,7 +911,7 @@ def test_spectral_pca_to_spectral(self): self.assertAlmostEqual(spectral_gamma_2, self.spectral_gamma_2[i], places=5) self.assertAlmostEqual(spectral_gamma_3, self.spectral_gamma_3[i], places=5) for val in [spectral_gamma_0, spectral_gamma_1, spectral_gamma_2, spectral_gamma_3]: - self.assertEqual(val.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(val), self.xp) def test_spectral_params_to_lambda_1_lambda_2(self): ''' diff --git a/test/gw/detector/geometry_test.py b/test/gw/detector/geometry_test.py index 4906f00cc..b18eb218d 100644 --- a/test/gw/detector/geometry_test.py +++ b/test/gw/detector/geometry_test.py @@ -1,6 +1,7 @@ import unittest from unittest import mock +import array_api_compat as aac import numpy as np import pytest @@ -44,35 +45,35 @@ def tearDown(self): def test_length_setting(self): self.assertEqual(self.geometry.length, self.length) - self.assertEqual(self.geometry.length.__array_namespace__(), self.xp) + self.assertEqual(self.geometry.aac.get_namespace(length), self.xp) def test_latitude_setting(self): self.assertEqual(self.geometry.latitude, self.latitude) - self.assertEqual(self.geometry.latitude.__array_namespace__(), self.xp) + self.assertEqual(self.geometry.aac.get_namespace(latitude), self.xp) def test_longitude_setting(self): self.assertEqual(self.geometry.longitude, self.longitude) - self.assertEqual(self.geometry.longitude.__array_namespace__(), self.xp) + self.assertEqual(self.geometry.aac.get_namespace(longitude), self.xp) def test_elevation_setting(self): self.assertEqual(self.geometry.elevation, self.elevation) - self.assertEqual(self.geometry.elevation.__array_namespace__(), self.xp) + self.assertEqual(self.geometry.aac.get_namespace(elevation), self.xp) def test_xarm_azi_setting(self): self.assertEqual(self.geometry.xarm_azimuth, self.xarm_azimuth) - self.assertEqual(self.geometry.xarm_azimuth.__array_namespace__(), self.xp) + self.assertEqual(self.geometry.aac.get_namespace(xarm_azimuth), self.xp) def test_yarm_azi_setting(self): self.assertEqual(self.geometry.yarm_azimuth, self.yarm_azimuth) - self.assertEqual(self.geometry.yarm_azimuth.__array_namespace__(), self.xp) + self.assertEqual(self.geometry.aac.get_namespace(yarm_azimuth), self.xp) def test_xarm_tilt_setting(self): self.assertEqual(self.geometry.xarm_tilt, self.xarm_tilt) - self.assertEqual(self.geometry.xarm_tilt.__array_namespace__(), self.xp) + self.assertEqual(self.geometry.aac.get_namespace(xarm_tilt), self.xp) def test_yarm_tilt_setting(self): self.assertEqual(self.geometry.yarm_tilt, self.yarm_tilt) - self.assertEqual(self.geometry.yarm_tilt.__array_namespace__(), self.xp) + self.assertEqual(self.geometry.aac.get_namespace(yarm_tilt), self.xp) def test_vertex_without_update(self): _ = self.geometry.vertex @@ -154,37 +155,37 @@ def test_detector_tensor_with_x_azimuth_update(self): original = self.geometry.detector_tensor self.geometry.xarm_azimuth += 1 self.assertNotEqual(np.max(abs(self.geometry.detector_tensor - original)), 0) - self.assertEqual(self.geometry.detector_tensor.__array_namespace__(), self.xp) + self.assertEqual(self.geometry.aac.get_namespace(detector_tensor), self.xp) def test_detector_tensor_with_y_azimuth_update(self): original = self.geometry.detector_tensor self.geometry.yarm_azimuth += 1 self.assertNotEqual(np.max(abs(self.geometry.detector_tensor - original)), 0) - self.assertEqual(self.geometry.detector_tensor.__array_namespace__(), self.xp) + self.assertEqual(self.geometry.aac.get_namespace(detector_tensor), self.xp) def test_detector_tensor_with_x_tilt_update(self): original = self.geometry.detector_tensor self.geometry.xarm_tilt += 1 self.assertNotEqual(np.max(abs(self.geometry.detector_tensor - original)), 0) - self.assertEqual(self.geometry.detector_tensor.__array_namespace__(), self.xp) + self.assertEqual(self.geometry.aac.get_namespace(detector_tensor), self.xp) def test_detector_tensor_with_y_tilt_update(self): original = self.geometry.detector_tensor self.geometry.yarm_tilt += 1 self.assertNotEqual(np.max(abs(self.geometry.detector_tensor - original)), 0) - self.assertEqual(self.geometry.detector_tensor.__array_namespace__(), self.xp) + self.assertEqual(self.geometry.aac.get_namespace(detector_tensor), self.xp) def test_detector_tensor_with_longitude_update(self): original = self.geometry.detector_tensor self.geometry.longitude += 1 self.assertNotEqual(np.max(abs(self.geometry.detector_tensor - original)), 0) - self.assertEqual(self.geometry.detector_tensor.__array_namespace__(), self.xp) + self.assertEqual(self.geometry.aac.get_namespace(detector_tensor), self.xp) def test_detector_tensor_with_latitude_update(self): original = self.geometry.detector_tensor self.geometry.latitude += 1 self.assertNotEqual(np.max(abs(self.geometry.detector_tensor - original)), 0) - self.assertEqual(self.geometry.detector_tensor.__array_namespace__(), self.xp) + self.assertEqual(self.geometry.aac.get_namespace(detector_tensor), self.xp) def test_unit_vector_along_arm_default(self): with self.assertRaises(ValueError): @@ -198,7 +199,7 @@ def test_unit_vector_along_arm_x(self): self.geometry.set_array_backend(self.xp) arm = self.geometry.unit_vector_along_arm("x") self.assertTrue(np.allclose(arm, np.array([0, 1, 0]))) - self.assertEqual(arm.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(arm), self.xp) def test_unit_vector_along_arm_y(self): self.geometry.longitude = 0 @@ -208,7 +209,7 @@ def test_unit_vector_along_arm_y(self): self.geometry.set_array_backend(self.xp) arm = self.geometry.unit_vector_along_arm("y") self.assertTrue(np.allclose(arm, np.array([0, 0, 1]))) - self.assertEqual(arm.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(arm), self.xp) def test_repr(self): expected = ( diff --git a/test/gw/likelihood_test.py b/test/gw/likelihood_test.py index 7cff69397..3b5839d50 100644 --- a/test/gw/likelihood_test.py +++ b/test/gw/likelihood_test.py @@ -7,6 +7,7 @@ import pytest from copy import deepcopy +import array_api_compat as aac import h5py import numpy as np import bilby @@ -27,7 +28,7 @@ def __getattr__(self, name): def convert_nested_dict(self, data): if is_array_api_obj(data): - return self.xp.array(data) + return self.xp.asarray(data) elif isinstance(data, dict): return {key: self.convert_nested_dict(value) for key, value in data.items()} else: @@ -70,8 +71,8 @@ def setUp(self): ) self.interferometers.set_array_backend(self.xp) base_wfg = bilby.gw.waveform_generator.GWSignalWaveformGenerator( - duration=self.xp.array(4.0), - sampling_frequency=self.xp.array(2048.0), + duration=self.xp.asarray(4.0), + sampling_frequency=self.xp.asarray(2048.0), waveform_arguments=dict(waveform_approximant="IMRPhenomPv2"), ) self.waveform_generator = BackendWaveformGenerator(base_wfg, self.xp) @@ -93,13 +94,13 @@ def test_noise_log_likelihood(self): self.assertAlmostEqual( -4014.1787704539474, nll, 3 ) - self.assertEqual(nll.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(nll), self.xp) def test_log_likelihood(self): """Test log likelihood matches precomputed value""" logl = self.likelihood.log_likelihood(self.parameters) self.assertAlmostEqual(logl, -4032.4397343470005, 3) - self.assertEqual(logl.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(logl), self.xp) def test_log_likelihood_ratio(self): """Test log likelihood ratio returns the correct value""" @@ -109,7 +110,7 @@ def test_log_likelihood_ratio(self): llr, 3, ) - self.assertEqual(llr.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(llr), self.xp) def test_likelihood_zero_when_waveform_is_none(self): """Test log likelihood returns np.nan_to_num(-np.inf) when the @@ -129,8 +130,8 @@ def test_repr(self): class TestGWTransient(unittest.TestCase): def setUp(self): bilby.core.utils.random.seed(500) - self.duration = self.xp.array(4.0) - self.sampling_frequency = self.xp.array(2048.0) + self.duration = self.xp.asarray(4.0) + self.sampling_frequency = self.xp.asarray(2048.0) self.parameters = dict( mass_1=31.0, mass_2=29.0, @@ -185,13 +186,13 @@ def test_noise_log_likelihood(self): self.assertAlmostEqual( -4014.1787704539474, nll, 3 ) - self.assertEqual(nll.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(nll), self.xp) def test_log_likelihood(self): """Test log likelihood matches precomputed value""" logl = self.likelihood.log_likelihood(self.parameters) self.assertAlmostEqual(logl, -4032.4397343470005, 3) - self.assertEqual(logl.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(logl), self.xp) def test_log_likelihood_ratio(self): """Test log likelihood ratio returns the correct value""" @@ -201,7 +202,7 @@ def test_log_likelihood_ratio(self): llr, 3, ) - self.assertEqual(llr.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(llr), self.xp) def test_likelihood_zero_when_waveform_is_none(self): """Test log likelihood returns np.nan_to_num(-np.inf) when the @@ -281,16 +282,16 @@ def test_reference_frame_agrees_with_default(self): ) parameters = self.parameters.copy() del parameters["ra"], parameters["dec"] - parameters["zenith"] = self.xp.array(1.0) - parameters["azimuth"] = self.xp.array(1.0) + parameters["zenith"] = self.xp.asarray(1.0) + parameters["azimuth"] = self.xp.asarray(1.0) parameters["ra"], parameters["dec"] = bilby.gw.utils.zenith_azimuth_to_ra_dec( zenith=parameters["zenith"], azimuth=parameters["azimuth"], geocent_time=parameters["geocent_time"], ifos=new_likelihood.reference_frame, ) - self.assertEqual(parameters["ra"].__array_namespace__(), self.xp) - self.assertEqual(parameters["dec"].__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(parameters["ra"]), self.xp) + self.assertEqual(aac.get_namespace(parameters["dec"]), self.xp) self.assertEqual( new_likelihood.log_likelihood_ratio(parameters), self.likelihood.log_likelihood_ratio(parameters) @@ -322,8 +323,8 @@ def test_time_reference_agrees_with_default(self): @pytest.mark.usefixtures("xp_class") class TestROQLikelihood(unittest.TestCase): def setUp(self): - self.duration = self.xp.array(4.0) - self.sampling_frequency = self.xp.array(2048.0) + self.duration = self.xp.asarray(4.0) + self.sampling_frequency = self.xp.asarray(2048.0) self.test_parameters = dict( mass_1=36.0, @@ -469,7 +470,7 @@ def test_matches_non_roq(self): abs(non_roq_llr - roq_llr) / non_roq_llr, 1e-3, ) - self.assertEqual(roq_llr.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(roq_llr), self.xp) def test_time_prior_out_of_bounds_returns_zero(self): parameters = deepcopy(self.test_parameters) @@ -490,7 +491,7 @@ def test_create_roq_weights_with_params(self): roq_llr, self.roq.log_likelihood_ratio(self.test_parameters) ) - self.assertEqual(roq_llr.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(roq_llr), self.xp) def test_create_roq_weights_frequency_mismatch_works_with_params(self): @@ -689,9 +690,9 @@ class TestROQLikelihoodHDF5(unittest.TestCase): _path_to_basis_mb = "/roq_basis/basis_multiband_addcal.hdf5" def setUp(self): - self.minimum_frequency = self.xp.array(20.0) - self.sampling_frequency = self.xp.array(2048.0) - self.duration = self.xp.array(16.0) + self.minimum_frequency = self.xp.asarray(20.0) + self.sampling_frequency = self.xp.asarray(2048.0) + self.duration = self.xp.asarray(16.0) self.reference_frequency = 20.0 self.waveform_approximant = "IMRPhenomD" # The SNRs of injections are 130-160 for roq_scale_factor=1 and 70-80 for roq_scale_factor=2 @@ -733,9 +734,9 @@ def test_fails_with_frequency_duration_mismatch( self.priors["chirp_mass"].maximum = 9 interferometers = bilby.gw.detector.InterferometerList(["H1"]) interferometers.set_strain_data_from_power_spectral_densities( - sampling_frequency=self.xp.array(2 * maximum_frequency), - duration=self.xp.array(duration), - start_time=self.xp.array(self.injection_parameters["geocent_time"] - duration + 1) + sampling_frequency=self.xp.asarray(2 * maximum_frequency), + duration=self.xp.asarray(duration), + start_time=self.xp.asarray(self.injection_parameters["geocent_time"] - duration + 1) ) interferometers.set_array_backend(self.xp) for ifo in interferometers: @@ -767,9 +768,9 @@ def test_fails_with_prior_mismatch(self, basis, chirp_mass_min, chirp_mass_max): self.priors["chirp_mass"].maximum = chirp_mass_max interferometers = bilby.gw.detector.InterferometerList(["H1"]) interferometers.set_strain_data_from_power_spectral_densities( - sampling_frequency=self.xp.array(self.sampling_frequency), - duration=self.xp.array(self.duration), - start_time=self.xp.array(self.injection_parameters["geocent_time"] - self.duration + 1) + sampling_frequency=self.xp.asarray(self.sampling_frequency), + duration=self.xp.asarray(self.duration), + start_time=self.xp.asarray(self.injection_parameters["geocent_time"] - self.duration + 1) ) interferometers.set_array_backend(self.xp) for ifo in interferometers: @@ -985,7 +986,7 @@ def assertLess_likelihood_errors( llr = likelihood.log_likelihood_ratio(parameters) llr_roq = likelihood_roq.log_likelihood_ratio(parameters) self.assertLess(np.abs(llr - llr_roq), max_llr_error) - self.assertEqual(llr_roq.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(llr_roq), self.xp) @pytest.mark.requires_roqs @@ -1317,9 +1318,9 @@ def test_instantiation(self): @pytest.mark.usefixtures("xp_class") class TestMBLikelihood(unittest.TestCase): def setUp(self): - self.duration = self.xp.array(16.0) - self.fmin = self.xp.array(20.0) - self.sampling_frequency = self.xp.array(2048.0) + self.duration = self.xp.asarray(16.0) + self.fmin = self.xp.asarray(20.0) + self.sampling_frequency = self.xp.asarray(2048.0) self.test_parameters = dict( chirp_mass=6.0, mass_ratio=0.5, @@ -1431,7 +1432,7 @@ def test_matches_original_likelihood( abs(likelihood.log_likelihood_ratio(parameters) - llmb), tolerance ) - self.assertEqual(llmb.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(llmb), self.xp) def test_large_accuracy_factor(self): """ @@ -1590,7 +1591,7 @@ def test_inout_weights(self, linear_interpolation): ) llr_from_weights = likelihood_mb_from_weights.log_likelihood_ratio(self.test_parameters) - self.assertEqual(llr_from_weights.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(llr_from_weights), self.xp) self.assertAlmostEqual(llr, llr_from_weights) @@ -1626,7 +1627,7 @@ def test_from_dict_weights(self, linear_interpolation): linear_interpolation=linear_interpolation, ) llr = likelihood_mb.log_likelihood_ratio(self.test_parameters) - self.assertEqual(llr.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(llr), self.xp) # reset waveform generator to check if likelihood recovered from the weights properly adds banded # frequency points to waveform arguments @@ -1643,7 +1644,7 @@ def test_from_dict_weights(self, linear_interpolation): interferometers=self.ifos, waveform_generator=wfg_mb, weights=weights ) llr_from_weights = likelihood_mb_from_weights.log_likelihood_ratio(self.test_parameters) - self.assertEqual(llr_from_weights.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(llr_from_weights), self.xp) self.assertAlmostEqual(llr, llr_from_weights) @@ -1696,7 +1697,7 @@ def test_matches_original_likelihood_low_maximum_frequency( abs(likelihood.log_likelihood_ratio(parameters) - llrmb), tolerance ) - self.assertEqual(llrmb.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(llrmb), self.xp) if __name__ == "__main__": diff --git a/test/gw/prior_test.py b/test/gw/prior_test.py index c4afa00f4..aec6f01b3 100644 --- a/test/gw/prior_test.py +++ b/test/gw/prior_test.py @@ -5,6 +5,7 @@ import sys import pickle +import array_api_compat as aac import numpy as np from astropy import cosmology from scipy.stats import ks_2samp @@ -582,7 +583,7 @@ def test_non_analytic_form_has_correct_statistics(self): alts = a_prior.sample(100000, xp=self.xp) * z_prior.sample(100000, xp=self.xp) self.assertAlmostEqual(np.mean(chis), np.mean(alts), 2) self.assertAlmostEqual(np.std(chis), np.std(alts), 2) - self.assertEqual(chis.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(chis), self.xp) @pytest.mark.array_backend @@ -600,7 +601,7 @@ def test_marginalized_prior_is_uniform(self): samples = priors.sample(100000, xp=self.xp)["a_1"] ks = ks_2samp(samples, np.random.uniform(0, priors["chi_1"].maximum, 100000)) self.assertTrue(ks.pvalue > 0.001) - self.assertEqual(samples.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(samples), self.xp) if __name__ == "__main__": diff --git a/test/gw/utils_test.py b/test/gw/utils_test.py index d082a01a1..80b8fe60d 100644 --- a/test/gw/utils_test.py +++ b/test/gw/utils_test.py @@ -3,6 +3,7 @@ from shutil import rmtree from importlib.metadata import version +import array_api_compat as aac import numpy as np import lal import lalsimulation as lalsim @@ -29,32 +30,32 @@ def tearDown(self): pass def test_asd_from_freq_series(self): - freq_data = self.xp.array([1, 2, 3]) + freq_data = self.xp.asarray([1, 2, 3]) df = 0.1 asd = gwutils.asd_from_freq_series(freq_data, df) self.assertTrue(np.all(asd == freq_data * 2 * df ** 0.5)) - self.assertEqual(asd.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(asd), self.xp) def test_psd_from_freq_series(self): - freq_data = self.xp.array([1, 2, 3]) + freq_data = self.xp.asarray([1, 2, 3]) df = 0.1 psd = gwutils.psd_from_freq_series(freq_data, df) self.assertTrue(np.all(psd == (freq_data * 2 * df ** 0.5) ** 2)) - self.assertEqual(psd.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(psd), self.xp) def test_inner_product(self): - aa = self.xp.array([1, 2, 3]) - bb = self.xp.array([5, 6, 7]) - frequency = self.xp.array([0.2, 0.4, 0.6]) + aa = self.xp.asarray([1, 2, 3]) + bb = self.xp.asarray([5, 6, 7]) + frequency = self.xp.asarray([0.2, 0.4, 0.6]) PSD = bilby.gw.detector.PowerSpectralDensity.from_aligo() ip = gwutils.inner_product(aa, bb, frequency, PSD) self.assertEqual(ip, 0) - self.assertEqual(ip.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(ip), self.xp) def test_noise_weighted_inner_product(self): - aa = self.xp.array([1e-23, 2e-23, 3e-23]) - bb = self.xp.array([5e-23, 6e-23, 7e-23]) - frequency = self.xp.array([100, 101, 102]) + aa = self.xp.asarray([1e-23, 2e-23, 3e-23]) + bb = self.xp.asarray([5e-23, 6e-23, 7e-23]) + frequency = self.xp.asarray([100, 101, 102]) PSD = bilby.gw.detector.PowerSpectralDensity.from_aligo() psd = PSD.power_spectral_density_interpolated(frequency) duration = 4 @@ -65,12 +66,12 @@ def test_noise_weighted_inner_product(self): gwutils.optimal_snr_squared(aa, psd, duration), gwutils.noise_weighted_inner_product(aa, aa, psd, duration), ) - self.assertEqual(nwip.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(nwip), self.xp) def test_matched_filter_snr(self): - signal = self.xp.array([1e-23, 2e-23, 3e-23]) - frequency_domain_strain = self.xp.array([5e-23, 6e-23, 7e-23]) - frequency = self.xp.array([100, 101, 102]) + signal = self.xp.asarray([1e-23, 2e-23, 3e-23]) + frequency_domain_strain = self.xp.asarray([5e-23, 6e-23, 7e-23]) + frequency = self.xp.asarray([100, 101, 102]) PSD = bilby.gw.detector.PowerSpectralDensity.from_aligo() psd = PSD.power_spectral_density_interpolated(frequency) duration = 4 @@ -79,7 +80,7 @@ def test_matched_filter_snr(self): signal, frequency_domain_strain, psd, duration ) self.assertEqual(mfsnr, 25.510869054168282) - self.assertEqual(mfsnr.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(mfsnr), self.xp) def test_overlap(self): signal = self.xp.linspace(1e-23, 21e-23, 21) @@ -99,7 +100,7 @@ def test_overlap(self): norm_b=gwutils.optimal_snr_squared(frequency_domain_strain, psd, duration), ) self.assertAlmostEqual(overlap, 2.76914407e-05) - self.assertEqual(overlap.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(overlap), self.xp) @pytest.mark.skip(reason="GWOSC unstable: avoiding this test") def test_get_event_time(self): @@ -319,8 +320,8 @@ def test_conversion_single(self) -> None: ra, dec = bilby.gw.utils.zenith_azimuth_to_ra_dec( zenith, azimuth, time, self.ifos ) - self.assertEqual(ra.__array_namespace__(), self.xp) - self.assertEqual(dec.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(ra), self.xp) + self.assertEqual(aac.get_namespace(dec), self.xp) def test_conversion_gives_correct_prior(self) -> None: zeniths = self.xp.asarray(self.samples["zenith"]) @@ -332,8 +333,8 @@ def test_conversion_gives_correct_prior(self) -> None: ) self.assertGreaterEqual(ks_2samp(self.samples["ra"], ras).pvalue, 0.01) self.assertGreaterEqual(ks_2samp(self.samples["dec"], decs).pvalue, 0.01) - self.assertEqual(ras.__array_namespace__(), self.xp) - self.assertEqual(decs.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(ras), self.xp) + self.assertEqual(aac.get_namespace(decs), self.xp) @pytest.mark.array_backend diff --git a/test/gw/waveform_generator_test.py b/test/gw/waveform_generator_test.py index 57d2db7c1..5fd897c02 100644 --- a/test/gw/waveform_generator_test.py +++ b/test/gw/waveform_generator_test.py @@ -1,6 +1,7 @@ import unittest from unittest import mock +import array_api_compat as aac import bilby import lalsimulation import numpy as np @@ -42,7 +43,7 @@ def dummy_func_dict_return_value( def dummy_func_array_return_value_2( array, amplitude, mu, sigma, ra, dec, geocent_time, psi, *, xp=None ): - return dict(plus=xp.array(array), cross=xp.array(array)) + return dict(plus=xp.asarray(array), cross=xp.asarray(array)) @pytest.mark.array_backend @@ -50,8 +51,8 @@ def dummy_func_array_return_value_2( class TestWaveformGeneratorInstantiationWithoutOptionalParameters(unittest.TestCase): def setUp(self): self.waveform_generator = bilby.gw.waveform_generator.WaveformGenerator( - self.xp.array(1.0), - self.xp.array(4096.0), + self.xp.asarray(1.0), + self.xp.asarray(4096.0), frequency_domain_source_model=dummy_func_dict_return_value, ) self.simulation_parameters = dict( @@ -125,11 +126,11 @@ def conversion_func(): def test_duration(self): self.assertEqual(self.waveform_generator.duration, 1) - self.assertEqual(self.waveform_generator.duration.__array_namespace__(), self.xp) + self.assertEqual(self.waveform_generator.aac.get_namespace(duration), self.xp) def test_sampling_frequency(self): self.assertEqual(self.waveform_generator.sampling_frequency, 4096) - self.assertEqual(self.waveform_generator.sampling_frequency.__array_namespace__(), self.xp) + self.assertEqual(self.waveform_generator.aac.get_namespace(sampling_frequency), self.xp) def test_source_model(self): self.assertEqual( @@ -280,8 +281,8 @@ def conversion_func(): class TestFrequencyDomainStrainMethod(unittest.TestCase): def setUp(self): self.waveform_generator = bilby.gw.waveform_generator.WaveformGenerator( - duration=self.xp.array(1.0), - sampling_frequency=self.xp.array(4096.0), + duration=self.xp.asarray(1.0), + sampling_frequency=self.xp.asarray(4096.0), frequency_domain_source_model=dummy_func_dict_return_value, ) self.simulation_parameters = dict( @@ -323,8 +324,8 @@ def test_frequency_domain_source_model_call(self): ) self.assertTrue(np.array_equal(expected["plus"], actual["plus"])) self.assertTrue(np.array_equal(expected["cross"], actual["cross"])) - self.assertEqual(actual["plus"].__array_namespace__(), self.xp) - self.assertEqual(actual["cross"].__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(actual["plus"]), self.xp) + self.assertEqual(aac.get_namespace(actual["cross"]), self.xp) def test_time_domain_source_model_call_with_ndarray(self): self.waveform_generator.frequency_domain_source_model = None @@ -342,7 +343,7 @@ def side_effect(value, value2): parameters=self.simulation_parameters ) self.assertTrue(np.array_equal(expected, actual)) - self.assertEqual(actual.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(actual), self.xp) def test_time_domain_source_model_call_with_dict(self): self.waveform_generator.frequency_domain_source_model = None @@ -361,8 +362,8 @@ def side_effect(value, value2): ) self.assertTrue(np.array_equal(expected["plus"], actual["plus"])) self.assertTrue(np.array_equal(expected["cross"], actual["cross"])) - self.assertEqual(actual["plus"].__array_namespace__(), self.xp) - self.assertEqual(actual["cross"].__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(actual["plus"]), self.xp) + self.assertEqual(aac.get_namespace(actual["cross"]), self.xp) def test_no_source_model_given(self): self.waveform_generator.time_domain_source_model = None @@ -472,8 +473,8 @@ def test_frequency_domain_caching_changing_model(self): def test_time_domain_caching_changing_model(self): self.waveform_generator = bilby.gw.waveform_generator.WaveformGenerator( - duration=self.xp.array(1.0), - sampling_frequency=self.xp.array(4096.0), + duration=self.xp.asarray(1.0), + sampling_frequency=self.xp.asarray(4096.0), time_domain_source_model=dummy_func_dict_return_value, ) original_waveform = self.waveform_generator.frequency_domain_strain( @@ -488,8 +489,8 @@ def test_time_domain_caching_changing_model(self): self.assertFalse( np.array_equal(original_waveform["plus"], new_waveform["plus"]) ) - self.assertEqual(new_waveform["plus"].__array_namespace__(), self.xp) - self.assertEqual(new_waveform["cross"].__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(new_waveform["plus"]), self.xp) + self.assertEqual(aac.get_namespace(new_waveform["cross"]), self.xp) @pytest.mark.array_backend @@ -497,8 +498,8 @@ def test_time_domain_caching_changing_model(self): class TestTimeDomainStrainMethod(unittest.TestCase): def setUp(self): self.waveform_generator = bilby.gw.waveform_generator.WaveformGenerator( - self.xp.array(1.0), - self.xp.array(4096.0), + self.xp.asarray(1.0), + self.xp.asarray(4096.0), time_domain_source_model=dummy_func_dict_return_value, ) self.simulation_parameters = dict( @@ -540,8 +541,8 @@ def test_time_domain_source_model_call(self): ) self.assertTrue(np.array_equal(expected["plus"], actual["plus"])) self.assertTrue(np.array_equal(expected["cross"], actual["cross"])) - self.assertEqual(actual["plus"].__array_namespace__(), self.xp) - self.assertEqual(actual["cross"].__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(actual["plus"]), self.xp) + self.assertEqual(aac.get_namespace(actual["cross"]), self.xp) def test_frequency_domain_source_model_call_with_ndarray(self): self.waveform_generator.time_domain_source_model = None @@ -561,7 +562,7 @@ def side_effect(value, value2): parameters=self.simulation_parameters ) self.assertTrue(np.array_equal(expected, actual)) - self.assertEqual(actual.__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(actual), self.xp) def test_frequency_domain_source_model_call_with_dict(self): self.waveform_generator.time_domain_source_model = None @@ -582,8 +583,8 @@ def side_effect(value, value2): ) self.assertTrue(np.array_equal(expected["plus"], actual["plus"])) self.assertTrue(np.array_equal(expected["cross"], actual["cross"])) - self.assertEqual(actual["plus"].__array_namespace__(), self.xp) - self.assertEqual(actual["cross"].__array_namespace__(), self.xp) + self.assertEqual(aac.get_namespace(actual["plus"]), self.xp) + self.assertEqual(aac.get_namespace(actual["cross"]), self.xp) def test_no_source_model_given(self): self.waveform_generator.time_domain_source_model = None From b330aa73e1920c53e417c42bfa2357bfdecdb9f6 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Mon, 2 Feb 2026 12:11:50 -0500 Subject: [PATCH 088/140] BUG: fix some broken formatting --- test/core/prior/slabspike_test.py | 4 ++-- test/core/utils_test.py | 6 +++--- test/gw/detector/geometry_test.py | 28 ++++++++++++++-------------- test/gw/utils_test.py | 4 ++-- test/gw/waveform_generator_test.py | 5 ++--- 5 files changed, 23 insertions(+), 24 deletions(-) diff --git a/test/core/prior/slabspike_test.py b/test/core/prior/slabspike_test.py index c8a07aae8..1ec76ab71 100644 --- a/test/core/prior/slabspike_test.py +++ b/test/core/prior/slabspike_test.py @@ -129,8 +129,8 @@ def test_ln_prob_on_slab(self): def test_ln_prob_on_spike(self): for slab_spike in self.slab_spikes: - expected = slab_spike.ln_prob(self.spike_loc) - self.assertEqual(np.inf, expected) + actual = slab_spike.ln_prob(self.spike_loc) + self.assertEqual(np.inf, actual) self.assertEqual(aac.get_namespace(actual), self.xp) def test_inverse_cdf_below_spike_with_spike_at_minimum(self): diff --git a/test/core/utils_test.py b/test/core/utils_test.py index bd1869a6a..f766f2c74 100644 --- a/test/core/utils_test.py +++ b/test/core/utils_test.py @@ -361,11 +361,11 @@ def test_returns_none_for_floats_outside_range(self): def test_returns_float_for_float_and_array(self): input_array = self.xp.asarray(np.random.random(10)) - aac.get_namespace(self.assertEqual(self.interpolant(input_array, 0.5)), self.xp) - aac.get_namespace(self.assertEqual( + 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 ) - aac.get_namespace(self.assertEqual(self.interpolant(0.5, 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): diff --git a/test/gw/detector/geometry_test.py b/test/gw/detector/geometry_test.py index b18eb218d..231b82b17 100644 --- a/test/gw/detector/geometry_test.py +++ b/test/gw/detector/geometry_test.py @@ -45,35 +45,35 @@ def tearDown(self): def test_length_setting(self): self.assertEqual(self.geometry.length, self.length) - self.assertEqual(self.geometry.aac.get_namespace(length), self.xp) + self.assertEqual(aac.get_namespace(self.geometry.length), self.xp) def test_latitude_setting(self): self.assertEqual(self.geometry.latitude, self.latitude) - self.assertEqual(self.geometry.aac.get_namespace(latitude), self.xp) + self.assertEqual(aac.get_namespace(self.geometry.latitude), self.xp) def test_longitude_setting(self): self.assertEqual(self.geometry.longitude, self.longitude) - self.assertEqual(self.geometry.aac.get_namespace(longitude), self.xp) + self.assertEqual(aac.get_namespace(self.geometry.longitude), self.xp) def test_elevation_setting(self): self.assertEqual(self.geometry.elevation, self.elevation) - self.assertEqual(self.geometry.aac.get_namespace(elevation), self.xp) + self.assertEqual(aac.get_namespace(self.geometry.elevation), self.xp) def test_xarm_azi_setting(self): self.assertEqual(self.geometry.xarm_azimuth, self.xarm_azimuth) - self.assertEqual(self.geometry.aac.get_namespace(xarm_azimuth), self.xp) + self.assertEqual(aac.get_namespace(self.geometry.xarm_azimuth), self.xp) def test_yarm_azi_setting(self): self.assertEqual(self.geometry.yarm_azimuth, self.yarm_azimuth) - self.assertEqual(self.geometry.aac.get_namespace(yarm_azimuth), self.xp) + self.assertEqual(aac.get_namespace(self.geometry.yarm_azimuth), self.xp) def test_xarm_tilt_setting(self): self.assertEqual(self.geometry.xarm_tilt, self.xarm_tilt) - self.assertEqual(self.geometry.aac.get_namespace(xarm_tilt), self.xp) + self.assertEqual(aac.get_namespace(self.geometry.xarm_tilt), self.xp) def test_yarm_tilt_setting(self): self.assertEqual(self.geometry.yarm_tilt, self.yarm_tilt) - self.assertEqual(self.geometry.aac.get_namespace(yarm_tilt), self.xp) + self.assertEqual(aac.get_namespace(self.geometry.yarm_tilt), self.xp) def test_vertex_without_update(self): _ = self.geometry.vertex @@ -155,37 +155,37 @@ def test_detector_tensor_with_x_azimuth_update(self): original = self.geometry.detector_tensor self.geometry.xarm_azimuth += 1 self.assertNotEqual(np.max(abs(self.geometry.detector_tensor - original)), 0) - self.assertEqual(self.geometry.aac.get_namespace(detector_tensor), self.xp) + self.assertEqual(aac.get_namespace(self.geometry.detector_tensor), self.xp) def test_detector_tensor_with_y_azimuth_update(self): original = self.geometry.detector_tensor self.geometry.yarm_azimuth += 1 self.assertNotEqual(np.max(abs(self.geometry.detector_tensor - original)), 0) - self.assertEqual(self.geometry.aac.get_namespace(detector_tensor), self.xp) + self.assertEqual(aac.get_namespace(self.geometry.detector_tensor), self.xp) def test_detector_tensor_with_x_tilt_update(self): original = self.geometry.detector_tensor self.geometry.xarm_tilt += 1 self.assertNotEqual(np.max(abs(self.geometry.detector_tensor - original)), 0) - self.assertEqual(self.geometry.aac.get_namespace(detector_tensor), self.xp) + self.assertEqual(aac.get_namespace(self.geometry.detector_tensor), self.xp) def test_detector_tensor_with_y_tilt_update(self): original = self.geometry.detector_tensor self.geometry.yarm_tilt += 1 self.assertNotEqual(np.max(abs(self.geometry.detector_tensor - original)), 0) - self.assertEqual(self.geometry.aac.get_namespace(detector_tensor), self.xp) + self.assertEqual(aac.get_namespace(self.geometry.detector_tensor), self.xp) def test_detector_tensor_with_longitude_update(self): original = self.geometry.detector_tensor self.geometry.longitude += 1 self.assertNotEqual(np.max(abs(self.geometry.detector_tensor - original)), 0) - self.assertEqual(self.geometry.aac.get_namespace(detector_tensor), self.xp) + self.assertEqual(aac.get_namespace(self.geometry.detector_tensor), self.xp) def test_detector_tensor_with_latitude_update(self): original = self.geometry.detector_tensor self.geometry.latitude += 1 self.assertNotEqual(np.max(abs(self.geometry.detector_tensor - original)), 0) - self.assertEqual(self.geometry.aac.get_namespace(detector_tensor), self.xp) + self.assertEqual(aac.get_namespace(self.geometry.detector_tensor), self.xp) def test_unit_vector_along_arm_default(self): with self.assertRaises(ValueError): diff --git a/test/gw/utils_test.py b/test/gw/utils_test.py index 80b8fe60d..679956b62 100644 --- a/test/gw/utils_test.py +++ b/test/gw/utils_test.py @@ -31,14 +31,14 @@ def tearDown(self): def test_asd_from_freq_series(self): freq_data = self.xp.asarray([1, 2, 3]) - df = 0.1 + df = self.xp.asarray(0.1) asd = gwutils.asd_from_freq_series(freq_data, df) self.assertTrue(np.all(asd == freq_data * 2 * df ** 0.5)) self.assertEqual(aac.get_namespace(asd), self.xp) def test_psd_from_freq_series(self): freq_data = self.xp.asarray([1, 2, 3]) - df = 0.1 + df = self.xp.asarray(0.1) psd = gwutils.psd_from_freq_series(freq_data, df) self.assertTrue(np.all(psd == (freq_data * 2 * df ** 0.5) ** 2)) self.assertEqual(aac.get_namespace(psd), self.xp) diff --git a/test/gw/waveform_generator_test.py b/test/gw/waveform_generator_test.py index 5fd897c02..d4c92b7dd 100644 --- a/test/gw/waveform_generator_test.py +++ b/test/gw/waveform_generator_test.py @@ -126,12 +126,11 @@ def conversion_func(): def test_duration(self): self.assertEqual(self.waveform_generator.duration, 1) - self.assertEqual(self.waveform_generator.aac.get_namespace(duration), self.xp) + self.assertEqual(aac.get_namespace(self.waveform_generator.duration), self.xp) def test_sampling_frequency(self): self.assertEqual(self.waveform_generator.sampling_frequency, 4096) - self.assertEqual(self.waveform_generator.aac.get_namespace(sampling_frequency), self.xp) - + self.assertEqual(aac.get_namespace(self.waveform_generator.sampling_frequency), self.xp) def test_source_model(self): self.assertEqual( self.waveform_generator.frequency_domain_source_model, From fe76a67e41d959c0b49688259e7c0f61ba78c1c0 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Mon, 2 Feb 2026 13:36:03 -0500 Subject: [PATCH 089/140] FMT: fix formatting --- bilby/core/prior/analytical.py | 2 +- test/conftest.py | 2 +- test/core/prior/analytical_test.py | 1 - test/gw/waveform_generator_test.py | 1 + 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bilby/core/prior/analytical.py b/bilby/core/prior/analytical.py index 9dd120964..11c1aa038 100644 --- a/bilby/core/prior/analytical.py +++ b/bilby/core/prior/analytical.py @@ -1530,7 +1530,7 @@ def prob(self, val, *, xp=None): index = xp.searchsorted(xp.asarray(self._values_array), val) index = xp.clip(index, 0, self.nvalues - 1) p = xp.where( - xp.asarray(self._values_array[index])== val, + xp.asarray(self._values_array[index]) == val, xp.asarray(self._weights_array[index]), xp.asarray(0.0), ) diff --git a/test/conftest.py b/test/conftest.py index 2bcd416cd..fda4a4387 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -50,7 +50,7 @@ def _xp(request): xp = jax.numpy case _: try: - xp = importlib.import_module(backend) + xp = importlib.import_module(backend) except ImportError: raise ValueError(f"Unknown backend for testing: {backend}") return aac.get_namespace(xp.ones(1)) diff --git a/test/core/prior/analytical_test.py b/test/core/prior/analytical_test.py index dddc0dfba..ec4ec975b 100644 --- a/test/core/prior/analytical_test.py +++ b/test/core/prior/analytical_test.py @@ -201,7 +201,6 @@ def test_array_sample(self): self.assertAlmostEqual(case / N, categorical_prior.prob(i), places=int(np.log10(np.sqrt(N)))) self.assertAlmostEqual(case / N, weights[i] / np.sum(weights), places=int(np.log10(np.sqrt(N)))) self.assertEqual(cases, N) - def test_single_probability(self): N = 3 diff --git a/test/gw/waveform_generator_test.py b/test/gw/waveform_generator_test.py index d4c92b7dd..2aff1c0ff 100644 --- a/test/gw/waveform_generator_test.py +++ b/test/gw/waveform_generator_test.py @@ -131,6 +131,7 @@ def test_duration(self): def test_sampling_frequency(self): self.assertEqual(self.waveform_generator.sampling_frequency, 4096) self.assertEqual(aac.get_namespace(self.waveform_generator.sampling_frequency), self.xp) + def test_source_model(self): self.assertEqual( self.waveform_generator.frequency_domain_source_model, From faf7535ed4beb429cac574064c2c2481df5c3059 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Mon, 2 Feb 2026 13:43:41 -0500 Subject: [PATCH 090/140] BUG: fix bugs in testing --- bilby/compat/utils.py | 15 ++++++++++++--- test/core/likelihood_test.py | 12 ++++++------ test/gw/utils_test.py | 4 ++-- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/bilby/compat/utils.py b/bilby/compat/utils.py index 34b819ab9..5c8e7dd95 100644 --- a/bilby/compat/utils.py +++ b/bilby/compat/utils.py @@ -1,3 +1,4 @@ +import inspect from collections.abc import Iterable import numpy as np @@ -61,8 +62,7 @@ def xp_wrap(func, no_xp=False): function The decorated function. """ - - def wrapped(self, *args, xp=None, **kwargs): + def parse_args_kwargs_for_xp(*args, xp=None, **kwargs): if not no_xp and xp is None: try: # if the user specified the target arrays in kwargs @@ -78,7 +78,16 @@ def wrapped(self, *args, xp=None, **kwargs): kwargs["xp"] = np elif not no_xp: kwargs["xp"] = xp - return func(self, *args, **kwargs) + return args, kwargs + + if inspect.isfunction(func): + def wrapped(*args, xp=None, **kwargs): + args, kwargs = parse_args_kwargs_for_xp(*args, xp=xp, **kwargs) + return func(*args, **kwargs) + else: + def wrapped(self, *args, xp=None, **kwargs): + args, kwargs = parse_args_kwargs_for_xp(*args, xp=xp, **kwargs) + return func(self, *args, **kwargs) return wrapped diff --git a/test/core/likelihood_test.py b/test/core/likelihood_test.py index aa1f92d59..bf7972c6c 100644 --- a/test/core/likelihood_test.py +++ b/test/core/likelihood_test.py @@ -58,8 +58,8 @@ def test_meta_data(self): @pytest.mark.usefixtures("xp_class") class TestAnalytical1DLikelihood(unittest.TestCase): def setUp(self): - self.x = self.xp.arange(start=0, stop=100, step=1) - self.y = self.xp.arange(start=0, stop=100, step=1) + self.x = self.xp.arange(0, 100, step=1) + self.y = self.xp.arange(0, 100, step=1) def test_func(x, parameter1, parameter2): return parameter1 * x + parameter2 @@ -83,7 +83,7 @@ def test_init_x(self): self.assertTrue(np.array_equal(self.x, self.analytical_1d_likelihood.x)) def test_set_x_to_array(self): - new_x = self.xp.arange(start=0, stop=50, step=2) + new_x = self.xp.arange(0, 50, step=2) self.analytical_1d_likelihood.x = new_x self.assertTrue(np.array_equal(new_x, self.analytical_1d_likelihood.x)) @@ -103,7 +103,7 @@ def test_init_y(self): self.assertTrue(np.array_equal(self.y, self.analytical_1d_likelihood.y)) def test_set_y_to_array(self): - new_y = self.xp.arange(start=0, stop=50, step=2) + new_y = self.xp.arange(0, 50, step=2) self.analytical_1d_likelihood.y = new_y self.assertTrue(np.array_equal(new_y, self.analytical_1d_likelihood.y)) @@ -357,7 +357,7 @@ def test_init_y(self): self.assertTrue(self.xp.array_equal(self.y, self.poisson_likelihood.y)) def test_set_y_to_array(self): - new_y = self.xp.arange(start=0, stop=50, step=2) + new_y = self.xp.arange(0, 50, step=2) self.poisson_likelihood.y = new_y self.assertTrue(self.xp.array_equal(new_y, self.poisson_likelihood.y)) @@ -463,7 +463,7 @@ def test_init_y(self): self.assertTrue(np.array_equal(self.y, self.exponential_likelihood.y)) def test_set_y_to_array(self): - new_y = self.xp.arange(start=0, stop=50, step=2) + new_y = self.xp.arange(0, 50, step=2) self.exponential_likelihood.y = new_y self.assertTrue(np.array_equal(new_y, self.exponential_likelihood.y)) diff --git a/test/gw/utils_test.py b/test/gw/utils_test.py index 679956b62..80b8fe60d 100644 --- a/test/gw/utils_test.py +++ b/test/gw/utils_test.py @@ -31,14 +31,14 @@ def tearDown(self): def test_asd_from_freq_series(self): freq_data = self.xp.asarray([1, 2, 3]) - df = self.xp.asarray(0.1) + df = 0.1 asd = gwutils.asd_from_freq_series(freq_data, df) self.assertTrue(np.all(asd == freq_data * 2 * df ** 0.5)) self.assertEqual(aac.get_namespace(asd), self.xp) def test_psd_from_freq_series(self): freq_data = self.xp.asarray([1, 2, 3]) - df = self.xp.asarray(0.1) + df = 0.1 psd = gwutils.psd_from_freq_series(freq_data, df) self.assertTrue(np.all(psd == (freq_data * 2 * df ** 0.5) ** 2)) self.assertEqual(aac.get_namespace(psd), self.xp) From 640d91170aa00c7d585e31edcf0d929a0f59b403 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Mon, 2 Feb 2026 14:16:46 -0500 Subject: [PATCH 091/140] Fix some more conversions --- bilby/compat/utils.py | 27 +++++++++++++++++---------- bilby/core/utils/calculus.py | 15 +++++++-------- test/gw/likelihood_test.py | 5 +++-- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/bilby/compat/utils.py b/bilby/compat/utils.py index 5c8e7dd95..7c2169d09 100644 --- a/bilby/compat/utils.py +++ b/bilby/compat/utils.py @@ -66,28 +66,35 @@ def parse_args_kwargs_for_xp(*args, xp=None, **kwargs): if not no_xp and xp is None: try: # if the user specified the target arrays in kwargs - # we need to be able to support this + # we need to be able to support this, if there is + # only one kwargs, pass it through alone, this is + # sometimes a dictionary of arrays so this is needed + # to remove a level of nesting if len(args) > 0: - xp = array_module(*args) - elif len(kwargs) > 0: - xp = array_module(*kwargs.values()) + xp = array_module(args) + elif len(kwargs) == 1: + xp = array_module(next(iter(kwargs.values()))) + elif len(kwargs) > 1: + xp = array_module(kwargs) else: xp = np kwargs["xp"] = xp - except TypeError: + except TypeError as e: + print("type failed", e) kwargs["xp"] = np elif not no_xp: kwargs["xp"] = xp return args, kwargs - if inspect.isfunction(func): - def wrapped(*args, xp=None, **kwargs): - args, kwargs = parse_args_kwargs_for_xp(*args, xp=xp, **kwargs) - return func(*args, **kwargs) - else: + sig = inspect.signature(func) + if any(name in sig.parameters for name in ("self", "cls")): def wrapped(self, *args, xp=None, **kwargs): args, kwargs = parse_args_kwargs_for_xp(*args, xp=xp, **kwargs) return func(self, *args, **kwargs) + else: + def wrapped(*args, xp=None, **kwargs): + args, kwargs = parse_args_kwargs_for_xp(*args, xp=xp, **kwargs) + return func(*args, **kwargs) return wrapped diff --git a/bilby/core/utils/calculus.py b/bilby/core/utils/calculus.py index 6852299ec..01a92b9c8 100644 --- a/bilby/core/utils/calculus.py +++ b/bilby/core/utils/calculus.py @@ -175,13 +175,14 @@ def logtrapzexp(lnf, dx, *, xp=np): lnfdx1 = lnf[:-1] lnfdx2 = lnf[1:] - if ( - isinstance(dx, (int, float)) or - (aac.is_array_api_obj(dx) and dx.size == 1) - ): - C = np.log(dx / 2.0) - elif isinstance(dx, (list, xp.ndarray)): + try: dx = xp.asarray(dx) + except TypeError: + raise TypeError(f"Step size dx={dx} could not be converted to an array") + + if dx.size == 1: + C = np.log(dx / 2.0) + else: if dx.size != len(lnf) - 1: raise ValueError( "Step size array must have length one less than the function length" @@ -191,8 +192,6 @@ def logtrapzexp(lnf, dx, *, xp=np): lnfdx1 = lnfdx1.copy() + lndx lnfdx2 = lnfdx2.copy() + lndx C = -xp.log(2.0) - else: - raise TypeError("Step size must be a single value or array-like") return C + logsumexp(xp.asarray([logsumexp(lnfdx1), logsumexp(lnfdx2)])) diff --git a/test/gw/likelihood_test.py b/test/gw/likelihood_test.py index 3b5839d50..60828a714 100644 --- a/test/gw/likelihood_test.py +++ b/test/gw/likelihood_test.py @@ -312,9 +312,10 @@ def test_time_reference_agrees_with_default(self): ) parameters = self.parameters.copy() parameters["H1_time"] = parameters["geocent_time"] + time_delay - self.assertEqual( + self.assertAlmostEqual( new_likelihood.log_likelihood_ratio(parameters), - self.likelihood.log_likelihood_ratio(parameters) + self.likelihood.log_likelihood_ratio(parameters), + 8, ) From 73f89b4d78f99a2e6a7229211ee0159ac93dc3e4 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Tue, 3 Feb 2026 08:23:11 -0500 Subject: [PATCH 092/140] Add pytorch core testing --- .github/workflows/unit-tests.yml | 3 ++ bilby/compat/patches.py | 4 +- bilby/compat/utils.py | 2 + bilby/core/grid.py | 35 ++++++++++---- bilby/core/likelihood.py | 7 +-- bilby/core/prior/analytical.py | 73 ++++++++++++++++-------------- bilby/core/prior/dict.py | 71 ++++++++++++++++------------- bilby/core/prior/joint.py | 20 +++++--- bilby/core/prior/slabspike.py | 7 +-- bilby/core/utils/calculus.py | 20 +++----- bilby/core/utils/io.py | 4 +- bilby/core/utils/series.py | 2 +- bilby/gw/prior.py | 6 +-- requirements.txt | 3 +- test/conftest.py | 12 +++++ test/core/grid_test.py | 16 +++---- test/core/likelihood_test.py | 30 ++++++------ test/core/prior/analytical_test.py | 22 ++++----- test/core/prior/base_test.py | 2 + test/core/prior/prior_test.py | 72 ++++++++++++++++------------- test/core/prior/slabspike_test.py | 15 +++--- test/core/series_test.py | 11 +++-- test/core/utils_test.py | 5 ++ 23 files changed, 256 insertions(+), 186 deletions(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 39aa3b1b8..f52f58610 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -70,6 +70,9 @@ jobs: run: | python -m pip install .[jax] pytest --array-backend jax --durations 10 + - name: Run torch-backend unit tests + run: | + pytest --array-backend torch --durations 10 test/core - name: Run sampler tests run: | pytest test/integration/sampler_run_test.py --durations 10 -v diff --git a/bilby/compat/patches.py b/bilby/compat/patches.py index 19ad0565a..53345abd4 100644 --- a/bilby/compat/patches.py +++ b/bilby/compat/patches.py @@ -41,8 +41,8 @@ def logsumexp(a, axis=None, b=None, keepdims=False, return_sign=False, *, xp=Non if xp is None: xp = aac.get_namespace(a) - if "jax" in xp.__name__: - # the scipy version of logsumexp cannot be vmapped + # the scipy version of logsumexp cannot be vmapped + if aac.is_jax_namespace(xp): from jax.scipy.special import logsumexp as lse else: from scipy.special import logsumexp as lse diff --git a/bilby/compat/utils.py b/bilby/compat/utils.py index 7c2169d09..a05cc0920 100644 --- a/bilby/compat/utils.py +++ b/bilby/compat/utils.py @@ -10,6 +10,8 @@ def array_module(arr): + if isinstance(arr, tuple) and len(arr) == 1: + arr = arr[0] try: return array_namespace(arr) except TypeError: diff --git a/bilby/core/grid.py b/bilby/core/grid.py index 9fda20882..07668aacf 100644 --- a/bilby/core/grid.py +++ b/bilby/core/grid.py @@ -1,5 +1,6 @@ import json import os +from copy import copy import array_api_compat as aac import numpy as np @@ -168,7 +169,7 @@ def marginalize(self, log_array, parameters=None, not_parameters=None): else: raise TypeError("Parameters names must be a list or string") - out_array = log_array.copy() + out_array = copy(log_array) names = list(self.parameter_names) for name in params: @@ -211,9 +212,17 @@ def _marginalize_single(self, log_array, name, non_marg_names=None): if len(places) > 1: dx = xp.diff(places) - out = xp.apply_along_axis( - logtrapzexp, axis, log_array, dx - ) + if log_array.ndim == 1: + out = logtrapzexp(log_array, dx=dx, xp=xp) + elif aac.is_torch_namespace(xp): + # https://discuss.pytorch.org/t/apply-a-function-along-an-axis/130440 + out = xp.stack([ + logtrapzexp(x_i, dx=dx, xp=xp) for x_i in xp.unbind(log_array, dim=axis) + ], dim=min(axis, log_array.ndim - 2)) + else: + out = xp.apply_along_axis( + logtrapzexp, axis, log_array, dx + ) else: # no marginalisation required, just remove the singleton dimension z = log_array.shape @@ -326,8 +335,11 @@ def marginalize_posterior(self, parameters=None, not_parameters=None): def _evaluate(self): xp = aac.get_namespace(self.mesh_grid[0]) - if xp.__name__ == "jax.numpy": - from jax import vmap + if aac.is_torch_namespace(xp) or aac.is_jax_namespace(xp): + if aac.is_torch_namespace(xp): + from torch import vmap + else: + from jax import vmap self._ln_likelihood = vmap(self.likelihood.log_likelihood)( {key: self.mesh_grid[i].flatten() for i, key in enumerate(self.parameter_names)} ).reshape(self.mesh_grid[0].shape) @@ -361,13 +373,13 @@ def _get_sample_points(self, grid_size, *, xp=np): self.sample_points[key] = self.priors[key].rescale( xp.linspace(0, 1, grid_size[ii])) else: - self.sample_points[key] = grid_size[ii] + self.sample_points[key] = xp.asarray(grid_size[ii]) elif isinstance(grid_size, dict): if isinstance(grid_size[key], int): self.sample_points[key] = self.priors[key].rescale( xp.linspace(0, 1, grid_size[key])) else: - self.sample_points[key] = grid_size[key] + self.sample_points[key] = xp.asarray(grid_size[key]) else: raise TypeError("Unrecognized 'grid_size' type") @@ -448,7 +460,7 @@ def save_to_file(self, filename=None, overwrite=False, outdir=None, "following message:\n {} \n\n".format(e)) @classmethod - def read(cls, filename=None, outdir=None, label=None, gzip=False): + def read(cls, filename=None, outdir=None, label=None, gzip=False, xp=None): """ Read in a saved .json grid file Parameters @@ -461,6 +473,9 @@ def read(cls, filename=None, outdir=None, label=None, gzip=False): If given, whether the file is gzipped or not (only required if the file is gzipped, but does not have the standard '.gz' file extension) + xp: array module | None + The array module to use for calculations (e.g., :code:`numpy`, + :code:`jax.numpy`). If :code:`None`, defaults to :code:`numpy`. Returns ======= @@ -484,7 +499,7 @@ def read(cls, filename=None, outdir=None, label=None, gzip=False): try: grid = cls(likelihood=None, priors=dictionary['priors'], grid_size=dictionary['sample_points'], - label=dictionary['label'], outdir=dictionary['outdir']) + label=dictionary['label'], outdir=dictionary['outdir'], xp=xp) # set the likelihood grid._ln_likelihood = dictionary['ln_likelihood'] diff --git a/bilby/core/likelihood.py b/bilby/core/likelihood.py index 8e1005a11..d69a3a469 100644 --- a/bilby/core/likelihood.py +++ b/bilby/core/likelihood.py @@ -201,7 +201,7 @@ def log_likelihood(self, parameters): xp = array_module(self.x) sigma = parameters.get("sigma", self.sigma) log_l = xp.sum(- (self.residual(parameters) / sigma)**2 / 2 - - xp.log(2 * np.pi * sigma**2) / 2) + xp.log(xp.asarray(2 * np.pi * sigma**2)) / 2) return log_l def __repr__(self): @@ -284,7 +284,8 @@ def y(self, y): y = np.atleast_1d(y) xp = aac.get_namespace(y) # check array is a non-negative integer array - if y.dtype.kind not in 'ui' or xp.any(y < 0): + # torch doesn't support checking dtype kind + if (not aac.is_torch_namespace(xp) and y.dtype.kind not in 'ui') or xp.any(y < 0): raise ValueError("Data must be non-negative integers") self.__y = y @@ -377,7 +378,7 @@ def log_likelihood(self, parameters): xp = array_module(self.x) log_l =\ xp.sum(- (nu + 1) * xp.log1p(self.lam * self.residual(parameters=parameters)**2 / nu) / 2 + - xp.log(self.lam / (nu * np.pi)) / 2 + + xp.log(xp.asarray(self.lam / (nu * np.pi))) / 2 + gammaln((nu + 1) / 2) - gammaln(nu / 2)) return log_l diff --git a/bilby/core/prior/analytical.py b/bilby/core/prior/analytical.py index 11c1aa038..d0a9bc22e 100644 --- a/bilby/core/prior/analytical.py +++ b/bilby/core/prior/analytical.py @@ -122,7 +122,7 @@ def rescale(self, val, *, xp=None): Union[float, array_like]: Rescaled probability """ if self.alpha == -1: - return self.minimum * xp.exp(val * xp.log(self.maximum / self.minimum)) + return self.minimum * xp.exp(val * xp.log(xp.asarray(self.maximum / self.minimum))) else: return (self.minimum ** (1 + self.alpha) + val * (self.maximum ** (1 + self.alpha) - self.minimum ** (1 + self.alpha))) ** (1. / (1 + self.alpha)) @@ -140,7 +140,7 @@ def prob(self, val, *, xp=None): float: Prior probability of val """ if self.alpha == -1: - return xp.nan_to_num(1 / val / xp.log(self.maximum / self.minimum)) * self.is_in_prior_range(val) + return xp.nan_to_num(1 / val / xp.log(xp.asarray(self.maximum / self.minimum))) * self.is_in_prior_range(val) else: return xp.nan_to_num(val ** self.alpha * (1 + self.alpha) / (self.maximum ** (1 + self.alpha) - @@ -160,10 +160,11 @@ def ln_prob(self, val, *, xp=None): """ if self.alpha == -1: - normalising = 1. / xp.log(self.maximum / self.minimum) + normalising = 1. / xp.log(xp.asarray(self.maximum / self.minimum)) else: - normalising = (1 + self.alpha) / (self.maximum ** (1 + self.alpha) - - self.minimum ** (1 + self.alpha)) + normalising = (1 + self.alpha) / xp.asarray( + self.maximum ** (1 + self.alpha) - self.minimum ** (1 + self.alpha) + ) with np.errstate(divide='ignore', invalid='ignore'): ln_in_range = xp.log(1. * self.is_in_prior_range(val)) @@ -175,7 +176,7 @@ def ln_prob(self, val, *, xp=None): def cdf(self, val, *, xp=None): if self.alpha == -1: with np.errstate(invalid="ignore"): - _cdf = xp.log(val / self.minimum) / xp.log(self.maximum / self.minimum) + _cdf = xp.log(val / self.minimum) / xp.log(xp.asarray(self.maximum / self.minimum)) else: _cdf = ( (val ** (self.alpha + 1) - self.minimum ** (self.alpha + 1)) @@ -334,7 +335,11 @@ def rescale(self, val, *, xp=None): ======= Union[float, array_like]: Rescaled probability """ - return xp.sign(2 * val - 1) * self.minimum * xp.exp(abs(2 * val - 1) * xp.log(self.maximum / self.minimum)) + return ( + xp.sign(2 * val - 1) + * self.minimum + * xp.exp(xp.abs(2 * val - 1) * xp.log(xp.asarray(self.maximum / self.minimum))) + ) @xp_wrap def prob(self, val, *, xp=None): @@ -349,7 +354,7 @@ def prob(self, val, *, xp=None): float: Prior probability of val """ val = xp.abs(val) - return (xp.nan_to_num(0.5 / val / xp.log(self.maximum / self.minimum)) * + return (xp.nan_to_num(0.5 / val / xp.log(xp.asarray(self.maximum / self.minimum))) * self.is_in_prior_range(val)) @xp_wrap @@ -365,11 +370,11 @@ def ln_prob(self, val, *, xp=None): float: """ - return xp.nan_to_num(- xp.log(2 * xp.abs(val)) - xp.log(xp.log(self.maximum / self.minimum))) + return xp.nan_to_num(- xp.log(2 * xp.abs(val)) - xp.log(xp.log(xp.asarray(self.maximum / self.minimum)))) @xp_wrap def cdf(self, val, *, xp=None): - asymmetric = xp.log(abs(val) / self.minimum) / xp.log(self.maximum / self.minimum) + asymmetric = xp.log(xp.abs(val) / self.minimum) / xp.log(xp.asarray(self.maximum / self.minimum)) return xp.clip(0.5 * (1 + xp.sign(val) * asymmetric), 0, 1) @@ -404,8 +409,8 @@ def rescale(self, val, *, xp=None): This maps to the inverse CDF. This has been analytically solved for this case. """ - norm = 1 / (xp.sin(self.maximum) - xp.sin(self.minimum)) - return xp.arcsin(val / norm + xp.sin(self.minimum)) + norm = 1 / (xp.sin(xp.asarray(self.maximum)) - xp.sin(xp.asarray(self.minimum))) + return xp.arcsin(val / norm + xp.sin(xp.asarray(self.minimum))) @xp_wrap def prob(self, val, *, xp=None): @@ -424,8 +429,8 @@ def prob(self, val, *, xp=None): @xp_wrap def cdf(self, val, *, xp=None): _cdf = ( - (xp.sin(val) - xp.sin(self.minimum)) / - (xp.sin(self.maximum) - xp.sin(self.minimum)) + (xp.sin(val) - xp.sin(xp.asarray(self.minimum))) / + (xp.sin(xp.asarray(self.maximum)) - xp.sin(xp.asarray(self.minimum))) ) _cdf *= val >= self.minimum _cdf *= val <= self.maximum @@ -464,8 +469,8 @@ def rescale(self, val, *, xp=None): This maps to the inverse CDF. This has been analytically solved for this case. """ - norm = 1 / (xp.cos(self.minimum) - xp.cos(self.maximum)) - return xp.arccos(xp.cos(self.minimum) - val / norm) + norm = 1 / (xp.cos(xp.asarray(self.minimum)) - xp.cos(xp.asarray(self.maximum))) + return xp.arccos(xp.cos(xp.asarray(self.minimum)) - val / norm) @xp_wrap def prob(self, val, *, xp=None): @@ -484,8 +489,8 @@ def prob(self, val, *, xp=None): @xp_wrap def cdf(self, val, *, xp=None): _cdf = ( - (xp.cos(val) - xp.cos(self.minimum)) - / (xp.cos(self.maximum) - xp.cos(self.minimum)) + (xp.cos(val) - xp.cos(xp.asarray(self.minimum))) + / (xp.cos(xp.asarray(self.maximum)) - xp.cos(xp.asarray(self.minimum))) ) _cdf *= val >= self.minimum _cdf *= val <= self.maximum @@ -560,7 +565,7 @@ def ln_prob(self, val, *, xp=None): ======= Union[float, array_like]: Prior probability of val """ - return -0.5 * ((self.mu - val) ** 2 / self.sigma ** 2 + xp.log(2 * np.pi * self.sigma ** 2)) + return -0.5 * ((self.mu - val) ** 2 / self.sigma ** 2 + xp.log(xp.asarray(2 * np.pi * self.sigma ** 2))) def cdf(self, val, *, xp=None): return (1 - erf((self.mu - val) / 2 ** 0.5 / self.sigma)) / 2 @@ -754,15 +759,15 @@ def ln_prob(self, val, *, xp=None): """ with np.errstate(divide="ignore", invalid="ignore"): return xp.nan_to_num(( - -(xp.log(xp.maximum(val, self.minimum)) - self.mu) ** 2 / self.sigma ** 2 / 2 - - xp.log(xp.sqrt(2 * np.pi) * val * self.sigma) + -(xp.log(xp.maximum(val, xp.asarray(self.minimum))) - self.mu) ** 2 / self.sigma ** 2 / 2 + - xp.log((2 * np.pi)**0.5 * val * self.sigma) ) + xp.log(val > self.minimum), nan=-xp.inf, neginf=-xp.inf, posinf=-xp.inf) @xp_wrap def cdf(self, val, *, xp=None): with np.errstate(divide="ignore"): return 0.5 + erf( - (xp.log(xp.maximum(val, self.minimum)) - self.mu) / self.sigma / np.sqrt(2) + (xp.log(xp.maximum(val, xp.asarray(self.minimum))) - self.mu) / self.sigma / np.sqrt(2) ) / 2 @@ -828,12 +833,12 @@ def ln_prob(self, val, *, xp=None): Union[float, array_like]: Prior probability of val """ with np.errstate(divide="ignore"): - return -val / self.mu - xp.log(self.mu) + xp.log(val >= self.minimum) + return -val / self.mu - xp.log(xp.asarray(self.mu)) + xp.log(val >= self.minimum) @xp_wrap def cdf(self, val, *, xp=None): with np.errstate(divide="ignore", invalid="ignore", over="ignore"): - return xp.maximum(1. - xp.exp(-val / self.mu), 0) + return xp.maximum(1. - xp.exp(-val / self.mu), xp.asarray(0.0)) class StudentT(Prior): @@ -916,7 +921,7 @@ def ln_prob(self, val, *, xp=None): """ return ( gammaln(0.5 * (self.df + 1)) - gammaln(0.5 * self.df) - - xp.log((np.pi * self.df)**0.5 * self.scale) - (self.df + 1) / 2 + - xp.log(xp.asarray((np.pi * self.df)**0.5 * self.scale)) - (self.df + 1) / 2 * xp.log(1 + ((val - self.mu) / self.scale) ** 2 / self.df) ) @@ -1053,7 +1058,7 @@ def rescale(self, val, *, xp=None): """ with np.errstate(divide="ignore"): val = xp.asarray(val) - return self.mu + self.scale * xp.log(xp.maximum(val / (1 - val), 0)) + return self.mu + self.scale * xp.log(xp.maximum(val / (1 - val), xp.asarray(0))) @xp_wrap def prob(self, val, *, xp=None): @@ -1083,7 +1088,7 @@ def ln_prob(self, val, *, xp=None): """ with np.errstate(over="ignore"): return -(val - self.mu) / self.scale -\ - 2. * xp.log1p(xp.exp(-(val - self.mu) / self.scale)) - xp.log(self.scale) + 2. * xp.log1p(xp.exp(-(val - self.mu) / self.scale)) - xp.log(xp.asarray(self.scale)) @xp_wrap def cdf(self, val, *, xp=None): @@ -1155,7 +1160,7 @@ def ln_prob(self, val, *, xp=None): ======= Union[float, array_like]: Log prior probability of val """ - return - xp.log(self.beta * np.pi) - xp.log(1. + ((val - self.alpha) / self.beta) ** 2) + return - xp.log(xp.asarray(self.beta * np.pi)) - xp.log(1. + ((val - self.alpha) / self.beta) ** 2) @xp_wrap def cdf(self, val, *, xp=None): @@ -1240,7 +1245,7 @@ def ln_prob(self, val, *, xp=None): @xp_wrap def cdf(self, val, *, xp=None): - return gammainc(xp.asarray(self.k), xp.maximum(val, self.minimum) / self.theta) + return gammainc(xp.asarray(self.k), xp.maximum(val, xp.asarray(self.minimum)) / self.theta) class ChiSquared(Gamma): @@ -1327,7 +1332,7 @@ def __init__(self, sigma, mu=None, r=None, name=None, latex_label=None, raise ValueError("For the Fermi-Dirac prior the values of sigma and r " "must be positive.") - xp = array_module(np) + xp = array_module((mu, sigma, r)) self.expr = xp.exp(self.r) @xp_wrap @@ -1349,7 +1354,7 @@ def rescale(self, val, *, xp=None): `_, 2017. """ inv = -1 / self.expr + (1 + self.expr)**-val + (1 + self.expr)**-val / self.expr - return -self.sigma * xp.log(xp.maximum(inv, 0)) + return -self.sigma * xp.log(xp.maximum(inv, xp.asarray(0))) @xp_wrap def prob(self, val, *, xp=None): @@ -1365,7 +1370,7 @@ def prob(self, val, *, xp=None): """ return ( (xp.exp((val - self.mu) / self.sigma) + 1)**-1 - / (self.sigma * xp.log1p(self.expr)) + / (self.sigma * xp.log1p(xp.asarray(self.expr))) * (val >= self.minimum) ) @@ -1406,8 +1411,8 @@ def cdf(self, val, *, xp=None): `_, 2017. """ result = ( - (xp.logaddexp(0, -self.r) - xp.logaddexp(-val / self.sigma, -self.r)) - / xp.logaddexp(0, self.r) + (xp.logaddexp(xp.asarray(0.0), -xp.asarray(self.r)) - xp.logaddexp(-val / self.sigma, -xp.asarray(self.r))) + / xp.logaddexp(xp.asarray(0.0), xp.asarray(self.r)) ) return xp.clip(result, 0, 1) diff --git a/bilby/core/prior/dict.py b/bilby/core/prior/dict.py index 47d92b663..d921cb8f4 100644 --- a/bilby/core/prior/dict.py +++ b/bilby/core/prior/dict.py @@ -5,6 +5,7 @@ from io import open as ioopen from warnings import warn +import array_api_extra as xpx import numpy as np from .analytical import DeltaFunction @@ -58,12 +59,13 @@ def __init__(self, dictionary=None, filename=None, conversion_function=None): def __hash__(self): return hash(str(self)) - def evaluate_constraints(self, sample): + @xp_wrap + def evaluate_constraints(self, sample, *, xp=None): out_sample = self.conversion_function(sample) try: - prob = np.ones_like(next(iter(out_sample.values()))) + prob = xp.ones_like(next(iter(out_sample.values())), dtype=bool) except TypeError: - prob = np.ones_like(out_sample) + prob = xp.ones_like(out_sample, dtype=bool) for key in self: if isinstance(self[key], Constraint) and key in out_sample: prob *= self[key].prob(out_sample[key]) @@ -385,7 +387,7 @@ def sample_subset_constrained_as_array(self, keys=iter([]), size=None, *, xp=np) samples_dict = self.sample_subset_constrained(keys=keys, size=size, xp=xp) samples_dict = {key: xp.atleast_1d(val) for key, val in samples_dict.items()} samples_list = [samples_dict[key] for key in keys] - return xp.asarray(samples_list) + return xp.stack(samples_list) def sample_subset(self, keys=iter([]), size=None, *, xp=np): """Draw samples from the prior set for parameters which are not a DeltaFunction @@ -474,18 +476,20 @@ def check_efficiency(n_tested, n_valid): for key in keys.copy(): if isinstance(self[key], Constraint): del keys[keys.index(key)] - all_samples = {key: np.array([]) for key in keys} + all_samples = {key: xp.asarray([]) for key in keys} _first_key = list(all_samples.keys())[0] while len(all_samples[_first_key]) < needed: samples = self.sample_subset(keys=keys, size=needed, xp=xp) - keep = np.array(self.evaluate_constraints(samples), dtype=bool) + keep = self.evaluate_constraints(samples, xp=xp) for key in keys: all_samples[key] = xp.hstack( [all_samples[key], samples[key][keep].flatten()] ) n_tested_samples += needed - n_valid_samples += np.sum(keep) + n_valid_samples += int(xp.sum(keep)) check_efficiency(n_tested_samples, n_valid_samples) + if not isinstance(size, tuple): + size = (size,) all_samples = { key: xp.reshape(all_samples[key][:needed], size) for key in keys } @@ -527,7 +531,8 @@ def _estimate_normalization(self, keys, min_accept, sampling_chunk, *, xp=np): factor = len(keep) / np.count_nonzero(keep) return factor - def prob(self, sample, **kwargs): + @xp_wrap + def prob(self, sample, *, xp=None, **kwargs): """ Parameters @@ -542,31 +547,31 @@ def prob(self, sample, **kwargs): float: Joint probability of all individual sample probabilities """ - xp = array_module(sample.values()) - prob = xp.prod(xp.asarray([self[key].prob(sample[key]) for key in sample]), **kwargs) + prob = xp.prod(xp.stack([self[key].prob(sample[key], xp=xp) for key in sample]), **kwargs) - return prob - # return self.check_prob(sample, prob) + return self.check_prob(sample, prob, xp=xp) - def check_prob(self, sample, prob): + @xp_wrap + def check_prob(self, sample, prob, *, xp=None): ratio = self.normalize_constraint_factor(tuple(sample.keys())) - if np.all(prob == 0.0): + if xp.all(prob == 0.0): return prob * ratio else: if isinstance(prob, float): - if self.evaluate_constraints(sample): + if self.evaluate_constraints(sample, xp=xp): return prob * ratio else: return 0.0 else: - constrained_prob = np.zeros_like(prob) - in_bounds = np.isfinite(prob) + constrained_prob = xp.zeros_like(prob) + in_bounds = xp.isfinite(prob) subsample = {key: sample[key][in_bounds] for key in sample} - keep = np.array(self.evaluate_constraints(subsample), dtype=bool) - constrained_prob[in_bounds] = prob[in_bounds] * keep * ratio + keep = self.evaluate_constraints(subsample, xp=xp) + constrained_prob = xpx.at(constrained_prob, in_bounds).set(prob[in_bounds] * keep * ratio) return constrained_prob - def ln_prob(self, sample, axis=None, normalized=True): + @xp_wrap + def ln_prob(self, sample, axis=None, normalized=True, *, xp=None): """ Parameters @@ -585,29 +590,32 @@ def ln_prob(self, sample, axis=None, normalized=True): Joint log probability of all the individual sample probabilities """ - ln_prob = np.sum([self[key].ln_prob(sample[key]) for key in sample], axis=axis) + ln_prob = xp.sum(xp.stack([self[key].ln_prob(sample[key], xp=xp) for key in sample]), axis=axis) return self.check_ln_prob(sample, ln_prob, - normalized=normalized) + normalized=normalized, xp=xp) - def check_ln_prob(self, sample, ln_prob, normalized=True): + @xp_wrap + def check_ln_prob(self, sample, ln_prob, normalized=True, *, xp=None): if normalized: ratio = self.normalize_constraint_factor(tuple(sample.keys())) else: ratio = 1 - if np.all(np.isinf(ln_prob)): + if xp.all(xp.isfinite(ln_prob)): return ln_prob else: if isinstance(ln_prob, float): - if np.all(self.evaluate_constraints(sample)): - return ln_prob + np.log(ratio) + if xp.all(self.evaluate_constraints(sample, xp=xp)): + return ln_prob + xp.log(ratio) else: return -np.inf else: - constrained_ln_prob = -np.inf * np.ones_like(ln_prob) - in_bounds = np.isfinite(ln_prob) + constrained_ln_prob = -np.inf * xp.ones_like(ln_prob) + in_bounds = xp.isfinite(ln_prob) subsample = {key: sample[key][in_bounds] for key in sample} - keep = np.log(np.array(self.evaluate_constraints(subsample), dtype=bool)) - constrained_ln_prob[in_bounds] = ln_prob[in_bounds] + keep + np.log(ratio) + keep = xp.log(self.evaluate_constraints(subsample, xp=xp)) + constrained_ln_prob = xpx.at(constrained_ln_prob, in_bounds).set( + ln_prob[in_bounds] + keep + xp.log(ratio) + ) return constrained_ln_prob @xp_wrap @@ -827,8 +835,7 @@ def prob(self, sample, *, xp=None, **kwargs): for key in sample ]) prob = xp.prod(res, **kwargs) - return prob - # return self.check_prob(sample, prob) + return self.check_prob(sample, prob, xp=xp) def ln_prob(self, sample, axis=None, normalized=True): """ diff --git a/bilby/core/prior/joint.py b/bilby/core/prior/joint.py index 924a24c6e..238c0d791 100644 --- a/bilby/core/prior/joint.py +++ b/bilby/core/prior/joint.py @@ -327,7 +327,9 @@ def rescale(self, value, *, xp=None, **kwargs): raise ValueError("Array is the wrong shape") samp = self._rescale(samp, **kwargs) - return xp.squeeze(samp) + if samp.shape[0] == 1: + samp = xp.squeeze(samp, axis=0) + return samp def _rescale(self, samp, **kwargs): """ @@ -627,8 +629,8 @@ def _rescale(self, samp, *, xp=None, **kwargs): samp = erfinv(2.0 * samp - 1) * 2.0 ** 0.5 # rotate and scale to the multivariate normal shape - samp = xp.asarray(self.mus[mode]) + self.sigmas[mode] * xp.einsum( - "ij,kj->ik", samp * self.sqeigvalues[mode], self.eigvectors[mode] + samp = xp.asarray(self.mus[mode]) + xp.asarray(self.sigmas[mode]) * xp.einsum( + "ij,kj->ik", samp * self.sqeigvalues[mode], xp.asarray(self.eigvectors[mode]) ) return samp @@ -684,11 +686,11 @@ def _ln_prob(self, samp, lnprob, outbounds, *, xp=None): # self.mvn[i] is a "standard" multivariate normal distribution; see add_mode() z = (samp[j] - self.mus[i]) / self.sigmas[i] lnprob = xpx.at(lnprob, j).set( - xp.logaddexp(lnprob[j], self.mvn[i].logpdf(z) - self.logprodsigmas[i]) + xp.logaddexp(lnprob[j], self.mvn[i].logpdf(z) - xp.asarray(self.logprodsigmas[i])) ) # set out-of-bounds values to -inf - lnprob = xp.where(outbounds, -xp.inf, lnprob) + lnprob = xp.where(xp.asarray(outbounds), -np.inf, lnprob) return lnprob def __eq__(self, other): @@ -801,7 +803,11 @@ def rescale(self, val, *, xp=None, **kwargs): self.dist.rescale_parameters[self.name] = val if self.dist.filled_rescale(): - values = xp.asarray(list(self.dist.rescale_parameters.values())).T + # print(self.dist.rescale_parameters) + values = xp.stack([ + xp.asarray(val) for val in self.dist.rescale_parameters.values() + ]).T + # values = xp.asarray(list(self.dist.rescale_parameters.values())).T samples = self.dist.rescale(values, **kwargs) self.dist.reset_rescale() return samples @@ -878,7 +884,7 @@ def ln_prob(self, val, *, xp=None): "number of requested values." ) - lnp = self.dist.ln_prob(xp.asarray(values).T) + lnp = self.dist.ln_prob(xp.stack(values).T) # reset the requested parameters self.dist.reset_request() diff --git a/bilby/core/prior/slabspike.py b/bilby/core/prior/slabspike.py index 23aed86d3..ad4f118b8 100644 --- a/bilby/core/prior/slabspike.py +++ b/bilby/core/prior/slabspike.py @@ -97,9 +97,10 @@ def rescale(self, val, *, xp=None): val - self.spike_height * higher_indices, xp=xp ) - res = xp.select( - [lower_indices | higher_indices, intermediate_indices], - [slab_scaled, self.spike_location], + res = xp.where( + lower_indices | higher_indices, + slab_scaled, + xp.asarray(self.spike_location), ) return res diff --git a/bilby/core/utils/calculus.py b/bilby/core/utils/calculus.py index 01a92b9c8..496946c2b 100644 --- a/bilby/core/utils/calculus.py +++ b/bilby/core/utils/calculus.py @@ -180,20 +180,14 @@ def logtrapzexp(lnf, dx, *, xp=np): except TypeError: raise TypeError(f"Step size dx={dx} could not be converted to an array") - if dx.size == 1: - C = np.log(dx / 2.0) - else: - if dx.size != len(lnf) - 1: - raise ValueError( - "Step size array must have length one less than the function length" - ) - - lndx = xp.log(dx) - lnfdx1 = lnfdx1.copy() + lndx - lnfdx2 = lnfdx2.copy() + lndx - C = -xp.log(2.0) + if dx.ndim > 0 and len(dx) != len(lnf) - 1: + raise ValueError( + "Step size array must have length one less than the function length" + ) + lnfdx1 = lnfdx1 + xp.log(dx) + lnfdx2 = lnfdx2 + xp.log(dx) - return C + logsumexp(xp.asarray([logsumexp(lnfdx1), logsumexp(lnfdx2)])) + return logsumexp(xp.asarray([logsumexp(lnfdx1), logsumexp(lnfdx2)])) - np.log(2) class interp1d(_interp1d): diff --git a/bilby/core/utils/io.py b/bilby/core/utils/io.py index d24b16fe2..f4c9bc4e8 100644 --- a/bilby/core/utils/io.py +++ b/bilby/core/utils/io.py @@ -60,7 +60,7 @@ def default(self, obj): return encode_astropy_unit(obj) except ImportError: logger.debug("Cannot import astropy, cannot write cosmological priors") - if hasattr(obj, "__array_namespace__"): + if aac.is_array_api_obj(obj): return { "__array__": True, "__array_namespace__": aac.get_namespace(obj).__name__, @@ -445,7 +445,7 @@ def encode_for_hdf5(key, item): if item.dtype.kind == 'U': logger.debug(f'converting dtype {item.dtype} for hdf5') item = np.array(item, dtype='S') - elif hasattr(item, "__array_namespace__"): + elif aac.is_array_api_obj(item): # temporarily dump all arrays as numpy arrays, we should figure ou # how to properly deserialize them item = np.asarray(item) diff --git a/bilby/core/utils/series.py b/bilby/core/utils/series.py index c60362ab3..4fa20b51a 100644 --- a/bilby/core/utils/series.py +++ b/bilby/core/utils/series.py @@ -123,7 +123,7 @@ def create_frequency_series(sampling_frequency, duration): """ xp = array_module(sampling_frequency) _check_legal_sampling_frequency_and_duration(sampling_frequency, duration) - number_of_samples = int(xp.round(duration * sampling_frequency)) + number_of_samples = xp.round(duration * sampling_frequency) number_of_frequencies = int(xp.round(number_of_samples / 2) + 1) return xp.linspace(0, sampling_frequency / 2, num=number_of_frequencies) diff --git a/bilby/gw/prior.py b/bilby/gw/prior.py index cd0cf182d..9127edeb2 100644 --- a/bilby/gw/prior.py +++ b/bilby/gw/prior.py @@ -1744,13 +1744,13 @@ def _ln_prob(self, samp, lnprob, outbounds, *, xp=None): phi, dec = samp[0] theta = 0.5 * np.pi - dec pixel = self.hp.ang2pix(self.nside, theta, phi) - xpx.at(lnprob, i).set(xp.log(self.prob[pixel] / self.pixel_area)) + xpx.at(lnprob, i).set(xp.log(xp.asarray(self.prob[pixel] / self.pixel_area))) if self.distance: self.update_distance(pixel) lnprob = xpx.at(lnprob, i).set( - lnprob[i] + xp.log(self.distance_pdf(dist) * dist ** 2) + lnprob[i] + xp.log(xp.asarray(self.distance_pdf(dist) * dist ** 2)) ) - lnprob = xp.where(outbounds, -np.inf, lnprob) + lnprob = xp.where(xp.asarray(outbounds), -np.inf, lnprob) return lnprob def __eq__(self, other): diff --git a/requirements.txt b/requirements.txt index ead66363d..f1a91484d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ -array_api_compat +# see https://github.com/data-apis/array-api-compat/pull/341 +array_api_compat>=1.13 array_api_extra dynesty>=2.0.1 emcee diff --git a/test/conftest.py b/test/conftest.py index fda4a4387..a9be96548 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -48,8 +48,20 @@ def _xp(request): os.environ["SCIPY_ARRAY_API"] = "1" jax.config.update("jax_enable_x64", True) xp = jax.numpy + case "torch": + import torch + # torch starts a lot of threads, so disable this on the first import + # to avoid segfaults + try: + torch.set_num_threads(1) + torch.set_num_interop_threads(1) + torch.set_default_dtype(torch.float64) + except RuntimeError: + pass + xp = torch case _: try: + xp = importlib.import_module(backend) except ImportError: raise ValueError(f"Unknown backend for testing: {backend}") diff --git a/test/core/grid_test.py b/test/core/grid_test.py index 009ab2c15..781077f34 100644 --- a/test/core/grid_test.py +++ b/test/core/grid_test.py @@ -2,11 +2,11 @@ import shutil import os +import array_api_compat as aac import numpy as np import pytest import bilby -from bilby.compat.patches import multivariate_logpdf class MultiGaussian(bilby.Likelihood): @@ -17,15 +17,13 @@ def __init__(self, mean, cov, *, xp=np): self.cov = xp.asarray(cov) self.mean = xp.asarray(mean) self.sigma = xp.sqrt(xp.diag(self.cov)) - self.logpdf = multivariate_logpdf(xp=xp, mean=self.mean, cov=self.cov) @property def dim(self): return len(self.cov[0]) def log_likelihood(self, parameters): - x = self.xp.asarray([parameters["x{0}".format(i)] for i in range(self.dim)]) - return self.logpdf(x) + return -parameters["x0"]**2 / 2 - parameters["x1"]**2 / 2 - np.log(2 * np.pi) @pytest.mark.array_backend @@ -146,7 +144,9 @@ def test_max_marginalized_likelihood(self): self.assertEqual(1.0, self.grid.marginalize_likelihood(self.grid.parameter_names[1]).max()) def test_ln_evidence(self): - self.assertAlmostEqual(self.expected_ln_evidence, self.grid.ln_evidence, places=5) + ln_z = self.grid.ln_evidence + self.assertEqual(aac.get_namespace(ln_z), self.xp) + self.assertAlmostEqual(self.expected_ln_evidence, float(ln_z), places=5) def test_fail_grid_size(self): with self.assertRaises(TypeError): @@ -218,7 +218,7 @@ def test_grid_from_array(self): def test_save_and_load_from_filename(self): filename = os.path.join("outdir", "test_output.json") self.grid.save_to_file(filename=filename) - new_grid = bilby.core.grid.Grid.read(filename=filename) + new_grid = bilby.core.grid.Grid.read(filename=filename, xp=self.xp) self.assertListEqual(new_grid.parameter_names, self.grid.parameter_names) self.assertEqual(new_grid.n_dims, self.grid.n_dims) @@ -231,7 +231,7 @@ def test_save_and_load_from_filename(self): def test_save_and_load_from_outdir_label(self): self.grid.save_to_file(overwrite=True, outdir="outdir") - new_grid = bilby.core.grid.Grid.read(outdir="outdir", label="label") + new_grid = bilby.core.grid.Grid.read(outdir="outdir", label="label", xp=self.xp) self.assertListEqual(self.grid.parameter_names, new_grid.parameter_names) self.assertEqual(self.grid.n_dims, new_grid.n_dims) @@ -248,7 +248,7 @@ def test_save_and_load_from_outdir_label(self): def test_save_and_load_gzip(self): filename = os.path.join("outdir", "test_output.json.gz") self.grid.save_to_file(filename=filename) - new_grid = bilby.core.grid.Grid.read(filename=filename) + new_grid = bilby.core.grid.Grid.read(filename=filename, xp=self.xp) self.assertListEqual(self.grid.parameter_names, new_grid.parameter_names) self.assertEqual(self.grid.n_dims, new_grid.n_dims) diff --git a/test/core/likelihood_test.py b/test/core/likelihood_test.py index bf7972c6c..05a42d3f5 100644 --- a/test/core/likelihood_test.py +++ b/test/core/likelihood_test.py @@ -308,8 +308,8 @@ def setUp(self): self.mu = 5 self.x = self.xp.linspace(0, 1, self.N) self.y = self.xp.asarray(np.random.poisson(self.mu, self.N)) - self.yfloat = self.y.copy() * 1.0 - self.yneg = self.y.copy() + self.yfloat = self.y * 1.0 + self.yneg = self.y * 1.0 self.yneg = xpx.at(self.yneg, 0).set(-1) def test_function(x, c): @@ -335,6 +335,8 @@ def tearDown(self): del self.poisson_likelihood def test_init_y_non_integer(self): + if aac.is_torch_namespace(self.xp): + pytest.skip("Torch tensor dtype does not have a 'kind' attribute") with self.assertRaises(ValueError): PoissonLikelihood(self.x, self.yfloat, self.function) @@ -354,12 +356,14 @@ def test_neg_rate_array(self): likelihood.log_likelihood(parameters) def test_init_y(self): - self.assertTrue(self.xp.array_equal(self.y, self.poisson_likelihood.y)) + self.assertEqual(aac.get_namespace(self.y), aac.get_namespace(self.poisson_likelihood.y)) + np.testing.assert_array_equal(np.asarray(self.y), np.asarray(self.poisson_likelihood.y)) def test_set_y_to_array(self): new_y = self.xp.arange(0, 50, step=2) self.poisson_likelihood.y = new_y - self.assertTrue(self.xp.array_equal(new_y, self.poisson_likelihood.y)) + self.assertEqual(aac.get_namespace(new_y), aac.get_namespace(self.poisson_likelihood.y)) + np.testing.assert_array_equal(np.asarray(new_y), np.asarray(self.poisson_likelihood.y)) def test_set_y_to_positive_int(self): new_y = 5 @@ -420,7 +424,7 @@ def setUp(self): self.mu = 5 self.x = self.xp.linspace(0, 1, self.N) self.y = self.xp.asarray(np.random.exponential(self.mu, self.N)) - self.yneg = self.y.copy() + self.yneg = self.y * 1.0 self.yneg = xpx.at(self.yneg, 0).set(-1.0) def test_function(x, c): @@ -514,9 +518,9 @@ def setUp(self): self.sigma = [1, 2, 3] self.mean = [10, 11, 12] if self.xp != np: - self.cov = self.xp.asarray(self.cov) - self.sigma = self.xp.asarray(self.sigma) - self.mean = self.xp.asarray(self.mean) + self.cov = self.xp.asarray(self.cov, dtype=float) + self.sigma = self.xp.asarray(self.sigma, dtype=float) + self.mean = self.xp.asarray(self.mean, dtype=float) self.likelihood = AnalyticalMultidimensionalCovariantGaussian( mean=self.mean, cov=self.cov ) @@ -541,7 +545,7 @@ def test_dim(self): def test_log_likelihood(self): likelihood = AnalyticalMultidimensionalCovariantGaussian( - mean=self.xp.asarray([0]), cov=self.xp.asarray([1]) + mean=self.xp.asarray([0.0]), cov=self.xp.asarray([1.0]) ) logl = likelihood.log_likelihood(dict(x0=self.xp.asarray(0.0))) self.assertEqual( @@ -560,10 +564,10 @@ def setUp(self): self.mean_1 = [10, 11, 12] self.mean_2 = [20, 21, 22] if self.xp != np: - self.cov = self.xp.asarray(self.cov) - self.sigma = self.xp.asarray(self.sigma) - self.mean_1 = self.xp.asarray(self.mean_1) - self.mean_2 = self.xp.asarray(self.mean_2) + self.cov = self.xp.asarray(self.cov, dtype=float) + self.sigma = self.xp.asarray(self.sigma, dtype=float) + self.mean_1 = self.xp.asarray(self.mean_1, dtype=float) + self.mean_2 = self.xp.asarray(self.mean_2, dtype=float) self.likelihood = AnalyticalMultidimensionalBimodalCovariantGaussian( mean_1=self.mean_1, mean_2=self.mean_2, cov=self.cov ) diff --git a/test/core/prior/analytical_test.py b/test/core/prior/analytical_test.py index ec4ec975b..09942ba07 100644 --- a/test/core/prior/analytical_test.py +++ b/test/core/prior/analytical_test.py @@ -9,6 +9,10 @@ @pytest.mark.array_backend @pytest.mark.usefixtures("xp_class") class TestDiscreteValuesPrior(unittest.TestCase): + def setUp(self): + if aac.is_torch_namespace(self.xp): + pytest.skip("DiscreteValues prior is unstable for torch backend") + def test_single_sample(self): values = [1.1, 1.2, 1.3] discrete_value_prior = bilby.core.prior.DiscreteValues(values) @@ -63,12 +67,9 @@ def test_array_probability(self): N = 3 values = [1.1, 2.2, 300.0] discrete_value_prior = bilby.core.prior.DiscreteValues(values) - self.assertTrue( - np.all( - discrete_value_prior.prob(self.xp.asarray([1.1, 2.2, 2.2, 300.0, 200.0])) - == np.array([1 / N, 1 / N, 1 / N, 1 / N, 0]) - ) - ) + probs = discrete_value_prior.prob(self.xp.asarray([1.1, 2.2, 2.2, 300.0, 200.0])) + self.assertEqual(aac.get_namespace(probs), self.xp) + np.testing.assert_array_equal(np.asarray(probs), np.array([1 / N] * 4 + [0])) def test_single_lnprobability(self): N = 3 @@ -87,12 +88,9 @@ def test_array_lnprobability(self): N = 3 values = [1.1, 2.2, 300.0] discrete_value_prior = bilby.core.prior.DiscreteValues(values) - self.assertTrue( - np.all( - discrete_value_prior.ln_prob(self.xp.asarray([1.1, 2.2, 2.2, 300, 150])) - == np.array([-np.log(N), -np.log(N), -np.log(N), -np.log(N), -np.inf]) - ) - ) + ln_probs = discrete_value_prior.ln_prob(self.xp.asarray([1.1, 2.2, 2.2, 300, 150])) + self.assertEqual(aac.get_namespace(ln_probs), self.xp) + np.testing.assert_array_equal(np.asarray(ln_probs), np.array([-np.log(N)] * 4 + [-np.inf])) @pytest.mark.array_backend diff --git a/test/core/prior/base_test.py b/test/core/prior/base_test.py index 84999d42c..469c53ece 100644 --- a/test/core/prior/base_test.py +++ b/test/core/prior/base_test.py @@ -160,6 +160,8 @@ def test_prob_integrate_to_one(self): n_samples = 1000000 samples = self.priors.sample_subset(keys=keys, size=n_samples, xp=self.xp) prob = self.priors.prob(samples, axis=0) + self.assertEqual(aac.get_namespace(prob), self.xp) + prob = np.asarray(prob) dm1 = self.priors["a"].maximum - self.priors["a"].minimum dm2 = self.priors["b"].maximum - self.priors["b"].minimum prior_volume = (dm1 * dm2) diff --git a/test/core/prior/prior_test.py b/test/core/prior/prior_test.py index c24ae1118..a3165adce 100644 --- a/test/core/prior/prior_test.py +++ b/test/core/prior/prior_test.py @@ -254,6 +254,11 @@ def condition_func(reference_params, test_param): dist=hp_3d_dist, name="testdistance", unit="unit" ), ] + if aac.is_torch_namespace(self.xp): + self.priors = [ + p for p in self.priors + if not isinstance(p, bilby.core.prior.Interped) + ] def tearDown(self): del self.priors @@ -268,29 +273,35 @@ def test_minimum_rescaling(self): # the edge of the prior is extremely suppressed for these priors # and so the rescale function doesn't quite return the lower bound continue + if isinstance(prior, bilby.gw.prior.HealPixPrior) and aac.is_torch_namespace(self.xp): + # HealPix rescaling requires interpolation + continue elif bilby.core.prior.JointPrior in prior.__class__.__mro__: minimum_sample = prior.rescale(self.xp.asarray(0)) if prior.dist.filled_rescale(): - self.assertAlmostEqual(minimum_sample[0], prior.minimum) - self.assertAlmostEqual(minimum_sample[1], prior.minimum) + self.assertAlmostEqual(np.asarray(minimum_sample[0]), prior.minimum) + self.assertAlmostEqual(np.asarray(minimum_sample[1]), prior.minimum) else: minimum_sample = prior.rescale(self.xp.asarray(0)) - self.assertAlmostEqual(minimum_sample, prior.minimum) + self.assertAlmostEqual(np.asarray(minimum_sample), prior.minimum) def test_maximum_rescaling(self): """Test the the rescaling works as expected.""" for prior in self.priors: + if isinstance(prior, bilby.gw.prior.HealPixPrior) and aac.is_torch_namespace(self.xp): + # HealPix rescaling requires interpolation + continue if bilby.core.prior.JointPrior in prior.__class__.__mro__: maximum_sample = prior.rescale(self.xp.asarray(0)) if prior.dist.filled_rescale(): - self.assertAlmostEqual(maximum_sample[0], prior.maximum) - self.assertAlmostEqual(maximum_sample[1], prior.maximum) + self.assertAlmostEqual(np.asarray(maximum_sample[0]), prior.maximum) + self.assertAlmostEqual(np.asarray(maximum_sample[1]), prior.maximum) elif isinstance(prior, bilby.gw.prior.AlignedSpin): maximum_sample = prior.rescale(self.xp.asarray(1)) - self.assertGreater(maximum_sample, 0.997) + self.assertGreater(np.asarray(maximum_sample), 0.997) else: maximum_sample = prior.rescale(self.xp.asarray(1)) - self.assertAlmostEqual(maximum_sample, prior.maximum) + self.assertAlmostEqual(np.asarray(maximum_sample), prior.maximum) def test_many_sample_rescaling(self): """Test the the rescaling works as expected.""" @@ -298,6 +309,9 @@ def test_many_sample_rescaling(self): if isinstance(prior, bilby.core.prior.analytical.SymmetricLogUniform): # SymmetricLogUniform has support down to -maximum continue + if isinstance(prior, bilby.gw.prior.HealPixPrior) and aac.is_torch_namespace(self.xp): + # HealPix rescaling requires interpolation + continue many_samples = prior.rescale(self.xp.asarray(np.random.uniform(0, 1, 1000))) if bilby.core.prior.JointPrior in prior.__class__.__mro__: if not prior.dist.filled_rescale(): @@ -383,12 +397,12 @@ def test_prob_and_ln_prob(self): # the prob and ln_prob functions, it must be ignored in this test. lnprob = prior.ln_prob(sample) prob = prior.prob(sample) - # lower precision for jax running tests with float32 - self.assertAlmostEqual( - self.xp.log(prob), lnprob, 6 - ) self.assertEqual(aac.get_namespace(lnprob), self.xp) self.assertEqual(aac.get_namespace(prob), self.xp) + # lower precision for jax running tests with float32 + lnprob = np.asarray(lnprob) + prob = np.asarray(prob) + self.assertAlmostEqual(np.log(prob), lnprob, 6) def test_many_prob_and_many_ln_prob(self): for prior in self.priors: @@ -431,6 +445,9 @@ def test_cdf_is_inverse_of_rescaling(self): def test_cdf_one_above_domain(self): for prior in self.priors: + if isinstance(prior, bilby.gw.prior.HealPixPrior) and aac.is_torch_namespace(self.xp): + # HealPix rescaling requires interpolation + continue if prior.maximum != np.inf: outside_domain = self.xp.linspace( prior.maximum + 1, prior.maximum + 1e4, 1000 @@ -442,6 +459,9 @@ def test_cdf_zero_below_domain(self): if isinstance(prior, bilby.core.prior.analytical.SymmetricLogUniform): # SymmetricLogUniform has support down to -maximum continue + if isinstance(prior, bilby.gw.prior.HealPixPrior) and aac.is_torch_namespace(self.xp): + # HealPix rescaling requires interpolation + continue if ( bilby.core.prior.JointPrior in prior.__class__.__mro__ and prior.maximum == np.inf @@ -604,7 +624,12 @@ def test_probability_in_domain(self): else: maximum = prior.maximum domain = self.xp.linspace(minimum, maximum, 1000) - self.assertTrue(all(prior.prob(domain) >= 0)) + print(prior) + prob = prior.prob(domain) + print(min(prob)) + self.assertEqual(aac.get_namespace(prob), self.xp) + prob = np.asarray(prob) + self.assertTrue(all(prob >= 0)) def test_probability_surrounding_domain(self): """Test that the prior probability is non-negative in domain of validity and zero outside.""" @@ -670,7 +695,9 @@ def test_normalized(self): domain = np.linspace(prior.minimum, prior.maximum, 10000) elif isinstance(prior, bilby.core.prior.WeightedDiscreteValues): domain = prior.values - self.assertTrue(np.sum(prior.prob(self.xp.asarray(domain))) == 1) + probs = prior.prob(self.xp.asarray(domain)) + self.assertEqual(aac.get_namespace(probs), self.xp) + self.assertTrue(np.sum(np.asarray(probs)) == 1) continue else: domain = np.linspace(prior.minimum, prior.maximum, 1000) @@ -831,6 +858,7 @@ def test_repr(self): repr_prior_string = repr_prior_string.replace( "HealPixMapPriorDist", "bilby.gw.prior.HealPixMapPriorDist" ) + prior.dist.rescale_parameters = {key: None for key in prior.dist.names} elif isinstance(prior, bilby.gw.prior.UniformComovingVolume): repr_prior_string = "bilby.gw.prior." + repr(prior) elif "Conditional" in prior.__class__.__name__: @@ -892,24 +920,6 @@ def test_set_minimum_setting(self): prior.minimum = (prior.maximum + prior.minimum) / 2 self.assertTrue(min(prior.sample(10000, xp=self.xp)) > prior.minimum) - # def test_jax_methods(self): - # import jax - - # points = jax.numpy.linspace(1e-3, 1 - 1e-3, 10) - # for prior in self.priors: - # if bilby.core.prior.JointPrior in prior.__class__.__mro__: - # continue - # scaled = prior.rescale(points) - # assert isinstance(scaled, jax.Array) - # if isinstance(prior, bilby.core.prior.DeltaFunction): - # continue - # probs = prior.prob(scaled) - # assert min(probs) > 0 - # assert max(abs(jax.numpy.log(probs) - prior.ln_prob(scaled))) < 1e-6 - # if isinstance(prior, bilby.core.prior.WeightedDiscreteValues): - # continue - # assert max(abs(prior.cdf(scaled) - points)) < 1e-6 - if __name__ == "__main__": unittest.main() diff --git a/test/core/prior/slabspike_test.py b/test/core/prior/slabspike_test.py index 1ec76ab71..7c5716b8a 100644 --- a/test/core/prior/slabspike_test.py +++ b/test/core/prior/slabspike_test.py @@ -68,10 +68,10 @@ def test_set_spike_height_domain_edge(self): class TestSlabSpikeClasses(unittest.TestCase): def setUp(self): - self.minimum = 0.4 - self.maximum = 2.4 + self.minimum = self.xp.asarray(0.4) + self.maximum = self.xp.asarray(2.4) self.spike_loc = self.xp.asarray(1.5) - self.spike_height = 0.3 + self.spike_height = self.xp.asarray(0.3) self.slabs = [ Uniform(minimum=self.minimum, maximum=self.maximum), @@ -80,8 +80,8 @@ def setUp(self): TruncatedGaussian(minimum=self.minimum, maximum=self.maximum, mu=0, sigma=1), Beta(minimum=self.minimum, maximum=self.maximum, alpha=1, beta=1), Gaussian(mu=0, sigma=1), - Cosine(), - Sine(), + Cosine(minimum=self.xp.asarray(-np.pi / 2), maximum=self.xp.asarray(np.pi / 2)), + Sine(minimum=self.xp.asarray(0), maximum=self.xp.asarray(np.pi)), HalfGaussian(sigma=1), LogNormal(mu=1, sigma=2), Exponential(mu=2), @@ -189,8 +189,11 @@ def test_rescale_no_spike(self): vals = self.xp.linspace(0, 1, 1000) expected = slab.rescale(vals) actual = slab_spike.rescale(vals) - self.assertTrue(np.allclose(expected, actual, rtol=1e-5)) self.assertEqual(aac.get_namespace(actual), self.xp) + self.assertEqual(aac.get_namespace(expected), self.xp) + actual = np.asarray(actual) + expected = np.asarray(expected) + self.assertTrue(np.allclose(expected, actual, rtol=1e-5)) def test_rescale_below_spike(self): for slab, slab_spike in zip(self.slabs, self.slab_spikes): diff --git a/test/core/series_test.py b/test/core/series_test.py index aec2ff42f..c2b8dccdb 100644 --- a/test/core/series_test.py +++ b/test/core/series_test.py @@ -1,5 +1,6 @@ import unittest +import array_api_compat as aac import numpy as np import pytest @@ -47,10 +48,10 @@ def test_start_time_from_init(self): self.assertEqual(self.start_time, self.series.start_time) def test_frequency_array_type(self): - self.assertIsInstance(self.series.frequency_array, self.xp.ndarray) + self.assertEqual(aac.get_namespace(self.series.frequency_array), self.xp) def test_time_array_type(self): - self.assertIsInstance(self.series.time_array, self.xp.ndarray) + self.assertEqual(aac.get_namespace(self.series.time_array), self.xp) def test_frequency_array_from_init(self): expected = create_frequency_series( @@ -94,10 +95,10 @@ def test_time_array_setter(self): self.series.time_array = new_time_array self.assertTrue(np.array_equal(new_time_array, self.series.time_array)) self.assertAlmostEqual( - new_sampling_frequency, self.series.sampling_frequency, places=1 + np.asarray(new_sampling_frequency), np.asarray(self.series.sampling_frequency), places=1 ) - self.assertAlmostEqual(new_duration, self.series.duration, places=1) - self.assertAlmostEqual(new_start_time, self.series.start_time, places=1) + self.assertAlmostEqual(np.asarray(new_duration), np.asarray(self.series.duration), places=1) + self.assertAlmostEqual(np.asarray(new_start_time), np.asarray(self.series.start_time), places=1) def test_time_array_without_sampling_frequency(self): self.series.sampling_frequency = None diff --git a/test/core/utils_test.py b/test/core/utils_test.py index f766f2c74..d8a78beee 100644 --- a/test/core/utils_test.py +++ b/test/core/utils_test.py @@ -72,6 +72,9 @@ def test_nfft_sine_function(self): time_domain_strain, self.sampling_frequency ) frequency_at_peak = frequencies[xp.argmax(abs(frequency_domain_strain))] + self.assertEqual(aac.get_namespace(frequency_at_peak), xp) + frequency_at_peak = np.asarray(frequency_at_peak) + injected_frequency = np.asarray(injected_frequency) self.assertAlmostEqual(injected_frequency, frequency_at_peak, places=1) def test_nfft_infft(self): @@ -344,6 +347,8 @@ def plot(): @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)) From bbb72d9cfc1c0be32671a9bf23ee7dea56887ef0 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Tue, 3 Feb 2026 08:23:56 -0500 Subject: [PATCH 093/140] FMT: run precommits --- bilby/core/grid.py | 2 +- bilby/core/prior/analytical.py | 4 +++- bilby/core/prior/dict.py | 2 +- bilby/core/prior/slabspike.py | 4 ---- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/bilby/core/grid.py b/bilby/core/grid.py index 07668aacf..4bd55bed7 100644 --- a/bilby/core/grid.py +++ b/bilby/core/grid.py @@ -217,7 +217,7 @@ def _marginalize_single(self, log_array, name, non_marg_names=None): elif aac.is_torch_namespace(xp): # https://discuss.pytorch.org/t/apply-a-function-along-an-axis/130440 out = xp.stack([ - logtrapzexp(x_i, dx=dx, xp=xp) for x_i in xp.unbind(log_array, dim=axis) + logtrapzexp(x_i, dx=dx, xp=xp) for x_i in xp.unbind(log_array, dim=axis) ], dim=min(axis, log_array.ndim - 2)) else: out = xp.apply_along_axis( diff --git a/bilby/core/prior/analytical.py b/bilby/core/prior/analytical.py index d0a9bc22e..02e65bf3a 100644 --- a/bilby/core/prior/analytical.py +++ b/bilby/core/prior/analytical.py @@ -140,7 +140,9 @@ def prob(self, val, *, xp=None): float: Prior probability of val """ if self.alpha == -1: - return xp.nan_to_num(1 / val / xp.log(xp.asarray(self.maximum / self.minimum))) * self.is_in_prior_range(val) + return xp.nan_to_num( + 1 / val / xp.log(xp.asarray(self.maximum / self.minimum)) + ) * self.is_in_prior_range(val) else: return xp.nan_to_num(val ** self.alpha * (1 + self.alpha) / (self.maximum ** (1 + self.alpha) - diff --git a/bilby/core/prior/dict.py b/bilby/core/prior/dict.py index d921cb8f4..46746073d 100644 --- a/bilby/core/prior/dict.py +++ b/bilby/core/prior/dict.py @@ -614,7 +614,7 @@ def check_ln_prob(self, sample, ln_prob, normalized=True, *, xp=None): subsample = {key: sample[key][in_bounds] for key in sample} keep = xp.log(self.evaluate_constraints(subsample, xp=xp)) constrained_ln_prob = xpx.at(constrained_ln_prob, in_bounds).set( - ln_prob[in_bounds] + keep + xp.log(ratio) + ln_prob[in_bounds] + keep + xp.log(ratio) ) return constrained_ln_prob diff --git a/bilby/core/prior/slabspike.py b/bilby/core/prior/slabspike.py index ad4f118b8..2ac310f55 100644 --- a/bilby/core/prior/slabspike.py +++ b/bilby/core/prior/slabspike.py @@ -87,10 +87,6 @@ def rescale(self, val, *, xp=None): array_like: Associated prior value with input value. """ lower_indices = val < self.inverse_cdf_below_spike - intermediate_indices = ( - (self.inverse_cdf_below_spike <= val) - * (val < (self.inverse_cdf_below_spike + self.spike_height)) - ) higher_indices = val >= (self.inverse_cdf_below_spike + self.spike_height) slab_scaled = self._contracted_rescale( From fcfabdc32fb981c90be3923c6062349c9bda2e61 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Tue, 3 Feb 2026 10:52:11 -0500 Subject: [PATCH 094/140] Make torch fully tested --- .github/workflows/unit-tests.yml | 2 +- bilby/gw/compat/__init__.py | 5 +++ bilby/gw/compat/torch.py | 19 ++++++++++ bilby/gw/conversion.py | 4 +-- bilby/gw/detector/calibration.py | 12 +++---- bilby/gw/detector/interferometer.py | 3 +- bilby/gw/detector/psd.py | 6 ++-- bilby/gw/geometry.py | 2 +- bilby/gw/likelihood/base.py | 6 ++-- bilby/gw/likelihood/multiband.py | 20 ++++++----- bilby/gw/likelihood/relative.py | 6 ++-- bilby/gw/likelihood/roq.py | 4 +-- bilby/gw/utils.py | 10 +++--- docs/array_api.rst | 18 ++++++++++ test/gw/conversion_test.py | 31 ++++++++-------- test/gw/detector/geometry_test.py | 12 +++---- test/gw/likelihood_test.py | 55 ++++++++++++++++------------- test/gw/prior_test.py | 6 ++-- test/gw/utils_test.py | 10 ++++-- test/gw/waveform_generator_test.py | 4 +-- 20 files changed, 149 insertions(+), 86 deletions(-) create mode 100644 bilby/gw/compat/torch.py diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index f52f58610..a81a89b37 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -72,7 +72,7 @@ jobs: pytest --array-backend jax --durations 10 - name: Run torch-backend unit tests run: | - pytest --array-backend torch --durations 10 test/core + pytest --array-backend torch --durations 10 - name: Run sampler tests run: | pytest test/integration/sampler_run_test.py --durations 10 -v diff --git a/bilby/gw/compat/__init__.py b/bilby/gw/compat/__init__.py index 16eea2d00..36f2566c4 100644 --- a/bilby/gw/compat/__init__.py +++ b/bilby/gw/compat/__init__.py @@ -6,5 +6,10 @@ try: from .cython import gps_time_to_utc +except ModuleNotFoundError: + pass + +try: + from .torch import n_leap_seconds except ModuleNotFoundError: pass \ No newline at end of file diff --git a/bilby/gw/compat/torch.py b/bilby/gw/compat/torch.py new file mode 100644 index 000000000..b3958f347 --- /dev/null +++ b/bilby/gw/compat/torch.py @@ -0,0 +1,19 @@ +import torch +from plum import dispatch + +from ..time import ( + LEAP_SECONDS as _LEAP_SECONDS, + n_leap_seconds as _n_leap_seconds, +) + +__all__ = ["n_leap_seconds"] + +LEAP_SECONDS = torch.tensor(_LEAP_SECONDS) + + +@dispatch +def n_leap_seconds(date: torch.Tensor) -> torch.Tensor: + """ + Find the number of leap seconds required for the specified date. + """ + return _n_leap_seconds(date, LEAP_SECONDS) diff --git a/bilby/gw/conversion.py b/bilby/gw/conversion.py index af3faa96c..96cd02dd1 100644 --- a/bilby/gw/conversion.py +++ b/bilby/gw/conversion.py @@ -645,7 +645,7 @@ def spectral_pca_to_spectral(gamma_pca_0, gamma_pca_1, gamma_pca_2, gamma_pca_3) model_space_mean = xp.asarray([0.89421, 0.33878, -0.07894, 0.00393]) model_space_standard_deviation = xp.asarray([0.35700, 0.25769, 0.05452, 0.00312]) converted_gamma_parameters = \ - model_space_mean + model_space_standard_deviation * xp.dot(transformation_matrix, sampled_pca_gammas) + model_space_mean + model_space_standard_deviation * (transformation_matrix @ sampled_pca_gammas) return converted_gamma_parameters @@ -1046,7 +1046,7 @@ def component_masses_to_symmetric_mass_ratio(mass_1, mass_2): Symmetric mass ratio of the binary """ xp = array_module(mass_1) - return xp.minimum((mass_1 * mass_2) / (mass_1 + mass_2) ** 2, 1 / 4) + return xp.minimum((mass_1 * mass_2) / (mass_1 + mass_2) ** 2, xp.asarray(0.25)) def component_masses_to_mass_ratio(mass_1, mass_2): diff --git a/bilby/gw/detector/calibration.py b/bilby/gw/detector/calibration.py index 883275016..a4cebffe6 100644 --- a/bilby/gw/detector/calibration.py +++ b/bilby/gw/detector/calibration.py @@ -48,7 +48,7 @@ from array_api_compat import is_jax_namespace from scipy.interpolate import interp1d -from ...compat.utils import array_module +from ...compat.utils import array_module, xp_wrap from ...core.utils.log import logger from ...core.prior.dict import PriorDict from ..prior import CalibrationPriorDict @@ -345,7 +345,8 @@ def _evaluate_spline(self, kind, a, b, c, d, previous_nodes): + d * spline_coefficients[next_nodes] ) - def get_calibration_factor(self, frequency_array, **params): + @xp_wrap + def get_calibration_factor(self, frequency_array, *, xp=np, **params): """Apply calibration model Parameters @@ -363,11 +364,11 @@ def get_calibration_factor(self, frequency_array, **params): calibration_factor : array-like The factor to multiply the strain by. """ - log10f_per_deltalog10f = np.nan_to_num( - np.log10(frequency_array) - self.log_spline_points[0], + log10f_per_deltalog10f = xp.nan_to_num( + xp.log10(frequency_array) - xp.asarray(self.log_spline_points[0]), neginf=0.0, ) / self.delta_log_spline_points - previous_nodes = np.clip(np.floor(log10f_per_deltalog10f).astype(int), a_min=0, a_max=self.n_points - 2) + previous_nodes = xp.clip(xp.astype(log10f_per_deltalog10f, int), min=0, max=self.n_points - 2) b = log10f_per_deltalog10f - previous_nodes a = 1 - b c = (a**3 - a) / 6 @@ -378,7 +379,6 @@ def get_calibration_factor(self, frequency_array, **params): delta_amplitude = self._evaluate_spline("amplitude", a, b, c, d, previous_nodes) delta_phase = self._evaluate_spline("phase", a, b, c, d, previous_nodes) calibration_factor = (1 + delta_amplitude) * (2 + 1j * delta_phase) / (2 - 1j * delta_phase) - xp = aac.get_namespace(calibration_factor) return xp.nan_to_num(calibration_factor) diff --git a/bilby/gw/detector/interferometer.py b/bilby/gw/detector/interferometer.py index 8bc79e399..7001b70b5 100644 --- a/bilby/gw/detector/interferometer.py +++ b/bilby/gw/detector/interferometer.py @@ -313,13 +313,14 @@ def get_detector_response(self, waveform_polarizations, parameters, frequencies= used to set the time at which the antenna response is evaluated, otherwise the provided :code:`Parameters["geocent_time"]` is used. """ + xp = array_module(waveform_polarizations) if frequencies is None: # frequencies = self.frequency_array[self.frequency_mask] frequencies = self.frequency_array mask = self.frequency_mask else: - xp = array_module(frequencies) mask = xp.ones(len(frequencies), dtype=bool) + frequencies = xp.asarray(frequencies) if self.reference_time is None: antenna_time = parameters["geocent_time"] diff --git a/bilby/gw/detector/psd.py b/bilby/gw/detector/psd.py index a3948f966..e3fe7091a 100644 --- a/bilby/gw/detector/psd.py +++ b/bilby/gw/detector/psd.py @@ -3,6 +3,7 @@ import numpy as np from scipy.interpolate import interp1d +from ...compat.utils import xp_wrap from ...core import utils from ...core.utils import logger from .strain_data import InterferometerStrainData @@ -341,7 +342,8 @@ def __import_power_spectral_density(self): """ Automagically load a power spectral density curve """ self.frequency_array, self.psd_array = np.genfromtxt(self.psd_file).T - def get_noise_realisation(self, sampling_frequency, duration): + @xp_wrap + def get_noise_realisation(self, sampling_frequency, duration, *, xp=None): """ Generate frequency Gaussian noise scaled to the power spectral density. @@ -363,4 +365,4 @@ def get_noise_realisation(self, sampling_frequency, duration): frequency_domain_strain = self.__power_spectral_density_interpolated(frequencies) ** 0.5 * white_noise out_of_bounds = (frequencies < min(self.frequency_array)) | (frequencies > max(self.frequency_array)) frequency_domain_strain[out_of_bounds] = 0 * (1 + 1j) - return frequency_domain_strain, frequencies + return xp.asarray(frequency_domain_strain), xp.asarray(frequencies) diff --git a/bilby/gw/geometry.py b/bilby/gw/geometry.py index 54d2f3a1d..68321d4b4 100644 --- a/bilby/gw/geometry.py +++ b/bilby/gw/geometry.py @@ -181,7 +181,7 @@ def time_delay_from_geocenter(detector1, ra, dec, time): def zenith_azimuth_to_theta_phi(zenith, azimuth, delta_x): """""" xp = array_module(delta_x) - omega_prime = xp.asarray( + omega_prime = xp.stack( [ xp.sin(zenith) * xp.cos(azimuth), xp.sin(zenith) * xp.sin(azimuth), diff --git a/bilby/gw/likelihood/base.py b/bilby/gw/likelihood/base.py index 4319c3581..bd0c3c48d 100644 --- a/bilby/gw/likelihood/base.py +++ b/bilby/gw/likelihood/base.py @@ -303,7 +303,7 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array elif self.time_marginalization and self.calibration_marginalization: d_inner_h_integrand = np.tile( - interferometer.frequency_domain_strain.conjugate() * signal / + interferometer.frequency_domain_strain.conj() * signal / interferometer.power_spectral_density_array, (self.number_of_response_curves, 1)).T d_inner_h_integrand[_mask] *= self.calibration_draws[interferometer.name].T @@ -323,14 +323,14 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array 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.frequency_domain_strain.conj()[0:-1] / interferometer.power_spectral_density_array[0:-1] ) elif self.calibration_marginalization and ('recalib_index' not in parameters): d_inner_h_integrand = ( normalization * - interferometer.frequency_domain_strain.conjugate() * signal + interferometer.frequency_domain_strain.conj() * signal / interferometer.power_spectral_density_array ) d_inner_h_array = np.dot(d_inner_h_integrand[_mask], self.calibration_draws[interferometer.name].T) diff --git a/bilby/gw/likelihood/multiband.py b/bilby/gw/likelihood/multiband.py index 87a61036d..6a10eb96c 100644 --- a/bilby/gw/likelihood/multiband.py +++ b/bilby/gw/likelihood/multiband.py @@ -534,7 +534,7 @@ def _setup_linear_coefficients(self): logger.info("Pre-computing linear coefficients for {}".format(ifo.name)) fddata = np.zeros(N // 2 + 1, dtype=complex) fddata[:len(ifo.frequency_domain_strain)][ifo.frequency_mask[:len(fddata)]] += \ - ifo.frequency_domain_strain[ifo.frequency_mask] / ifo.power_spectral_density_array[ifo.frequency_mask] + np.asarray(ifo.frequency_domain_strain[ifo.frequency_mask] / ifo.power_spectral_density_array[ifo.frequency_mask]) for b in range(self.number_of_bands): Ks, Ke = self.Ks_Ke[b] windows = self._get_window_sequence(1. / self.durations[b], Ks, Ke - Ks + 1, b) @@ -759,14 +759,14 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array if self.time_marginalization: parameters["geocent_time"] = original_time - d_inner_h = (strain @ self.linear_coeffs[interferometer.name]).conjugate() + d_inner_h = (strain @ self.linear_coeffs[interferometer.name]).conj() xp = array_module(strain) if self.linear_interpolation: optimal_snr_squared = xp.vdot( xp.abs(strain)**2, - self.quadratic_coeffs[interferometer.name] + xp.asarray(self.quadratic_coeffs[interferometer.name]) ) else: optimal_snr_squared = 0. @@ -777,16 +777,20 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array if b == 0: optimal_snr_squared += (4. / self.interferometers.duration) * xp.vdot( xp.abs(strain[start_idx:end_idx + 1])**2, - interferometer.frequency_mask[Ks:Ke + 1] * self.windows[start_idx:end_idx + 1] + interferometer.frequency_mask[Ks:Ke + 1] * xp.asarray(self.windows[start_idx:end_idx + 1]) / interferometer.power_spectral_density_array[Ks:Ke + 1]) else: self.wths[interferometer.name][b][Ks:Ke + 1] = ( - self.square_root_windows[start_idx:end_idx + 1] * strain[start_idx:end_idx + 1] + xp.asarray(self.square_root_windows[start_idx:end_idx + 1]) + * strain[start_idx:end_idx + 1] ) - self.hbcs[interferometer.name][b][-Mb:] = xp.fft.irfft(self.wths[interferometer.name][b]) - thbc = xp.fft.rfft(self.hbcs[interferometer.name][b]) + self.hbcs[interferometer.name][b][-Mb:] = xp.fft.irfft( + xp.asarray(self.wths[interferometer.name][b]) + ) + thbc = xp.fft.rfft(xp.asarray(self.hbcs[interferometer.name][b])) + print(self.Ibcs[interferometer.name][b]) optimal_snr_squared += (4. / self.Tbhats[b]) * xp.vdot( - xp.abs(thbc)**2, self.Ibcs[interferometer.name][b]) + xp.abs(thbc)**2, xp.asarray(self.Ibcs[interferometer.name][b].real)) complex_matched_filter_snr = d_inner_h / (optimal_snr_squared**0.5) diff --git a/bilby/gw/likelihood/relative.py b/bilby/gw/likelihood/relative.py index 80a7b0c8a..72c23c958 100644 --- a/bilby/gw/likelihood/relative.py +++ b/bilby/gw/likelihood/relative.py @@ -402,8 +402,8 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array parameters=parameters, ) a0, a1, b0, b1 = self.summary_data[interferometer.name] - d_inner_h = (a0 * r0.conjugate() + a1 * r1.conjugate()).sum() - h_inner_h = (b0 * abs(r0) ** 2 + 2 * b1 * (r0 * r1.conjugate()).real).sum() + d_inner_h = (a0 * r0.conj() + a1 * r1.conj()).sum() + h_inner_h = (b0 * abs(r0) ** 2 + 2 * b1 * (r0 * r1.conj()).real).sum() optimal_snr_squared = h_inner_h complex_matched_filter_snr = d_inner_h / (optimal_snr_squared ** 0.5) @@ -416,7 +416,7 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array ) d_inner_h_array = 4 / self.waveform_generator.duration * xp.fft.fft( full_waveform[0:-1] - * interferometer.frequency_domain_strain.conjugate()[0:-1] + * interferometer.frequency_domain_strain.conj()[0:-1] / interferometer.power_spectral_density_array[0:-1]) else: diff --git a/bilby/gw/likelihood/roq.py b/bilby/gw/likelihood/roq.py index dbe4d58db..e3c66e020 100644 --- a/bilby/gw/likelihood/roq.py +++ b/bilby/gw/likelihood/roq.py @@ -489,7 +489,7 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array indices = xp.clip(indices, 0, len(self.weights['time_samples']) - 1) d_inner_h_tc_array = xp.einsum( 'i,ji->j', - xp.conjugate(h_linear), + xp.conj(h_linear), xp.asarray( self.weights[interferometer.name + '_linear'][self.basis_number_linear] )[indices], @@ -599,7 +599,7 @@ def _calculate_d_inner_h_array(self, times, h_linear, ifo_name): # Get the nearest 5 samples of d_inner_h. Calculate only the required d_inner_h values if the time # spacing is larger than 5 times the ROQ time spacing. weights_linear = self.weights[ifo_name + '_linear'][self.basis_number_linear] - h_linear_conj = np.conjugate(h_linear) + h_linear_conj = np.conj(h_linear) if (times[1] - times[0]) / roq_time_space > 5: d_inner_h_m2 = np.dot(weights_linear[closest_idxs - 2], h_linear_conj) d_inner_h_m1 = np.dot(weights_linear[closest_idxs - 1], h_linear_conj) diff --git a/bilby/gw/utils.py b/bilby/gw/utils.py index a5fbfe59a..1a55de975 100644 --- a/bilby/gw/utils.py +++ b/bilby/gw/utils.py @@ -108,11 +108,11 @@ def inner_product(aa, bb, frequency, PSD): psd_interp = PSD.power_spectral_density_interpolated(frequency) # calculate the inner product - integrand = np.conj(aa) * bb / psd_interp + integrand = (aa.conj() * bb / psd_interp).real df = frequency[1] - frequency[0] - integral = np.sum(integrand) * df - return 4. * np.real(integral) + integral = integrand.sum() * df + return 4. * integral def noise_weighted_inner_product(aa, bb, power_spectral_density, duration): @@ -134,7 +134,7 @@ def noise_weighted_inner_product(aa, bb, power_spectral_density, duration): ======= Noise-weighted inner product. """ - integrand = aa.conjugate() * bb / power_spectral_density + integrand = aa.conj() * bb / power_spectral_density return 4 / duration * integrand.sum() @@ -223,7 +223,7 @@ def overlap(signal_a, signal_b, power_spectral_density=None, delta_frequency=Non """ low_index = int(lower_cut_off / delta_frequency) up_index = int(upper_cut_off / delta_frequency) - integrand = signal_a.conjugate() * signal_b + integrand = signal_a.conj() * signal_b integrand = integrand[low_index:up_index] / power_spectral_density[low_index:up_index] integral = (4 * delta_frequency * integrand) / norm_a / norm_b return sum(integral).real diff --git a/docs/array_api.rst b/docs/array_api.rst index 6ce77c28a..8ce1cc043 100644 --- a/docs/array_api.rst +++ b/docs/array_api.rst @@ -27,6 +27,9 @@ Bilby is currently tested with the following array backends: - **NumPy** (default): Standard CPU-based computations - **JAX**: GPU/TPU acceleration and automatic differentiation +- **PyTorch**: GPU acceleration and deep learning integration. + :code:`PyTorch` support is not complete, for example, functionality + requiring interpolation is not available. While :code:`Bilby` should be compatible with other Array API compliant libraries, these are not currently tested or officially supported. @@ -213,6 +216,12 @@ and the analysis is then performed using the JIT-compiled likelihood. However, there is currently a performance issue with the distance marginalized likelihood using the :code:`JAX` backend. +.. warning:: + + Some array backends (notably :code:`torch`) are more picky than others about data types. + For maximal consistency, try to consistently pass zero-dimensional arrays rather than :code:`Python` + scalars, e.g., :code:`torch.array(1.0)` instead of :code:`1.0`. + Performance Considerations -------------------------- @@ -314,6 +323,15 @@ The ``@xp_wrap`` decorator: - Falls back to NumPy when the input is a standard Python type or NumPy array - Handles the conversion seamlessly so users don't need to specify ``xp`` +Missing functionality +--------------------- + +The most significant missing functionality is the lack of a consistent random number generation +interface across different array backends. +Currently, all random calls use :code:`numpy.random` with the seed specified as described in :doc:`rng`. +This means that functionality like prior sampling and generating noise realizations in gravitational-wave +detectors will not be :code:`JIT`-compatible. + For Bilby Developers ===================== diff --git a/test/gw/conversion_test.py b/test/gw/conversion_test.py index 17243bfe3..9d2d46b48 100644 --- a/test/gw/conversion_test.py +++ b/test/gw/conversion_test.py @@ -85,28 +85,28 @@ def test_chirp_mass_and_primary_mass_to_mass_ratio(self): mass_ratio = conversion.chirp_mass_and_primary_mass_to_mass_ratio( self.chirp_mass, self.mass_1 ) - self.assertAlmostEqual(self.mass_ratio, mass_ratio) + self.assertAlmostEqual(float(self.mass_ratio), float(mass_ratio)) self.assertEqual(aac.get_namespace(mass_ratio), self.xp) def test_symmetric_mass_ratio_to_mass_ratio(self): mass_ratio = conversion.symmetric_mass_ratio_to_mass_ratio( self.symmetric_mass_ratio ) - self.assertAlmostEqual(self.mass_ratio, mass_ratio) + self.assertAlmostEqual(float(self.mass_ratio), float(mass_ratio)) self.assertEqual(aac.get_namespace(mass_ratio), self.xp) def test_chirp_mass_and_total_mass_to_symmetric_mass_ratio(self): symmetric_mass_ratio = conversion.chirp_mass_and_total_mass_to_symmetric_mass_ratio( self.chirp_mass, self.total_mass ) - self.assertAlmostEqual(self.symmetric_mass_ratio, symmetric_mass_ratio) + self.assertAlmostEqual(float(self.symmetric_mass_ratio), float(symmetric_mass_ratio)) self.assertEqual(aac.get_namespace(symmetric_mass_ratio), self.xp) def test_chirp_mass_and_mass_ratio_to_total_mass(self): total_mass = conversion.chirp_mass_and_mass_ratio_to_total_mass( self.chirp_mass, self.mass_ratio ) - self.assertAlmostEqual(self.total_mass, total_mass) + self.assertAlmostEqual(float(self.total_mass), float(total_mass)) self.assertEqual(aac.get_namespace(total_mass), self.xp) def test_chirp_mass_and_mass_ratio_to_component_masses(self): @@ -137,7 +137,7 @@ def test_component_masses_to_symmetric_mass_ratio(self): def test_component_masses_to_mass_ratio(self): mass_ratio = conversion.component_masses_to_mass_ratio(self.mass_1, self.mass_2) - self.assertAlmostEqual(self.mass_ratio, mass_ratio) + self.assertAlmostEqual(float(self.mass_ratio), float(mass_ratio)) self.assertEqual(aac.get_namespace(mass_ratio), self.xp) def test_mass_1_and_chirp_mass_to_mass_ratio(self): @@ -652,8 +652,8 @@ def helper_generation_from_keys(self, keys, expected_values, source=False): self.assertTrue("mass_2" in local_test_vars_with_component_masses.keys()) for key in local_test_vars_with_component_masses.keys(): self.assertAlmostEqual( - local_test_vars_with_component_masses[key], - self.expected_values[key]) + np.asarray(local_test_vars_with_component_masses[key]), + np.asarray(self.expected_values[key])) # Test the function more generally local_all_mass_parameters = \ @@ -683,7 +683,10 @@ def helper_generation_from_keys(self, keys, expected_values, source=False): ) ) for key in local_all_mass_parameters.keys(): - self.assertAlmostEqual(expected_values[key], local_all_mass_parameters[key]) + self.assertAlmostEqual( + np.asarray(expected_values[key]), + np.asarray(local_all_mass_parameters[key]), + ) self.assertEqual( aac.get_namespace(local_all_mass_parameters[key]), self.xp, @@ -906,10 +909,10 @@ def test_spectral_pca_to_spectral(self): self.spectral_pca_gamma_2[i], self.spectral_pca_gamma_3[i] ) - self.assertAlmostEqual(spectral_gamma_0, self.spectral_gamma_0[i], places=5) - self.assertAlmostEqual(spectral_gamma_1, self.spectral_gamma_1[i], places=5) - self.assertAlmostEqual(spectral_gamma_2, self.spectral_gamma_2[i], places=5) - self.assertAlmostEqual(spectral_gamma_3, self.spectral_gamma_3[i], places=5) + self.assertAlmostEqual(float(spectral_gamma_0), self.spectral_gamma_0[i], places=5) + self.assertAlmostEqual(float(spectral_gamma_1), self.spectral_gamma_1[i], places=5) + self.assertAlmostEqual(float(spectral_gamma_2), self.spectral_gamma_2[i], places=5) + self.assertAlmostEqual(float(spectral_gamma_3), self.spectral_gamma_3[i], places=5) for val in [spectral_gamma_0, spectral_gamma_1, spectral_gamma_2, spectral_gamma_3]: self.assertEqual(aac.get_namespace(val), self.xp) @@ -939,8 +942,8 @@ def test_spectral_params_to_lambda_1_lambda_2(self): self.mass_1_source_spectral[i], self.mass_2_source_spectral[i] ) - self.assertAlmostEqual(self.lambda_1_spectral[i], lambda_1, places=0) - self.assertAlmostEqual(self.lambda_2_spectral[i], lambda_2, places=0) + self.assertAlmostEqual(self.lambda_1_spectral[i], float(lambda_1), places=0) + self.assertAlmostEqual(self.lambda_2_spectral[i], float(lambda_2), places=0) self.assertAlmostEqual(self.eos_check_spectral[i], eos_check) def test_polytrope_or_causal_params_to_lambda_1_lambda_2_causal(self): diff --git a/test/gw/detector/geometry_test.py b/test/gw/detector/geometry_test.py index 231b82b17..7340a5f8d 100644 --- a/test/gw/detector/geometry_test.py +++ b/test/gw/detector/geometry_test.py @@ -154,37 +154,37 @@ def test_y_with_latitude_update(self): def test_detector_tensor_with_x_azimuth_update(self): original = self.geometry.detector_tensor self.geometry.xarm_azimuth += 1 - self.assertNotEqual(np.max(abs(self.geometry.detector_tensor - original)), 0) + self.assertNotEqual(np.max(np.asarray(abs(self.geometry.detector_tensor - original))), 0) self.assertEqual(aac.get_namespace(self.geometry.detector_tensor), self.xp) def test_detector_tensor_with_y_azimuth_update(self): original = self.geometry.detector_tensor self.geometry.yarm_azimuth += 1 - self.assertNotEqual(np.max(abs(self.geometry.detector_tensor - original)), 0) + self.assertNotEqual(np.max(np.asarray(abs(self.geometry.detector_tensor - original))), 0) self.assertEqual(aac.get_namespace(self.geometry.detector_tensor), self.xp) def test_detector_tensor_with_x_tilt_update(self): original = self.geometry.detector_tensor self.geometry.xarm_tilt += 1 - self.assertNotEqual(np.max(abs(self.geometry.detector_tensor - original)), 0) + self.assertNotEqual(np.max(np.asarray(abs(self.geometry.detector_tensor - original))), 0) self.assertEqual(aac.get_namespace(self.geometry.detector_tensor), self.xp) def test_detector_tensor_with_y_tilt_update(self): original = self.geometry.detector_tensor self.geometry.yarm_tilt += 1 - self.assertNotEqual(np.max(abs(self.geometry.detector_tensor - original)), 0) + self.assertNotEqual(np.max(np.asarray(abs(self.geometry.detector_tensor - original))), 0) self.assertEqual(aac.get_namespace(self.geometry.detector_tensor), self.xp) def test_detector_tensor_with_longitude_update(self): original = self.geometry.detector_tensor self.geometry.longitude += 1 - self.assertNotEqual(np.max(abs(self.geometry.detector_tensor - original)), 0) + self.assertNotEqual(np.max(np.asarray(abs(self.geometry.detector_tensor - original))), 0) self.assertEqual(aac.get_namespace(self.geometry.detector_tensor), self.xp) def test_detector_tensor_with_latitude_update(self): original = self.geometry.detector_tensor self.geometry.latitude += 1 - self.assertNotEqual(np.max(abs(self.geometry.detector_tensor - original)), 0) + self.assertNotEqual(np.max(np.asarray(abs(self.geometry.detector_tensor - original))), 0) self.assertEqual(aac.get_namespace(self.geometry.detector_tensor), self.xp) def test_unit_vector_along_arm_default(self): diff --git a/test/gw/likelihood_test.py b/test/gw/likelihood_test.py index 60828a714..633b0a81a 100644 --- a/test/gw/likelihood_test.py +++ b/test/gw/likelihood_test.py @@ -34,12 +34,14 @@ def convert_nested_dict(self, data): else: raise ValueError("Input must be an array API object or a dict of such objects.") - def frequency_domain_strain(self, parameters): - wf = self.wfg.frequency_domain_strain(parameters) - return self.convert_nested_dict(wf) + def _strain_from_model(self, model_data_points, model, parameters): + # we can't pass a frequency array through as a torch array + model_data_points = np.asarray(model_data_points) + return super()._strain_from_model(model_data_points, model, parameters) - def time_domain_strain(self, parameters): - wf = self.wfg.time_domain_strain(parameters) + def frequency_domain_strain(self, parameters): + self.wfg.frequency_array = np.asarray(self.wfg.frequency_array) + wf = self.wfg.__class__.frequency_domain_strain(self, parameters) return self.convert_nested_dict(wf) @@ -59,15 +61,15 @@ def setUp(self): phi_jl=0.3, luminosity_distance=4000.0, theta_jn=0.4, - psi=2.659, + psi=self.xp.asarray(2.659), phase=1.3, - geocent_time=1126259642.413, - ra=1.375, - dec=-1.2108, + geocent_time=self.xp.asarray(1126259642.413), + ra=self.xp.asarray(1.375), + dec=self.xp.asarray(-1.2108), ) self.interferometers = bilby.gw.detector.InterferometerList(["H1"]) self.interferometers.set_strain_data_from_power_spectral_densities( - sampling_frequency=2048, duration=4 + sampling_frequency=self.xp.asarray(2048.0), duration=self.xp.asarray(4.0) ) self.interferometers.set_array_backend(self.xp) base_wfg = bilby.gw.waveform_generator.GWSignalWaveformGenerator( @@ -99,7 +101,7 @@ def test_noise_log_likelihood(self): def test_log_likelihood(self): """Test log likelihood matches precomputed value""" logl = self.likelihood.log_likelihood(self.parameters) - self.assertAlmostEqual(logl, -4032.4397343470005, 3) + self.assertAlmostEqual(float(logl), -4032.4397343470005, 3) self.assertEqual(aac.get_namespace(logl), self.xp) def test_log_likelihood_ratio(self): @@ -143,11 +145,11 @@ def setUp(self): phi_jl=0.3, luminosity_distance=4000.0, theta_jn=0.4, - psi=2.659, - phase=1.3, - geocent_time=1126259642.413, - ra=1.375, - dec=-1.2108, + psi=self.xp.asarray(2.659), + phase=self.xp.asarray(1.3), + geocent_time=self.xp.asarray(1126259642.413), + ra=self.xp.asarray(1.375), + dec=self.xp.asarray(-1.2108), ) self.interferometers = bilby.gw.detector.InterferometerList(["H1"]) self.interferometers.set_strain_data_from_power_spectral_densities( @@ -183,23 +185,23 @@ def tearDown(self): def test_noise_log_likelihood(self): """Test noise log likelihood matches precomputed value""" nll = self.likelihood.noise_log_likelihood() + self.assertEqual(aac.get_namespace(nll), self.xp) self.assertAlmostEqual( - -4014.1787704539474, nll, 3 + -4014.1787704539474, float(nll), 3 ) - self.assertEqual(aac.get_namespace(nll), self.xp) def test_log_likelihood(self): """Test log likelihood matches precomputed value""" logl = self.likelihood.log_likelihood(self.parameters) - self.assertAlmostEqual(logl, -4032.4397343470005, 3) + self.assertAlmostEqual(float(logl), -4032.4397343470005, 3) self.assertEqual(aac.get_namespace(logl), self.xp) def test_log_likelihood_ratio(self): """Test log likelihood ratio returns the correct value""" llr = self.likelihood.log_likelihood_ratio(self.parameters) self.assertAlmostEqual( - self.likelihood.log_likelihood(self.parameters) - self.likelihood.noise_log_likelihood(), - llr, + float(self.likelihood.log_likelihood(self.parameters)) - float(self.likelihood.noise_log_likelihood()), + float(llr), 3, ) self.assertEqual(aac.get_namespace(llr), self.xp) @@ -1333,12 +1335,13 @@ def setUp(self): phi_jl=0.0, luminosity_distance=200.0, theta_jn=0.4, - psi=0.659, + psi=self.xp.asarray(0.659), phase=1.3, - geocent_time=1187008882, - ra=1.3, - dec=-1.2 + geocent_time=self.xp.asarray(1187008882), + ra=self.xp.asarray(1.3), + dec=self.xp.asarray(-1.2) ) # Network SNR is ~50 + # torch needs sky parameters to be tensors self.ifos = bilby.gw.detector.InterferometerList(["H1", "L1", "V1"]) bilby.core.utils.random.seed(70817) @@ -1553,6 +1556,7 @@ def test_inout_weights(self, linear_interpolation): reference_frequency=self.fmin, waveform_approximant=waveform_approximant ) ) + wfg = BackendWaveformGenerator(wfg, self.xp) self.ifos.inject_signal( parameters=self.test_parameters, waveform_generator=wfg ) @@ -1564,6 +1568,7 @@ def test_inout_weights(self, linear_interpolation): reference_frequency=self.fmin, waveform_approximant=waveform_approximant ) ) + wfg_mb = BackendWaveformGenerator(wfg_mb, self.xp) likelihood_mb = bilby.gw.likelihood.MBGravitationalWaveTransient( interferometers=self.ifos, waveform_generator=wfg_mb, reference_chirp_mass=self.test_parameters['chirp_mass'], diff --git a/test/gw/prior_test.py b/test/gw/prior_test.py index aec6f01b3..277235a62 100644 --- a/test/gw/prior_test.py +++ b/test/gw/prior_test.py @@ -566,7 +566,8 @@ def test_luminosity_distance_to_comoving_distance(self): @pytest.mark.usefixtures("xp_class") class TestAlignedSpin(unittest.TestCase): def setUp(self): - pass + if aac.is_torch_namespace(self.xp): + self.skipTest("Torch doesn't support interpolated priors.") def test_default_prior_matches_analytic(self): prior = bilby.gw.prior.AlignedSpin() @@ -591,7 +592,8 @@ def test_non_analytic_form_has_correct_statistics(self): class TestConditionalChiUniformSpinMagnitude(unittest.TestCase): def setUp(self): - pass + if aac.is_torch_namespace(self.xp): + self.skipTest("Torch doesn't support interpolated priors.") def test_marginalized_prior_is_uniform(self): priors = bilby.gw.prior.BBHPriorDict(aligned_spin=True) diff --git a/test/gw/utils_test.py b/test/gw/utils_test.py index 80b8fe60d..b4bb1af8e 100644 --- a/test/gw/utils_test.py +++ b/test/gw/utils_test.py @@ -33,15 +33,19 @@ def test_asd_from_freq_series(self): freq_data = self.xp.asarray([1, 2, 3]) df = 0.1 asd = gwutils.asd_from_freq_series(freq_data, df) - self.assertTrue(np.all(asd == freq_data * 2 * df ** 0.5)) self.assertEqual(aac.get_namespace(asd), self.xp) + asd = np.asarray(asd) + freq_data = np.asarray(freq_data) + self.assertTrue(np.all(asd == freq_data * 2 * df ** 0.5)) def test_psd_from_freq_series(self): freq_data = self.xp.asarray([1, 2, 3]) df = 0.1 psd = gwutils.psd_from_freq_series(freq_data, df) - self.assertTrue(np.all(psd == (freq_data * 2 * df ** 0.5) ** 2)) self.assertEqual(aac.get_namespace(psd), self.xp) + psd = np.asarray(psd) + freq_data = np.asarray(freq_data) + self.assertTrue(np.all(psd == (freq_data * 2 * df ** 0.5) ** 2)) def test_inner_product(self): aa = self.xp.asarray([1, 2, 3]) @@ -99,8 +103,8 @@ def test_overlap(self): norm_a=gwutils.optimal_snr_squared(signal, psd, duration), norm_b=gwutils.optimal_snr_squared(frequency_domain_strain, psd, duration), ) - self.assertAlmostEqual(overlap, 2.76914407e-05) self.assertEqual(aac.get_namespace(overlap), self.xp) + self.assertAlmostEqual(float(overlap), 2.76914407e-05) @pytest.mark.skip(reason="GWOSC unstable: avoiding this test") def test_get_event_time(self): diff --git a/test/gw/waveform_generator_test.py b/test/gw/waveform_generator_test.py index 2aff1c0ff..df74a067f 100644 --- a/test/gw/waveform_generator_test.py +++ b/test/gw/waveform_generator_test.py @@ -139,10 +139,10 @@ def test_source_model(self): ) def test_frequency_array_type(self): - self.assertIsInstance(self.waveform_generator.frequency_array, self.xp.ndarray) + self.assertEqual(aac.array_namespace(self.waveform_generator.frequency_array), self.xp) def test_time_array_type(self): - self.assertIsInstance(self.waveform_generator.time_array, self.xp.ndarray) + self.assertEqual(aac.array_namespace(self.waveform_generator.time_array), self.xp) def test_source_model_parameters(self): formatted_parameters = self.waveform_generator._format_parameters( From 9b3b5b8609de1d0aa58d1e66f65416cb4850ebd3 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Tue, 3 Feb 2026 10:56:47 -0500 Subject: [PATCH 095/140] FMT: pre-commit fix --- bilby/gw/likelihood/multiband.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bilby/gw/likelihood/multiband.py b/bilby/gw/likelihood/multiband.py index 6a10eb96c..0c54bc092 100644 --- a/bilby/gw/likelihood/multiband.py +++ b/bilby/gw/likelihood/multiband.py @@ -533,8 +533,10 @@ def _setup_linear_coefficients(self): for ifo in self.interferometers: logger.info("Pre-computing linear coefficients for {}".format(ifo.name)) fddata = np.zeros(N // 2 + 1, dtype=complex) - fddata[:len(ifo.frequency_domain_strain)][ifo.frequency_mask[:len(fddata)]] += \ - np.asarray(ifo.frequency_domain_strain[ifo.frequency_mask] / ifo.power_spectral_density_array[ifo.frequency_mask]) + fddata[:len(ifo.frequency_domain_strain)][ifo.frequency_mask[:len(fddata)]] += np.asarray( + ifo.frequency_domain_strain[ifo.frequency_mask] / ifo.power_spectral_density_array[ifo.frequency_mask] + ) + for b in range(self.number_of_bands): Ks, Ke = self.Ks_Ke[b] windows = self._get_window_sequence(1. / self.durations[b], Ks, Ke - Ks + 1, b) From 12ba0b5da4ef6c65def7c4104aaf1f01f4f5d33a Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Tue, 3 Feb 2026 11:37:39 -0500 Subject: [PATCH 096/140] TEST: fix torch roq tests --- bilby/gw/likelihood/multiband.py | 2 +- bilby/gw/likelihood/roq.py | 6 +++--- test/gw/likelihood_test.py | 16 ++++++++-------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/bilby/gw/likelihood/multiband.py b/bilby/gw/likelihood/multiband.py index 0c54bc092..f3f99ebcf 100644 --- a/bilby/gw/likelihood/multiband.py +++ b/bilby/gw/likelihood/multiband.py @@ -536,7 +536,7 @@ def _setup_linear_coefficients(self): fddata[:len(ifo.frequency_domain_strain)][ifo.frequency_mask[:len(fddata)]] += np.asarray( ifo.frequency_domain_strain[ifo.frequency_mask] / ifo.power_spectral_density_array[ifo.frequency_mask] ) - + for b in range(self.number_of_bands): Ks, Ke = self.Ks_Ke[b] windows = self._get_window_sequence(1. / self.durations[b], Ks, Ke - Ks + 1, b) diff --git a/bilby/gw/likelihood/roq.py b/bilby/gw/likelihood/roq.py index e3c66e020..2b2d36d4c 100644 --- a/bilby/gw/likelihood/roq.py +++ b/bilby/gw/likelihood/roq.py @@ -659,17 +659,17 @@ def perform_roq_params_check(self, ifo=None): except ValueError: roq_minimum_component_mass = None - if ifo.maximum_frequency > roq_maximum_frequency: + if float(ifo.maximum_frequency) > roq_maximum_frequency: raise BilbyROQParamsRangeError( "Requested maximum frequency {} larger than ROQ basis fhigh {}" .format(ifo.maximum_frequency, roq_maximum_frequency) ) - if ifo.minimum_frequency < roq_minimum_frequency: + if float(ifo.minimum_frequency) < roq_minimum_frequency: raise BilbyROQParamsRangeError( "Requested minimum frequency {} lower than ROQ basis flow {}" .format(ifo.minimum_frequency, roq_minimum_frequency) ) - if ifo.strain_data.duration != roq_segment_length: + if float(ifo.strain_data.duration) != roq_segment_length: raise BilbyROQParamsRangeError( "Requested duration differs from ROQ basis seglen") diff --git a/test/gw/likelihood_test.py b/test/gw/likelihood_test.py index 633b0a81a..e62e2d806 100644 --- a/test/gw/likelihood_test.py +++ b/test/gw/likelihood_test.py @@ -340,11 +340,11 @@ def setUp(self): phi_jl=0.3, luminosity_distance=1000.0, theta_jn=0.4, - psi=0.659, + psi=self.xp.asarray(0.659), phase=1.3, - geocent_time=1.2, - ra=1.3, - dec=-1.2, + geocent_time=self.xp.asarray(1.2), + ra=self.xp.asarray(1.3), + dec=self.xp.asarray(-1.2), ) ifos = bilby.gw.detector.InterferometerList(["H1"]) @@ -705,11 +705,11 @@ def setUp(self): chi_2=0.0, luminosity_distance=100.0, theta_jn=0.4, - psi=0.659, + psi=self.xp.asarray(0.659), phase=1.3, - geocent_time=1.2, - ra=1.3, - dec=-1.2 + geocent_time=self.xp.asarray(1.2), + ra=self.xp.asarray(1.3), + dec=self.xp.asarray(-1.2) ) self.priors = bilby.gw.prior.BBHPriorDict() self.priors.pop("mass_1") From 07a5ebeacca73502065404ac15437fb9f2db8ffa Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Tue, 3 Feb 2026 11:47:07 -0500 Subject: [PATCH 097/140] CI: prioritize torch tests --- .github/workflows/unit-tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index a81a89b37..a47e3602a 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -63,6 +63,9 @@ jobs: # - name: Run precommits # run: | # pre-commit run --all-files --verbose --show-diff-on-failure + - name: Run torch-backend unit tests + run: | + pytest --array-backend torch --durations 10 - name: Run unit tests run: | python -m pytest --cov=bilby --cov-branch --durations 10 -ra --color yes --cov-report=xml --junitxml=pytest.xml @@ -70,9 +73,6 @@ jobs: run: | python -m pip install .[jax] pytest --array-backend jax --durations 10 - - name: Run torch-backend unit tests - run: | - pytest --array-backend torch --durations 10 - name: Run sampler tests run: | pytest test/integration/sampler_run_test.py --durations 10 -v From 42d8b07261e1f5f2db1c7ffe22e5e1cbcf036739 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Tue, 3 Feb 2026 11:56:22 -0500 Subject: [PATCH 098/140] TEST: another attempt to fix torch tests --- .github/workflows/unit-tests.yml | 4 ++-- bilby/gw/likelihood/roq.py | 2 +- test/conftest.py | 2 -- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index a47e3602a..a72c32223 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -65,14 +65,14 @@ jobs: # pre-commit run --all-files --verbose --show-diff-on-failure - name: Run torch-backend unit tests run: | - pytest --array-backend torch --durations 10 + SCIPY_ARRAY_API=1 pytest --array-backend torch --durations 10 - name: Run unit tests run: | python -m pytest --cov=bilby --cov-branch --durations 10 -ra --color yes --cov-report=xml --junitxml=pytest.xml - name: Run jax-backend unit tests run: | python -m pip install .[jax] - pytest --array-backend jax --durations 10 + SCIPY_ARRAY_API=1 pytest --array-backend jax --durations 10 - name: Run sampler tests run: | pytest test/integration/sampler_run_test.py --durations 10 -v diff --git a/bilby/gw/likelihood/roq.py b/bilby/gw/likelihood/roq.py index 2b2d36d4c..aefcfed89 100644 --- a/bilby/gw/likelihood/roq.py +++ b/bilby/gw/likelihood/roq.py @@ -734,7 +734,7 @@ def _set_weights(self, linear_matrix, quadratic_matrix): - self.interferometers.start_time ) / time_space)) ) - self.weights['time_samples'] = np.arange(start_idx, end_idx + 1) * time_space + self.weights['time_samples'] = np.arange(start_idx, end_idx + 1) * float(time_space) logger.info("Using {} ROQ time samples".format(len(self.weights['time_samples']))) # select bases to be used, set prior ranges and frequency nodes if exist diff --git a/test/conftest.py b/test/conftest.py index a9be96548..83e7a89a6 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -42,10 +42,8 @@ def _xp(request): case None | "numpy": import numpy as xp case "jax" | "jax.numpy": - import os import jax - os.environ["SCIPY_ARRAY_API"] = "1" jax.config.update("jax_enable_x64", True) xp = jax.numpy case "torch": From c669bfab0b179e420302073d7e5bf57a2483c574 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Tue, 3 Feb 2026 12:13:20 -0500 Subject: [PATCH 099/140] Another attempt at fixing torch ROQ tests --- bilby/gw/likelihood/roq.py | 4 ++-- test/gw/likelihood_test.py | 12 ++++++------ test/gw/utils_test.py | 6 ++++-- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/bilby/gw/likelihood/roq.py b/bilby/gw/likelihood/roq.py index aefcfed89..2ce7d411b 100644 --- a/bilby/gw/likelihood/roq.py +++ b/bilby/gw/likelihood/roq.py @@ -871,7 +871,7 @@ def _set_weights_linear(self, linear_matrix, basis_idxs, roq_idxs, ifo_idxs): basis_element = linear_matrix_single[i][roq_idxs[ifo.name]] ifft_input[nonzero_idxs[ifo.name]] = data_over_psd[ifo.name] * np.conj(basis_element) linear_weights[:, i] = ifft(ifft_input)[start_idx:end_idx + 1] - linear_weights *= 4. * number_of_time_samples / self.interferometers.duration + linear_weights *= 4. * number_of_time_samples / float(self.interferometers.duration) self.weights[ifo.name + '_linear'].append(linear_weights) if pyfftw is not None: pyfftw.forget_wisdom() @@ -911,7 +911,7 @@ def _set_weights_linear_multiband(self, linear_matrix, basis_idxs): )[start_frequency_bin:end_frequency_bin + 1] start_idx_of_next_band = start_idx_of_band + end_frequency_bin - start_frequency_bin + 1 tc_shifted_data[ifo.name][start_idx_of_band:start_idx_of_next_band] = 4. / Tb * Db[:, None] * np.exp( - 2. * np.pi * 1j * fs[:, None] * (self.weights['time_samples'][None, :] - ifo.duration + Tb)) + 2. * np.pi * 1j * fs[:, None] * (self.weights['time_samples'][None, :] - float(ifo.duration) + Tb)) start_idx_of_band = start_idx_of_next_band # compute inner products for basis_idx in basis_idxs: diff --git a/test/gw/likelihood_test.py b/test/gw/likelihood_test.py index e62e2d806..accbadb83 100644 --- a/test/gw/likelihood_test.py +++ b/test/gw/likelihood_test.py @@ -743,11 +743,11 @@ def test_fails_with_frequency_duration_mismatch( ) interferometers.set_array_backend(self.xp) for ifo in interferometers: - ifo.minimum_frequency = minimum_frequency - ifo.maximum_frequency = maximum_frequency + ifo.minimum_frequency = self.xp.asarray(minimum_frequency) + ifo.maximum_frequency = self.xp.asarray(maximum_frequency) search_waveform_generator = bilby.gw.waveform_generator.WaveformGenerator( - duration=duration, - sampling_frequency=2 * maximum_frequency, + duration=self.xp.asarray(duration), + sampling_frequency=self.xp.asarray(2 * maximum_frequency), frequency_domain_source_model=bilby.gw.source.binary_black_hole_roq, waveform_arguments=dict( reference_frequency=self.reference_frequency, @@ -919,9 +919,9 @@ def assertLess_likelihood_errors( if minimum_frequency is None: ifo.minimum_frequency = self.minimum_frequency else: - ifo.minimum_frequency = minimum_frequency + ifo.minimum_frequency = self.xp.asarray(minimum_frequency) if maximum_frequency is not None: - ifo.maximum_frequency = maximum_frequency + ifo.maximum_frequency = self.xp.asarray(maximum_frequency) interferometers.set_strain_data_from_zero_noise( sampling_frequency=self.sampling_frequency, duration=self.duration, diff --git a/test/gw/utils_test.py b/test/gw/utils_test.py index b4bb1af8e..2fc700993 100644 --- a/test/gw/utils_test.py +++ b/test/gw/utils_test.py @@ -335,10 +335,12 @@ def test_conversion_gives_correct_prior(self) -> None: ras, decs = bilby.gw.utils.zenith_azimuth_to_ra_dec( zeniths, azimuths, times, self.ifos ) - self.assertGreaterEqual(ks_2samp(self.samples["ra"], ras).pvalue, 0.01) - self.assertGreaterEqual(ks_2samp(self.samples["dec"], decs).pvalue, 0.01) self.assertEqual(aac.get_namespace(ras), self.xp) self.assertEqual(aac.get_namespace(decs), self.xp) + ras = np.asarray(ras) + decs = np.asarray(decs) + self.assertGreaterEqual(ks_2samp(self.samples["ra"], ras).pvalue, 0.01) + self.assertGreaterEqual(ks_2samp(self.samples["dec"], decs).pvalue, 0.01) @pytest.mark.array_backend From 70f029d7642708e2c0b64df4d77f3fa68394ffb3 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Tue, 3 Feb 2026 12:40:05 -0500 Subject: [PATCH 100/140] Fix arrays of data setting --- bilby/gw/detector/interferometer.py | 28 +++++++++---- bilby/gw/detector/strain_data.py | 63 +++++++++++++++++++++-------- bilby/gw/likelihood/roq.py | 2 +- test/gw/likelihood_test.py | 2 +- 4 files changed, 69 insertions(+), 26 deletions(-) diff --git a/bilby/gw/detector/interferometer.py b/bilby/gw/detector/interferometer.py index 7001b70b5..cfe145fbe 100644 --- a/bilby/gw/detector/interferometer.py +++ b/bilby/gw/detector/interferometer.py @@ -115,16 +115,19 @@ def __repr__(self): float(self.geometry.yarm_azimuth), float(self.geometry.xarm_tilt), float(self.geometry.yarm_tilt)) - def set_strain_data_from_gwpy_timeseries(self, time_series): + def set_strain_data_from_gwpy_timeseries(self, time_series, *, xp=None): """ Set the `Interferometer.strain_data` from a gwpy TimeSeries Parameters ========== time_series: gwpy.timeseries.timeseries.TimeSeries The data to set. + xp: array module, optional + The array module to use, e.g., :code:`numpy` or :code:`jax.numpy`. + If not specified :code:`numpy` will be used. """ - self.strain_data.set_from_gwpy_timeseries(time_series=time_series) + self.strain_data.set_from_gwpy_timeseries(time_series=time_series, xp=xp) def set_strain_data_from_frequency_domain_strain( self, frequency_domain_strain, sampling_frequency=None, @@ -175,7 +178,7 @@ def set_strain_data_from_power_spectral_density( def set_strain_data_from_frame_file( self, frame_file, sampling_frequency, duration, start_time=0, - channel=None, buffer_time=1): + channel=None, buffer_time=1, *, xp=None): """ Set the `Interferometer.strain_data` from a frame file Parameters @@ -193,15 +196,18 @@ def set_strain_data_from_frame_file( buffer_time: float Read in data with `start_time-buffer_time` and `start_time+duration+buffer_time` + xp: array module, optional + The array module to use, e.g., :code:`numpy` or :code:`jax.numpy`. + If not specified :code:`numpy` will be used. """ self.strain_data.set_from_frame_file( frame_file=frame_file, sampling_frequency=sampling_frequency, duration=duration, start_time=start_time, - channel=channel, buffer_time=buffer_time) + channel=channel, buffer_time=buffer_time, xp=xp) def set_strain_data_from_channel_name( - self, channel, sampling_frequency, duration, start_time=0): + self, channel, sampling_frequency, duration, start_time=0, *, xp=None): """ Set the `Interferometer.strain_data` by fetching from given channel using strain_data.set_from_channel_name() @@ -216,22 +222,28 @@ def set_strain_data_from_channel_name( The data duration (in s) start_time: float The GPS start-time of the data + xp: array module, optional + The array module to use, e.g., :code:`numpy` or :code:`jax.numpy`. + If not specified :code:`numpy` will be used. """ self.strain_data.set_from_channel_name( channel=channel, sampling_frequency=sampling_frequency, - duration=duration, start_time=start_time) + duration=duration, start_time=start_time, xp=xp) - def set_strain_data_from_csv(self, filename): + def set_strain_data_from_csv(self, filename, *, xp=None): """ Set the `Interferometer.strain_data` from a csv file Parameters ========== filename: str The path to the file to read in + xp: array module, optional + The array module to use, e.g., :code:`numpy` or :code:`jax.numpy`. + If not specified :code:`numpy` will be used. """ - self.strain_data.set_from_csv(filename) + self.strain_data.set_from_csv(filename, xp=xp) def set_strain_data_from_zero_noise( self, sampling_frequency, duration, start_time=0): diff --git a/bilby/gw/detector/strain_data.py b/bilby/gw/detector/strain_data.py index bca7acced..017d2ea50 100644 --- a/bilby/gw/detector/strain_data.py +++ b/bilby/gw/detector/strain_data.py @@ -1,5 +1,7 @@ +import array_api_compat as aac import numpy as np +from ...compat.utils import array_module from ...core import utils from ...core.series import CoupledTimeAndFrequencySeries from ...core.utils import logger, PropertyAccessor @@ -498,7 +500,7 @@ def set_from_time_domain_strain( else: raise ValueError("Data times do not match time array") - def set_from_gwpy_timeseries(self, time_series): + def set_from_gwpy_timeseries(self, time_series, *, xp=np): """ Set the strain data from a gwpy TimeSeries This sets the time_domain_strain attribute, the frequency_domain_strain @@ -509,17 +511,23 @@ def set_from_gwpy_timeseries(self, time_series): ========== time_series: gwpy.timeseries.timeseries.TimeSeries The data to use + xp: array module, optional + The array module to use, e.g., :code:`numpy` or :code:`jax.numpy`. + If not specified :code:`numpy` will be used. """ from gwpy.timeseries import TimeSeries logger.debug('Setting data using provided gwpy TimeSeries object') if not isinstance(time_series, TimeSeries): raise ValueError("Input time_series is not a gwpy TimeSeries") + duration = xp.asarray(time_series.duration.value) + sampling_frequency = xp.asarray(time_series.sample_rate.value) + start_time = xp.asarray(time_series.epoch.value) self._times_and_frequencies = \ - CoupledTimeAndFrequencySeries(duration=time_series.duration.value, - sampling_frequency=time_series.sample_rate.value, - start_time=time_series.epoch.value) - self._time_domain_strain = time_series.value + CoupledTimeAndFrequencySeries(duration=duration, + sampling_frequency=sampling_frequency, + start_time=start_time) + self._time_domain_strain = xp.asarray(time_series.value) self._frequency_domain_strain = None self._channel = time_series.channel @@ -529,7 +537,7 @@ def channel(self): def set_from_open_data( self, name, start_time, duration=4, outdir='outdir', cache=True, - **kwargs): + *, xp=None, **kwargs): """ Set the strain data from open LOSC data This sets the time_domain_strain attribute, the frequency_domain_strain @@ -548,30 +556,38 @@ def set_from_open_data( Directory where the psd files are saved cache: bool, optional Whether or not to store/use the acquired data. + xp: array module, optional + The array module to use, e.g., :code:`numpy` or :code:`jax.numpy`. + If not specified :code:`numpy` will be used. **kwargs: All keyword arguments are passed to `gwpy.timeseries.TimeSeries.fetch_open_data()`. """ - timeseries = gwutils.get_open_strain_data( - name, start_time, start_time + duration, outdir=outdir, cache=cache, + name, float(start_time), float(start_time + duration), outdir=outdir, cache=cache, **kwargs) - self.set_from_gwpy_timeseries(timeseries) + if xp is None: + xp = array_module((duration, start_time)) + + self.set_from_gwpy_timeseries(timeseries, xp=xp) - def set_from_csv(self, filename): + def set_from_csv(self, filename, xp=None): """ Set the strain data from a csv file Parameters ========== filename: str The path to the file to read in + xp: array module, optional + The array module to use, e.g., :code:`numpy` or :code:`jax.numpy`. + If not specified :code:`numpy` will be used. """ from gwpy.timeseries import TimeSeries timeseries = TimeSeries.read(filename, format='csv') - self.set_from_gwpy_timeseries(timeseries) + self.set_from_gwpy_timeseries(timeseries, xp=xp) def set_from_frequency_domain_strain( self, frequency_domain_strain, sampling_frequency=None, @@ -661,12 +677,13 @@ def set_from_zero_noise(self, sampling_frequency, duration, start_time=0): sampling_frequency=sampling_frequency, start_time=start_time) logger.debug('Setting zero noise data') - self._frequency_domain_strain = np.zeros_like(self.frequency_array, + xp = aac.get_namespace(self.frequency_array) + self._frequency_domain_strain = xp.zeros_like(self.frequency_array, dtype=complex) def set_from_frame_file( self, frame_file, sampling_frequency, duration, start_time=0, - channel=None, buffer_time=1): + channel=None, buffer_time=1, *, xp=None): """ Set the `frequency_domain_strain` from a frame fiile Parameters @@ -684,6 +701,10 @@ def set_from_frame_file( buffer_time: float Read in data with `start_time-buffer_time` and `start_time+duration+buffer_time` + xp: array module, optional + The array module to use, e.g., :code:`numpy` or :code:`jax.numpy`. + If not specified, it will be inferred from the provided duration/ + sampling frequency. """ @@ -697,9 +718,12 @@ def set_from_frame_file( buffer_time=buffer_time, channel=channel, resample=sampling_frequency) - self.set_from_gwpy_timeseries(strain) + if xp is None: + xp = aac.get_namespace(self.frequency_array) - def set_from_channel_name(self, channel, duration, start_time, sampling_frequency): + self.set_from_gwpy_timeseries(strain, xp=xp) + + def set_from_channel_name(self, channel, duration, start_time, sampling_frequency, *, xp=None): """ Set the `frequency_domain_strain` by fetching from given channel using gwpy.TimesSeries.get(), which dynamically accesses either frames on disk, or a remote NDS2 server to find and return data. This function @@ -715,6 +739,10 @@ def set_from_channel_name(self, channel, duration, start_time, sampling_frequenc The GPS start-time of the data sampling_frequency: float The sampling frequency (in Hz) + xp: array module, optional + The array module to use, e.g., :code:`numpy` or :code:`jax.numpy`. + If not specified, it will be inferred from the provided duration/ + sampling frequency. """ from gwpy.timeseries import TimeSeries @@ -730,7 +758,10 @@ def set_from_channel_name(self, channel, duration, start_time, sampling_frequenc strain = TimeSeries.get(channel, start_time, start_time + duration) strain = strain.resample(sampling_frequency) - self.set_from_gwpy_timeseries(strain) + if xp is None: + xp = aac.get_namespace(self.frequency_array) + + self.set_from_gwpy_timeseries(strain, xp=xp) class Notch(object): diff --git a/bilby/gw/likelihood/roq.py b/bilby/gw/likelihood/roq.py index 2ce7d411b..a48534a67 100644 --- a/bilby/gw/likelihood/roq.py +++ b/bilby/gw/likelihood/roq.py @@ -544,7 +544,7 @@ def _closest_time_indices(time, samples): closest = xp.floor((time - samples[0]) / (samples[1] - samples[0])) indices = [closest + ii for ii in [-2, -1, 0, 1, 2]] in_bounds = (indices[0] >= 0) & (indices[-1] < samples.size) - return xp.asarray(indices).astype(int), in_bounds + return xp.astype(indices, int), in_bounds @staticmethod def _interp_five_samples(time_samples, values, time): diff --git a/test/gw/likelihood_test.py b/test/gw/likelihood_test.py index accbadb83..1e9389354 100644 --- a/test/gw/likelihood_test.py +++ b/test/gw/likelihood_test.py @@ -94,7 +94,7 @@ def test_noise_log_likelihood(self): """Test noise log likelihood matches precomputed value""" nll = self.likelihood.noise_log_likelihood() self.assertAlmostEqual( - -4014.1787704539474, nll, 3 + -4014.1787704539474, float(nll), 3 ) self.assertEqual(aac.get_namespace(nll), self.xp) From 01841201c9c33a004269fd5a8dc37c16c89918d9 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Tue, 3 Feb 2026 12:58:03 -0500 Subject: [PATCH 101/140] BUG: fix some more roq array issues --- bilby/gw/detector/interferometer.py | 2 +- bilby/gw/likelihood/roq.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bilby/gw/detector/interferometer.py b/bilby/gw/detector/interferometer.py index cfe145fbe..3a0367efe 100644 --- a/bilby/gw/detector/interferometer.py +++ b/bilby/gw/detector/interferometer.py @@ -363,7 +363,7 @@ def get_detector_response(self, waveform_polarizations, parameters, frequencies= signal_ifo = signal_ifo * xp.exp(-1j * 2 * np.pi * dt * frequencies) signal_ifo *= self.calibration_model.get_calibration_factor( - frequencies, prefix='recalib_{}_'.format(self.name), **parameters + frequencies, prefix=f'recalib_{self.name}_', xp=xp, **parameters ) return signal_ifo diff --git a/bilby/gw/likelihood/roq.py b/bilby/gw/likelihood/roq.py index a48534a67..71d071c94 100644 --- a/bilby/gw/likelihood/roq.py +++ b/bilby/gw/likelihood/roq.py @@ -470,13 +470,13 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array xp = array_module(h_linear) calib_factor = interferometer.calibration_model.get_calibration_factor( - frequency_nodes, prefix='recalib_{}_'.format(interferometer.name), **parameters) + xp.asarray(frequency_nodes), prefix=f'recalib_{interferometer.name}_', xp=xp, **parameters) h_linear *= calib_factor[linear_indices] h_quadratic *= calib_factor[quadratic_indices] optimal_snr_squared = xp.vdot( xp.abs(h_quadratic)**2, - self.weights[interferometer.name + '_quadratic'][self.basis_number_quadratic] + xp.asarray(self.weights[interferometer.name + '_quadratic'][self.basis_number_quadratic]) ) dt = interferometer.time_delay_from_geocenter( @@ -485,7 +485,7 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array ifo_time = dt_geocent + dt indices, in_bounds = self._closest_time_indices( - ifo_time, self.weights['time_samples']) + ifo_time, xp.asarray(self.weights['time_samples'])) indices = xp.clip(indices, 0, len(self.weights['time_samples']) - 1) d_inner_h_tc_array = xp.einsum( 'i,ji->j', From b2cf8aac9b1ef5bf40f90f8cb6205bb304b907bb Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Tue, 3 Feb 2026 14:37:39 -0500 Subject: [PATCH 102/140] Make ROQ calculations use correct array backend --- bilby/gw/likelihood/roq.py | 165 ++++++++++++++++++++--------------- test/gw/likelihood_test.py | 170 +++++++++++++++---------------------- 2 files changed, 165 insertions(+), 170 deletions(-) diff --git a/bilby/gw/likelihood/roq.py b/bilby/gw/likelihood/roq.py index 71d071c94..11e124090 100644 --- a/bilby/gw/likelihood/roq.py +++ b/bilby/gw/likelihood/roq.py @@ -1,5 +1,5 @@ - import array_api_compat as aac +import array_api_extra as xpx import numpy as np from .base import GravitationalWaveTransient @@ -272,15 +272,16 @@ def _set_unique_frequency_nodes_and_inverse(self): """Set unique frequency nodes and indices to recover linear and quadratic frequency nodes for each combination of linear and quadratic bases """ + xp = aac.array_namespace(self.interferometers.frequency_array) self._unique_frequency_nodes_and_inverse = [] for idx_linear in range(self.number_of_bases_linear): tmp = [] - frequency_nodes_linear = self.weights['frequency_nodes_linear'][idx_linear] + frequency_nodes_linear = xp.asarray(self.weights['frequency_nodes_linear'][idx_linear]) size_linear = len(frequency_nodes_linear) for idx_quadratic in range(self.number_of_bases_quadratic): - frequency_nodes_quadratic = self.weights['frequency_nodes_quadratic'][idx_quadratic] - frequency_nodes_unique, original_indices = np.unique( - np.hstack((frequency_nodes_linear, frequency_nodes_quadratic)), + frequency_nodes_quadratic = xp.asarray(self.weights['frequency_nodes_quadratic'][idx_quadratic]) + frequency_nodes_unique, original_indices = xp.unique( + xp.hstack((frequency_nodes_linear, frequency_nodes_quadratic)), return_inverse=True ) linear_indices = original_indices[:size_linear] @@ -486,7 +487,7 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array indices, in_bounds = self._closest_time_indices( ifo_time, xp.asarray(self.weights['time_samples'])) - indices = xp.clip(indices, 0, len(self.weights['time_samples']) - 1) + indices = xp.clip(xp.asarray(indices), 0, len(self.weights['time_samples']) - 1) d_inner_h_tc_array = xp.einsum( 'i,ji->j', xp.conj(h_linear), @@ -541,10 +542,10 @@ def _closest_time_indices(time, samples): Whether the indices are for valid times """ xp = array_module(time) - closest = xp.floor((time - samples[0]) / (samples[1] - samples[0])) + closest = xp.astype(xp.floor((time - samples[0]) / (samples[1] - samples[0])), int) indices = [closest + ii for ii in [-2, -1, 0, 1, 2]] - in_bounds = (indices[0] >= 0) & (indices[-1] < samples.size) - return xp.astype(indices, int), in_bounds + in_bounds = (indices[0] >= 0) & (indices[-1] < len(samples)) + return indices, in_bounds @staticmethod def _interp_five_samples(time_samples, values, time): @@ -566,16 +567,15 @@ def _interp_five_samples(time_samples, values, time): value: float The value of the function at the input time """ - xp = aac.get_namespace(time_samples) r1 = (-values[0] + 8. * values[1] - 14. * values[2] + 8. * values[3] - values[4]) / 4. r2 = values[2] - 2. * values[3] + values[4] - a = (time_samples[3] - time) / xp.maximum(time_samples[1] - time_samples[0], 1e-12) + a = (time_samples[3] - time) / max(time_samples[1] - time_samples[0], 1e-12) b = 1. - a c = (a**3. - a) / 6. d = (b**3. - b) / 6. return a * values[2] + b * values[3] + c * r1 + d * r2 - def _calculate_d_inner_h_array(self, times, h_linear, ifo_name): + def _calculate_d_inner_h_array(self, times, h_linear, ifo_name, *, xp=None): """ Calculate d_inner_h at regularly-spaced time samples. Each value is interpolated from the nearest 5 samples with the algorithm explained in @@ -593,21 +593,23 @@ def _calculate_d_inner_h_array(self, times, h_linear, ifo_name): ======= d_inner_h_array: array-like """ + if xp is None: + xp = aac.array_namespace(h_linear) roq_time_space = self.weights['time_samples'][1] - self.weights['time_samples'][0] times_per_roq_time_space = (times - self.weights['time_samples'][0]) / roq_time_space - closest_idxs = np.floor(times_per_roq_time_space).astype(int) + closest_idxs = xp.astype(xp.floor(times_per_roq_time_space), int) # Get the nearest 5 samples of d_inner_h. Calculate only the required d_inner_h values if the time # spacing is larger than 5 times the ROQ time spacing. weights_linear = self.weights[ifo_name + '_linear'][self.basis_number_linear] - h_linear_conj = np.conj(h_linear) + h_linear_conj = h_linear.conj() if (times[1] - times[0]) / roq_time_space > 5: - d_inner_h_m2 = np.dot(weights_linear[closest_idxs - 2], h_linear_conj) - d_inner_h_m1 = np.dot(weights_linear[closest_idxs - 1], h_linear_conj) - d_inner_h_0 = np.dot(weights_linear[closest_idxs], h_linear_conj) - d_inner_h_p1 = np.dot(weights_linear[closest_idxs + 1], h_linear_conj) - d_inner_h_p2 = np.dot(weights_linear[closest_idxs + 2], h_linear_conj) + d_inner_h_m2 = weights_linear[closest_idxs - 2] @ h_linear_conj + d_inner_h_m1 = weights_linear[closest_idxs - 1] @ h_linear_conj + d_inner_h_0 = weights_linear[closest_idxs] @ h_linear_conj + d_inner_h_p1 = weights_linear[closest_idxs + 1] @ h_linear_conj + d_inner_h_p2 = weights_linear[closest_idxs + 2] @ h_linear_conj else: - d_inner_h_at_roq_time_samples = np.dot(weights_linear, h_linear_conj) + d_inner_h_at_roq_time_samples = weights_linear @ h_linear_conj d_inner_h_m2 = d_inner_h_at_roq_time_samples[closest_idxs - 2] d_inner_h_m1 = d_inner_h_at_roq_time_samples[closest_idxs - 1] d_inner_h_0 = d_inner_h_at_roq_time_samples[closest_idxs] @@ -715,6 +717,7 @@ def _set_weights(self, linear_matrix, quadratic_matrix): linear and quadratic basis """ + xp = aac.array_namespace(self.interferometers.frequency_array) time_space = self._get_time_resolution() number_of_time_samples = int(self.interferometers.duration / time_space) earth_light_crossing_time = 2 * radius_of_earth / speed_of_light + 5 * time_space @@ -734,7 +737,7 @@ def _set_weights(self, linear_matrix, quadratic_matrix): - self.interferometers.start_time ) / time_space)) ) - self.weights['time_samples'] = np.arange(start_idx, end_idx + 1) * float(time_space) + self.weights['time_samples'] = xp.arange(start_idx, end_idx + 1) * float(time_space) logger.info("Using {} ROQ time samples".format(len(self.weights['time_samples']))) # select bases to be used, set prior ranges and frequency nodes if exist @@ -787,10 +790,10 @@ def _set_weights(self, linear_matrix, quadratic_matrix): roq_mask = roq_frequencies >= roq_scaled_minimum_frequency roq_frequencies = roq_frequencies[roq_mask] overlap_frequencies, ifo_idxs_this_ifo, roq_idxs_this_ifo = np.intersect1d( - ifo.frequency_array[ifo.frequency_mask], roq_frequencies, + np.asarray(ifo.frequency_array[ifo.frequency_mask]), roq_frequencies, return_indices=True) else: - overlap_frequencies = ifo.frequency_array[ifo.frequency_mask] + overlap_frequencies = np.asarray(ifo.frequency_array[ifo.frequency_mask]) roq_idxs_this_ifo = np.arange( linear_matrix['basis_linear'][str(idxs_in_prior_range['linear'][0])]['basis'].shape[1], dtype=int) @@ -846,31 +849,43 @@ def _set_weights_linear(self, linear_matrix, basis_idxs, roq_idxs, ifo_idxs): data_over_psd = {} for ifo in self.interferometers: nonzero_idxs[ifo.name] = ifo_idxs[ifo.name] + int( - ifo.frequency_array[ifo.frequency_mask][0] * self.interferometers.duration) - data_over_psd[ifo.name] = ifo.frequency_domain_strain[ifo.frequency_mask][ifo_idxs[ifo.name]] / \ - ifo.power_spectral_density_array[ifo.frequency_mask][ifo_idxs[ifo.name]] - try: - import pyfftw - ifft_input = pyfftw.empty_aligned(number_of_time_samples, dtype=complex) - ifft_output = pyfftw.empty_aligned(number_of_time_samples, dtype=complex) - ifft = pyfftw.FFTW(ifft_input, ifft_output, direction='FFTW_BACKWARD') - except ImportError: + ifo.minimum_frequency * self.interferometers.duration) + data_over_psd[ifo.name] = ( + ifo.frequency_domain_strain[ifo.frequency_mask][ifo_idxs[ifo.name]] + / ifo.power_spectral_density_array[ifo.frequency_mask][ifo_idxs[ifo.name]] + ) + xp = array_module(data_over_psd) + if aac.is_numpy_namespace(xp): + try: + import pyfftw + ifft_input = pyfftw.empty_aligned(number_of_time_samples, dtype=complex) + ifft_output = pyfftw.empty_aligned(number_of_time_samples, dtype=complex) + ifft = pyfftw.FFTW(ifft_input, ifft_output, direction='FFTW_BACKWARD') + except ImportError: + pyfftw = None + logger.warning("You do not have pyfftw installed, falling back to numpy.fft.") + ifft_input = np.zeros(number_of_time_samples, dtype=complex) + ifft = np.fft.ifft + else: pyfftw = None - logger.warning("You do not have pyfftw installed, falling back to numpy.fft.") - ifft_input = np.zeros(number_of_time_samples, dtype=complex) - ifft = np.fft.ifft + ifft_input = xp.zeros(number_of_time_samples, dtype=complex) + ifft = xp.fft.ifft for basis_idx in basis_idxs: logger.info(f"Building linear ROQ weights for the {basis_idx}-th basis.") - linear_matrix_single = linear_matrix['basis_linear'][str(basis_idx)]['basis'] + linear_matrix_single = xp.asarray(linear_matrix['basis_linear'][str(basis_idx)]['basis']) basis_size = linear_matrix_single.shape[0] for ifo in self.interferometers: - ifft_input[:] *= 0. + if pyfftw: + ifft_input[:] *= 0. + else: + ifft_input *= 0 linear_weights = \ - np.zeros((len(self.weights['time_samples']), basis_size), dtype=complex) + xp.zeros((basis_size, len(self.weights['time_samples'])), dtype=complex) for i in range(basis_size): - basis_element = linear_matrix_single[i][roq_idxs[ifo.name]] - ifft_input[nonzero_idxs[ifo.name]] = data_over_psd[ifo.name] * np.conj(basis_element) - linear_weights[:, i] = ifft(ifft_input)[start_idx:end_idx + 1] + basis_element = xp.asarray(linear_matrix_single[i][roq_idxs[ifo.name]]).conj() + ifft_input = xpx.at(ifft_input, nonzero_idxs[ifo.name]).set(data_over_psd[ifo.name] * basis_element) + linear_weights = xpx.at(linear_weights, i).set(ifft(ifft_input)[start_idx:end_idx + 1]) + linear_weights = linear_weights.T linear_weights *= 4. * number_of_time_samples / float(self.interferometers.duration) self.weights[ifo.name + '_linear'].append(linear_weights) if pyfftw is not None: @@ -890,6 +905,7 @@ def _set_weights_linear_multiband(self, linear_matrix, basis_idxs): """ for ifo in self.interferometers: self.weights[ifo.name + '_linear'] = [] + xp = aac.array_namespace(self.interferometers.frequency_array) Tbs = linear_matrix['durations_s_linear'][()] / self.roq_scale_factor start_end_frequency_bins = linear_matrix['start_end_frequency_bins_linear'][()] basis_dimension = np.sum(start_end_frequency_bins[:, 1] - start_end_frequency_bins[:, 0] + 1) @@ -897,29 +913,36 @@ def _set_weights_linear_multiband(self, linear_matrix, basis_idxs): # prepare time-shifted data, which is multiplied by basis tc_shifted_data = dict() for ifo in self.interferometers: - over_whitened_frequency_data = np.zeros(int(fhigh_basis * ifo.duration) + 1, dtype=complex) - over_whitened_frequency_data[np.arange(len(ifo.frequency_domain_strain))[ifo.frequency_mask]] = \ - ifo.frequency_domain_strain[ifo.frequency_mask] / ifo.power_spectral_density_array[ifo.frequency_mask] - over_whitened_time_data = np.fft.irfft(over_whitened_frequency_data) - tc_shifted_data[ifo.name] = np.zeros((basis_dimension, len(self.weights['time_samples'])), dtype=complex) + over_whitened_frequency_data = xp.zeros(int(fhigh_basis * ifo.duration) + 1, dtype=complex) + over_whitened_frequency_data = xpx.at( + over_whitened_frequency_data, xp.arange(len(ifo.frequency_domain_strain))[ifo.frequency_mask] + ).set(ifo.frequency_domain_strain[ifo.frequency_mask] / ifo.power_spectral_density_array[ifo.frequency_mask]) + over_whitened_time_data = xp.fft.irfft(over_whitened_frequency_data) + tc_shifted_data[ifo.name] = xp.zeros((basis_dimension, len(self.weights['time_samples'])), dtype=complex) start_idx_of_band = 0 for b, Tb in enumerate(Tbs): start_frequency_bin, end_frequency_bin = start_end_frequency_bins[b] - fs = np.arange(start_frequency_bin, end_frequency_bin + 1) / Tb - Db = np.fft.rfft( + fs = xp.arange(start_frequency_bin, end_frequency_bin + 1) / Tb + Db = xp.fft.rfft( over_whitened_time_data[-int(2. * fhigh_basis * Tb):] )[start_frequency_bin:end_frequency_bin + 1] start_idx_of_next_band = start_idx_of_band + end_frequency_bin - start_frequency_bin + 1 - tc_shifted_data[ifo.name][start_idx_of_band:start_idx_of_next_band] = 4. / Tb * Db[:, None] * np.exp( - 2. * np.pi * 1j * fs[:, None] * (self.weights['time_samples'][None, :] - float(ifo.duration) + Tb)) + this_data = xp.zeros(len(self.weights['time_samples']), dtype=complex) + sl = slice(start_idx_of_band, start_idx_of_next_band) + this_data = ( + 4. / Tb * Db[:, None] * xp.exp( + 2. * np.pi * 1j * fs[:, None] * (xp.asarray(self.weights['time_samples'][None, :]) - ifo.duration + Tb) + ) + ) + tc_shifted_data[ifo.name] = xpx.at(tc_shifted_data[ifo.name], sl).set(this_data) + start_idx_of_band = start_idx_of_next_band # compute inner products for basis_idx in basis_idxs: logger.info(f"Building linear ROQ weights for the {basis_idx}-th basis.") - linear_matrix_single = linear_matrix['basis_linear'][str(basis_idx)]['basis'][()] + linear_matrix_single = xp.asarray(linear_matrix['basis_linear'][str(basis_idx)]['basis'][()]) for ifo in self.interferometers: - self.weights[ifo.name + '_linear'].append( - np.dot(np.conj(linear_matrix_single), tc_shifted_data[ifo.name]).T) + self.weights[ifo.name + '_linear'].append((linear_matrix_single.conj() @ tc_shifted_data[ifo.name]).T) def _set_weights_quadratic(self, quadratic_matrix, basis_idxs, roq_idxs, ifo_idxs): """ @@ -941,14 +964,15 @@ def _set_weights_quadratic(self, quadratic_matrix, basis_idxs, roq_idxs, ifo_idx """ for ifo in self.interferometers: self.weights[ifo.name + '_quadratic'] = [] + xp = aac.array_namespace(self.interferometers.frequency_array) for basis_idx in basis_idxs: logger.info(f"Building quadratic ROQ weights for the {basis_idx}-th basis.") - quadratic_matrix_single = quadratic_matrix['basis_quadratic'][str(basis_idx)]['basis'][()].real + quadratic_matrix_single = xp.asarray(quadratic_matrix['basis_quadratic'][str(basis_idx)]['basis'][()].real) for ifo in self.interferometers: + inv_psd = xp.asarray(1 / ifo.power_spectral_density_array[ifo.frequency_mask][ifo_idxs[ifo.name]]) self.weights[ifo.name + '_quadratic'].append( - 4. / ifo.strain_data.duration * np.dot( - quadratic_matrix_single[:, roq_idxs[ifo.name]], - 1 / ifo.power_spectral_density_array[ifo.frequency_mask][ifo_idxs[ifo.name]])) + 4. / ifo.strain_data.duration * quadratic_matrix_single[:, roq_idxs[ifo.name]] @ inv_psd + ) del quadratic_matrix_single def _set_weights_quadratic_multiband(self, quadratic_matrix, basis_idxs): @@ -965,6 +989,7 @@ def _set_weights_quadratic_multiband(self, quadratic_matrix, basis_idxs): """ for ifo in self.interferometers: self.weights[ifo.name + '_quadratic'] = [] + xp = aac.array_namespace(self.interferometers.frequency_array) Tbs = quadratic_matrix['durations_s_quadratic'][()] / self.roq_scale_factor start_end_frequency_bins = quadratic_matrix['start_end_frequency_bins_quadratic'][()] basis_dimension = np.sum(start_end_frequency_bins[:, 1] - start_end_frequency_bins[:, 0] + 1) @@ -972,27 +997,31 @@ def _set_weights_quadratic_multiband(self, quadratic_matrix, basis_idxs): # prepare coefficients multiplied by basis multibanded_inverse_psd = dict() for ifo in self.interferometers: - inverse_psd_frequency = np.zeros(int(fhigh_basis * ifo.duration) + 1) - inverse_psd_frequency[np.arange(len(ifo.power_spectral_density_array))[ifo.frequency_mask]] = \ - 1. / ifo.power_spectral_density_array[ifo.frequency_mask] - inverse_psd_time = np.fft.irfft(inverse_psd_frequency) - multibanded_inverse_psd[ifo.name] = np.zeros(basis_dimension) + inverse_psd_frequency = xp.zeros(int(fhigh_basis * ifo.duration) + 1) + sl = np.arange(len(ifo.power_spectral_density_array))[ifo.frequency_mask] + inverse_psd_frequency = xpx.at(inverse_psd_frequency, sl).set( + 1. / xp.asarray(ifo.power_spectral_density_array[ifo.frequency_mask]) + ) + inverse_psd_time = xp.fft.irfft(inverse_psd_frequency) + multibanded_inverse_psd[ifo.name] = xp.zeros(basis_dimension) start_idx_of_band = 0 for b, Tb in enumerate(Tbs): start_frequency_bin, end_frequency_bin = start_end_frequency_bins[b] number_of_samples_half = int(fhigh_basis * Tb) start_idx_of_next_band = start_idx_of_band + end_frequency_bin - start_frequency_bin + 1 - multibanded_inverse_psd[ifo.name][start_idx_of_band:start_idx_of_next_band] = 4. / Tb * np.fft.rfft( - np.append(inverse_psd_time[:number_of_samples_half], inverse_psd_time[-number_of_samples_half:]) + sl = slice(start_idx_of_band, start_idx_of_next_band) + this_data = 4. / Tb * xp.fft.rfft( + xp.concat([inverse_psd_time[:number_of_samples_half], inverse_psd_time[-number_of_samples_half:]]) )[start_frequency_bin:end_frequency_bin + 1].real + multibanded_inverse_psd[ifo.name] = xpx.at(multibanded_inverse_psd[ifo.name], sl).set(this_data) start_idx_of_band = start_idx_of_next_band # compute inner products for basis_idx in basis_idxs: logger.info(f"Building quadratic ROQ weights for the {basis_idx}-th basis.") - quadratic_matrix_single = quadratic_matrix['basis_quadratic'][str(basis_idx)]['basis'][()].real + quadratic_matrix_single = xp.asarray(quadratic_matrix['basis_quadratic'][str(basis_idx)]['basis'][()].real) for ifo in self.interferometers: self.weights[ifo.name + '_quadratic'].append( - np.dot(quadratic_matrix_single, multibanded_inverse_psd[ifo.name])) + quadratic_matrix_single @ multibanded_inverse_psd[ifo.name]) def save_weights(self, filename, format='hdf5'): """ @@ -1207,8 +1236,8 @@ def generate_time_sample_from_marginalized_likelihood(self, signal_polarizations times = self._times if self.jitter_time: times = times + parameters["time_jitter"] - time_prior_array = self.priors['geocent_time'].prob(times) - time_post = np.exp(time_log_like - max(time_log_like)) * time_prior_array + time_prior_array = np.asarray(self.priors['geocent_time'].prob(times)) + time_post = np.exp(np.asarray(time_log_like - max(time_log_like))) * time_prior_array time_post /= np.sum(time_post) return random.rng.choice(times, p=time_post) diff --git a/test/gw/likelihood_test.py b/test/gw/likelihood_test.py index 1e9389354..125fca229 100644 --- a/test/gw/likelihood_test.py +++ b/test/gw/likelihood_test.py @@ -16,7 +16,13 @@ class BackendWaveformGenerator(bilby.gw.waveform_generator.WaveformGenerator): - """A thin wrapper to emulate different backends in the waveform generator.""" + """ + A thin wrapper to emulate different backends in the waveform generator. + + This ensures that all frequency arrays that might be used inside the + source are cast to numpy for compatibility. The outputs are converted + to the appropriate array type. + """ def __init__(self, wfg, xp): self.wfg = wfg self.xp = xp @@ -35,12 +41,15 @@ def convert_nested_dict(self, data): raise ValueError("Input must be an array API object or a dict of such objects.") def _strain_from_model(self, model_data_points, model, parameters): - # we can't pass a frequency array through as a torch array model_data_points = np.asarray(model_data_points) return super()._strain_from_model(model_data_points, model, parameters) def frequency_domain_strain(self, parameters): self.wfg.frequency_array = np.asarray(self.wfg.frequency_array) + if "frequency_nodes" in self.wfg.waveform_arguments: + self.wfg.waveform_arguments["frequency_nodes"] = np.asarray( + self.wfg.waveform_arguments["frequency_nodes"] + ) wf = self.wfg.__class__.frequency_domain_strain(self, parameters) return self.convert_nested_dict(wf) @@ -321,13 +330,31 @@ def test_time_reference_agrees_with_default(self): ) +class ROQBasisMixin: + + @property + def roq_dir(self): + trial_roq_paths = [ + "/roq_basis", + os.path.join(os.path.expanduser("~"), "ROQ_data/IMRPhenomPv2/4s"), + "/home/cbc/ROQ_data/IMRPhenomPv2/4s", + ] + if "BILBY_TESTING_ROQ_DIR" in os.environ: + trial_roq_paths.insert(0, os.environ["BILBY_TESTING_ROQ_DIR"]) + for path in trial_roq_paths: + if os.path.isdir(path): + return path + raise Exception("Unable to load ROQ basis: cannot proceed with tests") + @pytest.mark.requires_roqs @pytest.mark.array_backend @pytest.mark.usefixtures("xp_class") -class TestROQLikelihood(unittest.TestCase): +@pytest.mark.flaky(reruns=3) # pyfftw is flake on some machines +class TestROQLikelihood(ROQBasisMixin, unittest.TestCase): def setUp(self): self.duration = self.xp.asarray(4.0) self.sampling_frequency = self.xp.asarray(2048.0) + bilby.core.utils.random.seed(500) self.test_parameters = dict( mass_1=36.0, @@ -398,22 +425,6 @@ def tearDown(self): self.priors, ) - @property - def roq_dir(self): - trial_roq_paths = [ - "/roq_basis", - os.path.join(os.path.expanduser("~"), "ROQ_data/IMRPhenomPv2/4s"), - "/home/cbc/ROQ_data/IMRPhenomPv2/4s", - ] - if "BILBY_TESTING_ROQ_DIR" in os.environ: - trial_roq_paths.insert(0, os.environ["BILBY_TESTING_ROQ_DIR"]) - print(trial_roq_paths) - for path in trial_roq_paths: - print(path, os.path.isdir(path)) - if os.path.isdir(path): - return path - raise Exception("Unable to load ROQ basis: cannot proceed with tests") - @property def linear_matrix_file(self): return f"{self.roq_dir}/B_linear.npy" @@ -604,33 +615,18 @@ def test_create_roq_weights_fails_due_to_duration(self): @pytest.mark.requires_roqs -class TestRescaledROQLikelihood(unittest.TestCase): +class TestRescaledROQLikelihood(unittest.TestCase, ROQBasisMixin): def test_rescaling(self): + linear_matrix_file = f"{self.roq_dir}/B_linear.npy" + quadratic_matrix_file = f"{self.roq_dir}/B_quadratic.npy" - # Possible locations for the ROQ: in the docker image, local, or on CIT - trial_roq_paths = [ - "/roq_basis", - os.path.join(os.path.expanduser("~"), "ROQ_data/IMRPhenomPv2/4s"), - "/home/cbc/ROQ_data/IMRPhenomPv2/4s", - ] - roq_dir = None - for path in trial_roq_paths: - if os.path.isdir(path): - roq_dir = path - break - if roq_dir is None: - raise Exception("Unable to load ROQ basis: cannot proceed with tests") - - linear_matrix_file = "{}/B_linear.npy".format(roq_dir) - quadratic_matrix_file = "{}/B_quadratic.npy".format(roq_dir) - - fnodes_linear_file = "{}/fnodes_linear.npy".format(roq_dir) + fnodes_linear_file = f"{self.roq_dir}/fnodes_linear.npy" fnodes_linear = np.load(fnodes_linear_file).T - fnodes_quadratic_file = "{}/fnodes_quadratic.npy".format(roq_dir) + fnodes_quadratic_file = f"{self.roq_dir}/fnodes_quadratic.npy" fnodes_quadratic = np.load(fnodes_quadratic_file).T - self.linear_matrix_file = "{}/B_linear.npy".format(roq_dir) - self.quadratic_matrix_file = "{}/B_quadratic.npy".format(roq_dir) - self.params_file = "{}/params.dat".format(roq_dir) + self.linear_matrix_file = f"{self.roq_dir}/B_linear.npy" + self.quadratic_matrix_file = f"{self.roq_dir}/B_quadratic.npy" + self.params_file = f"{self.roq_dir}/params.dat" scale_factor = 0.5 params = np.genfromtxt(self.params_file, names=True) @@ -680,7 +676,7 @@ def test_rescaling(self): @pytest.mark.requires_roqs @pytest.mark.array_backend @pytest.mark.usefixtures("xp_class") -class TestROQLikelihoodHDF5(unittest.TestCase): +class TestROQLikelihoodHDF5(unittest.TestCase, ROQBasisMixin): """ Test ROQ likelihood constructed from .hdf5 basis @@ -688,9 +684,8 @@ class TestROQLikelihoodHDF5(unittest.TestCase): respectively, and 2 quadratic bases constructed over 8Msun= self.priors["chirp_mass"].minimum) * @@ -979,8 +974,8 @@ def assertLess_likelihood_errors( interferometers=interferometers, priors=self.priors, waveform_generator=search_waveform_generator, - linear_matrix=basis_linear, - quadratic_matrix=basis_quadratic, + linear_matrix=f"{self.roq_dir}/{basis_linear}", + quadratic_matrix=f"{self.roq_dir}/{basis_quadratic}", roq_scale_factor=roq_scale_factor ) for mc in np.linspace(self.priors["chirp_mass"].minimum, self.priors["chirp_mass"].maximum, 11): @@ -993,7 +988,7 @@ def assertLess_likelihood_errors( @pytest.mark.requires_roqs -class TestCreateROQLikelihood(unittest.TestCase): +class TestCreateROQLikelihood(unittest.TestCase, ROQBasisMixin): """ Test if ROQ likelihood is constructed without any errors from .hdf5 or .npy basis @@ -1001,9 +996,8 @@ class TestCreateROQLikelihood(unittest.TestCase): respectively, and 2 quadratic bases constructed over 8Msun Date: Tue, 3 Feb 2026 15:22:43 -0500 Subject: [PATCH 103/140] BUG: fix a missing array case --- bilby/core/prior/analytical.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bilby/core/prior/analytical.py b/bilby/core/prior/analytical.py index 02e65bf3a..69afd5926 100644 --- a/bilby/core/prior/analytical.py +++ b/bilby/core/prior/analytical.py @@ -1011,7 +1011,7 @@ def ln_prob(self, val, *, xp=None): ======= Union[float, array_like]: Prior probability of val """ - ln_prob = xlog1py(self.beta - 1.0, -val) + xlogy(self.alpha - 1.0, val) + ln_prob = xlog1py(self.beta - 1.0, -val) + xlogy(xp.asarray(self.alpha - 1.0), val) ln_prob -= betaln(self.alpha, self.beta) return xp.nan_to_num(ln_prob, nan=-xp.inf, neginf=-xp.inf, posinf=-xp.inf) From a44cdbf0ced892e6dd6fb0d96a6c5b4ed1c7e699 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Tue, 3 Feb 2026 15:34:45 -0500 Subject: [PATCH 104/140] FMT: pre-commit fixes --- bilby/gw/likelihood/roq.py | 9 ++++++--- test/gw/likelihood_test.py | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/bilby/gw/likelihood/roq.py b/bilby/gw/likelihood/roq.py index 11e124090..16f9c4cfc 100644 --- a/bilby/gw/likelihood/roq.py +++ b/bilby/gw/likelihood/roq.py @@ -916,7 +916,9 @@ def _set_weights_linear_multiband(self, linear_matrix, basis_idxs): over_whitened_frequency_data = xp.zeros(int(fhigh_basis * ifo.duration) + 1, dtype=complex) over_whitened_frequency_data = xpx.at( over_whitened_frequency_data, xp.arange(len(ifo.frequency_domain_strain))[ifo.frequency_mask] - ).set(ifo.frequency_domain_strain[ifo.frequency_mask] / ifo.power_spectral_density_array[ifo.frequency_mask]) + ).set( + ifo.frequency_domain_strain[ifo.frequency_mask] / ifo.power_spectral_density_array[ifo.frequency_mask] + ) over_whitened_time_data = xp.fft.irfft(over_whitened_frequency_data) tc_shifted_data[ifo.name] = xp.zeros((basis_dimension, len(self.weights['time_samples'])), dtype=complex) start_idx_of_band = 0 @@ -931,7 +933,8 @@ def _set_weights_linear_multiband(self, linear_matrix, basis_idxs): sl = slice(start_idx_of_band, start_idx_of_next_band) this_data = ( 4. / Tb * Db[:, None] * xp.exp( - 2. * np.pi * 1j * fs[:, None] * (xp.asarray(self.weights['time_samples'][None, :]) - ifo.duration + Tb) + 2. * np.pi * 1j * fs[:, None] + * (xp.asarray(self.weights['time_samples'][None, :]) - ifo.duration + Tb) ) ) tc_shifted_data[ifo.name] = xpx.at(tc_shifted_data[ifo.name], sl).set(this_data) @@ -971,7 +974,7 @@ def _set_weights_quadratic(self, quadratic_matrix, basis_idxs, roq_idxs, ifo_idx for ifo in self.interferometers: inv_psd = xp.asarray(1 / ifo.power_spectral_density_array[ifo.frequency_mask][ifo_idxs[ifo.name]]) self.weights[ifo.name + '_quadratic'].append( - 4. / ifo.strain_data.duration * quadratic_matrix_single[:, roq_idxs[ifo.name]] @ inv_psd + 4. / ifo.strain_data.duration * quadratic_matrix_single[:, roq_idxs[ifo.name]] @ inv_psd ) del quadratic_matrix_single diff --git a/test/gw/likelihood_test.py b/test/gw/likelihood_test.py index 125fca229..05201b731 100644 --- a/test/gw/likelihood_test.py +++ b/test/gw/likelihood_test.py @@ -346,6 +346,7 @@ def roq_dir(self): return path raise Exception("Unable to load ROQ basis: cannot proceed with tests") + @pytest.mark.requires_roqs @pytest.mark.array_backend @pytest.mark.usefixtures("xp_class") From 2542fa24b08bff2ae5f6a090e7de23ad55884c1f Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Tue, 3 Feb 2026 15:54:29 -0500 Subject: [PATCH 105/140] CI: drop torch tests for python 3.10 --- .github/workflows/unit-tests.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index a72c32223..5829c23ec 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -63,9 +63,6 @@ jobs: # - name: Run precommits # run: | # pre-commit run --all-files --verbose --show-diff-on-failure - - name: Run torch-backend unit tests - run: | - SCIPY_ARRAY_API=1 pytest --array-backend torch --durations 10 - name: Run unit tests run: | python -m pytest --cov=bilby --cov-branch --durations 10 -ra --color yes --cov-report=xml --junitxml=pytest.xml @@ -73,6 +70,11 @@ jobs: run: | python -m pip install .[jax] SCIPY_ARRAY_API=1 pytest --array-backend jax --durations 10 + - name: Run torch-backend unit tests + # there are scipy version issues with python 3.10 and torch + if: matrix.python.version > 3.10 + run: | + SCIPY_ARRAY_API=1 pytest --array-backend torch --durations 10 - name: Run sampler tests run: | pytest test/integration/sampler_run_test.py --durations 10 -v From a68bd3709c6a673b9282cf430488d7d8536b25ad Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Tue, 3 Feb 2026 16:26:40 -0500 Subject: [PATCH 106/140] FMT: precommit fix --- bilby/gw/likelihood/roq.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bilby/gw/likelihood/roq.py b/bilby/gw/likelihood/roq.py index 16f9c4cfc..d54091dfd 100644 --- a/bilby/gw/likelihood/roq.py +++ b/bilby/gw/likelihood/roq.py @@ -938,7 +938,7 @@ def _set_weights_linear_multiband(self, linear_matrix, basis_idxs): ) ) tc_shifted_data[ifo.name] = xpx.at(tc_shifted_data[ifo.name], sl).set(this_data) - + start_idx_of_band = start_idx_of_next_band # compute inner products for basis_idx in basis_idxs: From 4e3ca6542955da075aaa6c32244362c7474b917f Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Tue, 3 Feb 2026 16:40:28 -0500 Subject: [PATCH 107/140] TEST: exclude studentt tests for jax --- test/core/prior/prior_test.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/core/prior/prior_test.py b/test/core/prior/prior_test.py index a3165adce..67fbd2422 100644 --- a/test/core/prior/prior_test.py +++ b/test/core/prior/prior_test.py @@ -259,6 +259,11 @@ def condition_func(reference_params, test_param): p for p in self.priors if not isinstance(p, bilby.core.prior.Interped) ] + elif aac.is_jax_namespace(self.xp): + self.priors = [ + p for p in self.priors + if not isinstance(p, bilby.core.prior.StudentT) + ] def tearDown(self): del self.priors @@ -798,7 +803,6 @@ def test_accuracy(self): bilby.core.prior.WeightedDiscreteValues, ) if isinstance(prior, (testTuple)): - print(prior) np.testing.assert_almost_equal(prior.prob(self.xp.asarray(domain)), scipy_prob) np.testing.assert_almost_equal(prior.ln_prob(self.xp.asarray(domain)), scipy_lnprob) np.testing.assert_almost_equal(prior.cdf(self.xp.asarray(domain)), scipy_cdf) From a909e24e8b2d10d7409eecd848b7c0a41dd47da7 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Tue, 3 Feb 2026 17:58:10 -0500 Subject: [PATCH 108/140] Add some more explicit array casts --- bilby/core/prior/analytical.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/bilby/core/prior/analytical.py b/bilby/core/prior/analytical.py index 69afd5926..bec049d90 100644 --- a/bilby/core/prior/analytical.py +++ b/bilby/core/prior/analytical.py @@ -981,7 +981,8 @@ def rescale(self, val, *, xp=None): This explicitly casts to the requested backend, but the computation will be done by scipy. """ return ( - xp.asarray(betaincinv(self.alpha, self.beta, val)) * (self.maximum - self.minimum) + xp.asarray(betaincinv(xp.asarray(self.alpha), xp.asarray(self.beta), val)) + * (self.maximum - self.minimum) + self.minimum ) @@ -1011,14 +1012,18 @@ def ln_prob(self, val, *, xp=None): ======= Union[float, array_like]: Prior probability of val """ - ln_prob = xlog1py(self.beta - 1.0, -val) + xlogy(xp.asarray(self.alpha - 1.0), val) - ln_prob -= betaln(self.alpha, self.beta) + ln_prob = xlog1py(xp.asarray(self.beta - 1.0), -val) + xlogy(xp.asarray(self.alpha - 1.0), val) + ln_prob -= betaln(xp.asarray(self.alpha), xp.asarray(self.beta)) return xp.nan_to_num(ln_prob, nan=-xp.inf, neginf=-xp.inf, posinf=-xp.inf) @xp_wrap def cdf(self, val, *, xp=None): return xp.nan_to_num( - betainc(self.alpha, self.beta, (val - self.minimum) / (self.maximum - self.minimum)) + betainc( + xp.asarray(self.alpha), + xp.asarray(self.beta), + (val - self.minimum) / (self.maximum - self.minimum) + ) ) + (val > self.maximum) From 9343b45b637142b542f7a16492f695e3b2c65f6e Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Tue, 17 Feb 2026 09:44:41 -0500 Subject: [PATCH 109/140] BUG: bug fixes for prior and gw likelihoods --- bilby/core/prior/dict.py | 27 +++++++++----------------- bilby/core/utils/calculus.py | 4 ++-- bilby/gw/likelihood/base.py | 37 +++++++++++++++++++----------------- 3 files changed, 31 insertions(+), 37 deletions(-) diff --git a/bilby/core/prior/dict.py b/bilby/core/prior/dict.py index 46746073d..68f76e06a 100644 --- a/bilby/core/prior/dict.py +++ b/bilby/core/prior/dict.py @@ -5,7 +5,7 @@ from io import open as ioopen from warnings import warn -import array_api_extra as xpx +import array_api_compat as aac import numpy as np from .analytical import DeltaFunction @@ -467,7 +467,7 @@ def check_efficiency(n_tested, n_valid): sample = self.sample_subset(keys=keys, size=size, xp=xp) is_valid = self.evaluate_constraints(sample) n_tested_samples += 1 - n_valid_samples += int(is_valid) + n_valid_samples += int(is_valid.item()) check_efficiency(n_tested_samples, n_valid_samples) if is_valid: return sample @@ -554,7 +554,7 @@ def prob(self, sample, *, xp=None, **kwargs): @xp_wrap def check_prob(self, sample, prob, *, xp=None): ratio = self.normalize_constraint_factor(tuple(sample.keys())) - if xp.all(prob == 0.0): + if not aac.is_jax_namespace(xp) and xp.all(prob == 0.0): return prob * ratio else: if isinstance(prob, float): @@ -563,11 +563,8 @@ def check_prob(self, sample, prob, *, xp=None): else: return 0.0 else: - constrained_prob = xp.zeros_like(prob) - in_bounds = xp.isfinite(prob) - subsample = {key: sample[key][in_bounds] for key in sample} - keep = self.evaluate_constraints(subsample, xp=xp) - constrained_prob = xpx.at(constrained_prob, in_bounds).set(prob[in_bounds] * keep * ratio) + keep = self.evaluate_constraints(sample, xp=xp) + constrained_prob = xp.where(keep, prob * ratio, 0.0) return constrained_prob @xp_wrap @@ -591,8 +588,7 @@ def ln_prob(self, sample, axis=None, normalized=True, *, xp=None): """ ln_prob = xp.sum(xp.stack([self[key].ln_prob(sample[key], xp=xp) for key in sample]), axis=axis) - return self.check_ln_prob(sample, ln_prob, - normalized=normalized, xp=xp) + return self.check_ln_prob(sample, ln_prob, normalized=normalized, xp=xp) @xp_wrap def check_ln_prob(self, sample, ln_prob, normalized=True, *, xp=None): @@ -600,7 +596,7 @@ def check_ln_prob(self, sample, ln_prob, normalized=True, *, xp=None): ratio = self.normalize_constraint_factor(tuple(sample.keys())) else: ratio = 1 - if xp.all(xp.isfinite(ln_prob)): + if not aac.is_jax_namespace(xp) and xp.all(xp.isfinite(ln_prob)): return ln_prob else: if isinstance(ln_prob, float): @@ -609,13 +605,8 @@ def check_ln_prob(self, sample, ln_prob, normalized=True, *, xp=None): else: return -np.inf else: - constrained_ln_prob = -np.inf * xp.ones_like(ln_prob) - in_bounds = xp.isfinite(ln_prob) - subsample = {key: sample[key][in_bounds] for key in sample} - keep = xp.log(self.evaluate_constraints(subsample, xp=xp)) - constrained_ln_prob = xpx.at(constrained_ln_prob, in_bounds).set( - ln_prob[in_bounds] + keep + xp.log(ratio) - ) + keep = self.evaluate_constraints(sample, xp=xp) + constrained_ln_prob = xp.where(keep, ln_prob + xp.log(ratio), -xp.inf) return constrained_ln_prob @xp_wrap diff --git a/bilby/core/utils/calculus.py b/bilby/core/utils/calculus.py index 496946c2b..f97ee1a9b 100644 --- a/bilby/core/utils/calculus.py +++ b/bilby/core/utils/calculus.py @@ -263,8 +263,8 @@ def _call_jax(self, x, y): from interpax import interp2d return interp2d( - x, - y, + jnp.asarray(x), + jnp.asarray(y), jnp.asarray(self.x), jnp.asarray(self.y), jnp.asarray(self.z), diff --git a/bilby/gw/likelihood/base.py b/bilby/gw/likelihood/base.py index bd0c3c48d..f1af143ad 100644 --- a/bilby/gw/likelihood/base.py +++ b/bilby/gw/likelihood/base.py @@ -2,6 +2,7 @@ import os import copy +import array_api_compat as aac import attr import numpy as np from scipy.special import logsumexp @@ -296,32 +297,33 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array optimal_snr_squared_array = None normalization = 4 / self.waveform_generator.duration + xp = signal.__array_namespace__() 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( + d_inner_h_integrand = xp.tile( interferometer.frequency_domain_strain.conj() * signal / interferometer.power_spectral_density_array, (self.number_of_response_curves, 1)).T d_inner_h_integrand[_mask] *= self.calibration_draws[interferometer.name].T - d_inner_h_array = 4 / self.waveform_generator.duration * np.fft.fft( + d_inner_h_array = 4 / self.waveform_generator.duration * xp.fft.fft( d_inner_h_integrand[0:-1], axis=0 ).T optimal_snr_squared_integrand = ( - normalization * np.abs(signal)**2 / interferometer.power_spectral_density_array + normalization * xp.abs(signal)**2 / interferometer.power_spectral_density_array ) - optimal_snr_squared_array = np.dot( + optimal_snr_squared_array = xp.dot( optimal_snr_squared_integrand[_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( + d_inner_h_array = normalization * xp.fft.fft( signal[0:-1] * interferometer.frequency_domain_strain.conj()[0:-1] / interferometer.power_spectral_density_array[0:-1] @@ -333,12 +335,12 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array interferometer.frequency_domain_strain.conj() * signal / interferometer.power_spectral_density_array ) - d_inner_h_array = np.dot(d_inner_h_integrand[_mask], self.calibration_draws[interferometer.name].T) + d_inner_h_array = xp.dot(d_inner_h_integrand[_mask], self.calibration_draws[interferometer.name].T) optimal_snr_squared_integrand = ( - normalization * np.abs(signal)**2 / interferometer.power_spectral_density_array + normalization * xp.abs(signal)**2 / interferometer.power_spectral_density_array ) - optimal_snr_squared_array = np.dot( + optimal_snr_squared_array = xp.dot( optimal_snr_squared_integrand[_mask], self.calibration_abs_draws[interferometer.name].T ) @@ -790,14 +792,15 @@ def time_marginalized_likelihood(self, d_inner_h_tc_array, h_inner_h, *, paramet if self.jitter_time: times = self._times + parameters['time_jitter'] - _time_prior = self.priors['geocent_time'] - time_mask = (times >= _time_prior.minimum) & (times <= _time_prior.maximum) - times = times[time_mask] + if not aac.is_jax_array(d_inner_h_tc_array): + _time_prior = self.priors['geocent_time'] + time_mask = (times >= _time_prior.minimum) & (times <= _time_prior.maximum) + times = times[time_mask] + if self.calibration_marginalization: + d_inner_h_tc_array = d_inner_h_tc_array[:, time_mask] + else: + d_inner_h_tc_array = d_inner_h_tc_array[time_mask] time_prior_array = self.priors['geocent_time'].prob(times) * self._delta_tc - if self.calibration_marginalization: - d_inner_h_tc_array = d_inner_h_tc_array[:, time_mask] - else: - d_inner_h_tc_array = d_inner_h_tc_array[time_mask] if self.distance_marginalization: log_l_tc_array = self.distance_marginalized_likelihood( @@ -807,9 +810,9 @@ def time_marginalized_likelihood(self, d_inner_h_tc_array, h_inner_h, *, paramet d_inner_h=d_inner_h_tc_array, h_inner_h=h_inner_h) elif self.calibration_marginalization: - log_l_tc_array = np.real(d_inner_h_tc_array) - h_inner_h[:, np.newaxis] / 2 + log_l_tc_array = d_inner_h_tc_array.real - h_inner_h[:, np.newaxis] / 2 else: - log_l_tc_array = np.real(d_inner_h_tc_array) - h_inner_h / 2 + log_l_tc_array = d_inner_h_tc_array.real - h_inner_h / 2 return logsumexp(log_l_tc_array, b=time_prior_array, axis=-1) def get_calibration_log_likelihoods(self, signal_polarizations=None, *, parameters): From b99fc354aa2e5510fd7a52c42fa6a75fa06f27a4 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Tue, 17 Feb 2026 10:50:53 -0500 Subject: [PATCH 110/140] BUG: fix array namespace for torch --- bilby/gw/likelihood/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bilby/gw/likelihood/base.py b/bilby/gw/likelihood/base.py index f1af143ad..5d028d49f 100644 --- a/bilby/gw/likelihood/base.py +++ b/bilby/gw/likelihood/base.py @@ -297,7 +297,7 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array optimal_snr_squared_array = None normalization = 4 / self.waveform_generator.duration - xp = signal.__array_namespace__() + xp = aac.array_namespace(signal) if return_array is False: d_inner_h_array = None From a7fbff15a5e26d7e54f73badfd73854aa99987e4 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Thu, 14 May 2026 20:10:55 +0000 Subject: [PATCH 111/140] Update patches and backend documentation --- bilby/compat/patches.py | 53 ++++++++++---------- bilby/compat/utils.py | 88 ++++++++++++++++++++++++++++++++-- bilby/core/prior/analytical.py | 18 +------ bilby/core/utils/calculus.py | 2 +- 4 files changed, 112 insertions(+), 49 deletions(-) diff --git a/bilby/compat/patches.py b/bilby/compat/patches.py index 53345abd4..8df16d52c 100644 --- a/bilby/compat/patches.py +++ b/bilby/compat/patches.py @@ -3,21 +3,29 @@ from .utils import BackendNotImplementedError -def erfinv_import(xp): - if aac.is_numpy_namespace(xp): - from scipy.special import erfinv - elif aac.is_jax_namespace(xp): - from jax.scipy.special import erfinv - elif aac.is_torch_namespace(xp): - from torch.special import erfinv - elif aac.is_cupy_namespace(xp): - from cupyx.scipy.special import erfinv - else: - raise BackendNotImplementedError - return erfinv - - def multivariate_logpdf(xp, mean, cov): + """ + Return a function to evaluate the log probability density of a multivariate + Gaussian with given mean vector and covariance matrix for the provided + array backend. + + Parameters + ========== + xp: numpy, torch, jax.numpy + A module that will resolve to :code:`numpy`, :code:`torch`, or + :code:`jax.numpy` in :code:`array_api_compat.is_..._namespace`. + mean: array-like + A one-dimensional array providing the mean of the distribution. + cov: array-like + A two-dimensional array providing the covariance matrix of the + distribution. + + Returns + ======= + logpdf: callable + A callable that provides the log probaility density provided an array + of points to evaluate at. + """ if aac.is_numpy_namespace(xp): from scipy.stats import multivariate_normal @@ -33,18 +41,7 @@ def multivariate_logpdf(xp, mean, cov): mvn = MultivariateNormal(loc=mean, covariance_matrix=xp.asarray(cov)) logpdf = mvn.log_prob else: - raise BackendNotImplementedError + raise BackendNotImplementedError( + f"Unable to import multivariate_logpdf for {xp}" + ) return logpdf - - -def logsumexp(a, axis=None, b=None, keepdims=False, return_sign=False, *, xp=None): - if xp is None: - xp = aac.get_namespace(a) - - # the scipy version of logsumexp cannot be vmapped - if aac.is_jax_namespace(xp): - from jax.scipy.special import logsumexp as lse - else: - from scipy.special import logsumexp as lse - - return lse(a=a, axis=axis, b=b, keepdims=keepdims, return_sign=return_sign) diff --git a/bilby/compat/utils.py b/bilby/compat/utils.py index a05cc0920..e3d34861e 100644 --- a/bilby/compat/utils.py +++ b/bilby/compat/utils.py @@ -2,7 +2,7 @@ from collections.abc import Iterable import numpy as np -from array_api_compat import array_namespace +from array_api_compat import array_namespace, is_numpy_namespace from ..core.utils.log import logger @@ -10,6 +10,67 @@ def array_module(arr): + """ + Infer the array module (namespace) from the input argument. + This is a generalization of the :code:`array_api_compat.array_namespace` + function that can handle a wider variety of inputs, including some nested + structures. + + This function determines which array library backend is being used + by inspecting the input argument. It handles various input types and + fallback mechanisms to ensure a valid array module is always returned. + + The inference logic proceeds as follows: + 1. If a single-element tuple is provided, extract the element first. + 2. Attempt to use the array_api_compat.array_namespace() function + directly, which works for most array-like objects. + 3. If that fails, handle special cases: + - Dictionaries: extract values and infer from non-string values + - Builtin iterables (list, tuple, etc.): infer from elements + - Builtin scalars: default to numpy + - Pandas objects: default to numpy (treated as numpy-compatible) + - Unknown types: log a warning and default to numpy + + This is a best-effort function, but will not cover all possible edge cases. + + Parameters + ========== + arr: array-like, tuple, dict, or other type + The input argument to infer the array module from. Can be: + - An array object (numpy, cupy, jax.numpy, etc.) + - A tuple of arrays (single-element unwrapped) + - A dictionary with array values + - An iterable containing arrays + - A builtin scalar or type + + Returns + ======= + module + The array namespace module (e.g., numpy, cupy, jax.numpy, etc.). + Defaults to numpy if the module cannot be determined. + + Examples + ======== + >>> import numpy as np + >>> import jax.numpy as jnp + >>> array_module(np.array([1, 2, 3])) + + + >>> array_module(jnp.array([1, 2, 3])) + + + >>> array_module({'data': np.array([1, 2, 3])}) + + + >>> array_module([np.array([1]), np.array([2])]) + + + >>> array_module([1, jnp.array([2])]) + + + >>> array_module(5) + + """ if isinstance(arr, tuple) and len(arr) == 1: arr = arr[0] try: @@ -36,13 +97,32 @@ def array_module(arr): return np -def promote_to_array(args, backend, skip=None): +def promote_to_array(args, xp): + """ + Promote arguments to arrays using the specified array module. + + Parameters + ========== + args: tuple + Tuple of arguments to promote. + xp: module + The array module (namespace) to use for promotion. + skip: int, optional + Number of trailing arguments to skip promotion for. + Defaults to None (promote all arguments). + + Returns + ======= + tuple + Arguments with the first (len(args) - skip) elements promoted to + arrays using the specified module. + """ if skip is None: skip = len(args) else: skip = len(args) - skip - if backend.__name__ != "numpy": - args = tuple(backend.array(arg) for arg in args[:skip]) + args[skip:] + if not is_numpy_namespace(xp): + args = tuple(xp.array(arg) for arg in args[:skip]) + args[skip:] return args diff --git a/bilby/core/prior/analytical.py b/bilby/core/prior/analytical.py index bec049d90..e0de50327 100644 --- a/bilby/core/prior/analytical.py +++ b/bilby/core/prior/analytical.py @@ -7,6 +7,7 @@ betaincinv, betaln, erf, + erfinv, gammaincinv, gammainc, gammaln, @@ -18,8 +19,7 @@ from .base import Prior from ..utils import logger -from ...compat.patches import erfinv_import -from ...compat.utils import BackendNotImplementedError, array_module, xp_wrap +from ...compat.utils import array_module, xp_wrap class DeltaFunction(Prior): @@ -535,10 +535,6 @@ def rescale(self, val, *, xp=None): This maps to the inverse CDF. This has been analytically solved for this case. """ - try: - erfinv = erfinv_import(xp) - except BackendNotImplementedError: - raise NotImplementedError(f"Gaussian prior rescale not implemented for this {xp.__name__}") return self.mu + erfinv(2 * val - 1) * 2 ** 0.5 * self.sigma @xp_wrap @@ -627,12 +623,6 @@ def rescale(self, val, *, xp=None): This maps to the inverse CDF. This has been analytically solved for this case. """ - try: - erfinv = erfinv_import(xp) - except BackendNotImplementedError: - raise NotImplementedError( - f"Truncated Gaussian prior rescale not implemented for this {xp.__name__}" - ) return erfinv(2 * val * self.normalisation + erf( (self.minimum - self.mu) / 2 ** 0.5 / self.sigma)) * 2 ** 0.5 * self.sigma + self.mu @@ -727,10 +717,6 @@ def rescale(self, val, *, xp=None): This maps to the inverse CDF. This has been analytically solved for this case. """ - try: - erfinv = erfinv_import(xp) - except BackendNotImplementedError: - raise NotImplementedError(f"LogNormal prior rescale not implemented for this {xp.__name__}") return xp.exp(self.mu + (2 * self.sigma ** 2)**0.5 * erfinv(2 * val - 1)) @xp_wrap diff --git a/bilby/core/utils/calculus.py b/bilby/core/utils/calculus.py index f97ee1a9b..8794f00a4 100644 --- a/bilby/core/utils/calculus.py +++ b/bilby/core/utils/calculus.py @@ -3,9 +3,9 @@ import array_api_compat as aac import numpy as np from scipy.interpolate import RectBivariateSpline, interp1d as _interp1d +from scipy.special import logsumexp from .log import logger -from ...compat.patches import logsumexp from ...compat.utils import array_module, xp_wrap From 933dd947ac9dbbd1924ddfab2ce9cc31cbeaafcb Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Thu, 14 May 2026 20:25:51 +0000 Subject: [PATCH 112/140] Address some comments and add some docstrings --- bilby/compat/utils.py | 8 +- bilby/core/prior/analytical.py | 3 +- bilby/gw/detector/interferometer.py | 1 - bilby/gw/geometry.py | 202 ++++++++++++++++++++++++++-- 4 files changed, 200 insertions(+), 14 deletions(-) diff --git a/bilby/compat/utils.py b/bilby/compat/utils.py index e3d34861e..f6b74c071 100644 --- a/bilby/compat/utils.py +++ b/bilby/compat/utils.py @@ -97,7 +97,7 @@ def array_module(arr): return np -def promote_to_array(args, xp): +def promote_to_array(args, xp, skip=None): """ Promote arguments to arrays using the specified array module. @@ -116,6 +116,12 @@ def promote_to_array(args, xp): tuple Arguments with the first (len(args) - skip) elements promoted to arrays using the specified module. + + Notes + ===== + This function cannot handle manual specification of devices. Arrays + are promoted to the default device of the specified array module. This + may be added in future if there is a need. """ if skip is None: skip = len(args) diff --git a/bilby/core/prior/analytical.py b/bilby/core/prior/analytical.py index e0de50327..26af1f07e 100644 --- a/bilby/core/prior/analytical.py +++ b/bilby/core/prior/analytical.py @@ -71,6 +71,7 @@ def prob(self, val, *, xp=None): """ at_peak = (val == self.peak) + # coerce bool to float for some array backends return at_peak * 1.0 def cdf(self, val, *, xp=None): @@ -1755,5 +1756,3 @@ def cdf(self, val, *, xp=None): / (self.mode - self.rescaled_minimum) ) ) - - betaln, diff --git a/bilby/gw/detector/interferometer.py b/bilby/gw/detector/interferometer.py index 3a0367efe..e45112161 100644 --- a/bilby/gw/detector/interferometer.py +++ b/bilby/gw/detector/interferometer.py @@ -327,7 +327,6 @@ def get_detector_response(self, waveform_polarizations, parameters, frequencies= """ xp = array_module(waveform_polarizations) if frequencies is None: - # frequencies = self.frequency_array[self.frequency_mask] frequencies = self.frequency_array mask = self.frequency_mask else: diff --git a/bilby/gw/geometry.py b/bilby/gw/geometry.py index 68321d4b4..fa4be3d86 100644 --- a/bilby/gw/geometry.py +++ b/bilby/gw/geometry.py @@ -20,7 +20,29 @@ @dispatch def antenna_response(detector_tensor, ra, dec, time, psi, mode): - """""" + """ + Calculate the antenna response for a detector. + + Parameters + ========== + detector_tensor: array-like + The detector tensor (3x3 matrix). + ra: float or array-like + Right ascension of the source in radians. + dec: float or array-like + Declination of the source in radians. + time: float or array-like + GPS time of the observation. + psi: float or array-like + Polarization angle in radians. + mode: str + Polarization mode ('plus', 'cross', 'breathing', 'longitudinal', 'x', 'y'). + + Returns + ======= + array-like + The antenna response (scalar or array depending on input). + """ xp = array_module(detector_tensor) polarization_tensor = get_polarization_tensor(*promote_to_array((ra, dec, time, psi), xp), mode) return three_by_three_matrix_contraction(detector_tensor, polarization_tensor) @@ -28,7 +50,25 @@ def antenna_response(detector_tensor, ra, dec, time, psi, mode): @dispatch def calculate_arm(arm_tilt, arm_azimuth, longitude, latitude): - """""" + """ + Calculate arm unit vector from tilt, azimuth, and location. + + Parameters + ========== + arm_tilt: float or array-like + Tilt angle of the arm from horizontal in radians. + arm_azimuth: float or array-like + Azimuth angle of the arm in radians. + longitude: float or array-like + Longitude of the detector in radians. + latitude: float or array-like + Latitude of the detector in radians. + + Returns + ======= + array-like + 3D unit vector (shape (3,) or (3, ...)) representing the arm direction. + """ xp = array_module(arm_tilt) e_long = xp.asarray([-xp.sin(longitude), xp.cos(longitude), longitude * 0]) e_lat = xp.asarray( @@ -55,14 +95,50 @@ def calculate_arm(arm_tilt, arm_azimuth, longitude, latitude): @dispatch def detector_tensor(x, y): - """""" + """ + Calculate the detector tensor from x and y arm components. + + Parameters + ========== + x: array-like + 3D unit vector for the x arm. + y: array-like + 3D unit vector for the y arm. + + Returns + ======= + array-like + 3x3 detector tensor with components + :math:`d_{ij} = (x_i x_j - y_i y_j) / 2`. + """ xp = array_module(x) return (xp.outer(x, x) - xp.outer(y, y)) / 2 @dispatch def get_polarization_tensor(ra, dec, time, psi, mode): - """""" + """ + Calculate the polarization tensor for a given sky location and mode. + + Parameters + ========== + ra: float or array-like + Right ascension of the source in radians. + dec: float or array-like + Declination of the source in radians. + time: float or array-like + GPS time of the observation. + psi: float or array-like + Polarization angle in radians. + mode: str + Polarization mode: 'plus', 'cross', 'breathing', 'longitudinal', + 'x', or 'y'. + + Returns + ======= + array-like + 3x3 polarization tensor for the specified mode. + """ from functools import partial xp = array_module(ra) @@ -112,13 +188,47 @@ def get_polarization_tensor(ra, dec, time, psi, mode): @dispatch def get_polarization_tensor_multiple_modes(ra, dec, time, psi, modes): - """""" + """ + Calculate polarization tensors for multiple modes. + + Parameters + ========== + ra: float or array-like + Right ascension of the source in radians. + dec: float or array-like + Declination of the source in radians. + time: float or array-like + GPS time of the observation. + psi: float or array-like + Polarization angle in radians. + modes: list of str + List of polarization modes to calculate. + + Returns + ======= + list + List of 3x3 polarization tensors, one for each mode. + """ return [get_polarization_tensor(ra, dec, time, psi, mode) for mode in modes] @dispatch def rotation_matrix_from_delta(delta_x): - """""" + r""" + Calculate rotation matrix from a delta vector. + + Parameters + ========== + delta_x: array-like + 3D vector :math:`\vec{\Delta}x` representing the separation + or orientation. + + Returns + ======= + array-like + 3x3 rotation matrix that rotates the z-axis to align with + :math:`\vec{\Delta}x` direction. + """ xp = array_module(delta_x) delta_x = delta_x / (delta_x**2).sum() ** 0.5 alpha = xp.arctan2(-delta_x[1] * delta_x[2], delta_x[0]) @@ -150,14 +260,50 @@ def rotation_matrix_from_delta(delta_x): @dispatch def three_by_three_matrix_contraction(a, b): - """""" + """ + Perform contraction of two 3x3 matrices. + + Parameters + ========== + a: array-like + First 3x3 matrix. + b: array-like + Second 3x3 matrix. + + Returns + ======= + float or array-like + Scalar result of the einsum contraction :math:`a_{ij} b_{ij}`. + """ xp = array_module(a) return xp.einsum("ij,ij->", a, b) @dispatch def time_delay_geocentric(detector1, detector2, ra, dec, time): - """""" + r""" + Calculate time delay between two detectors for a source direction. + + Parameters + ========== + detector1: array-like + 3D position vector of the first detector in meters. + detector2: array-like + 3D position vector of the second detector in meters. + ra: float or array-like + Right ascension of the source in radians. + dec: float or array-like + Declination of the source in radians. + time: float or array-like + GPS time of the observation. + + Returns + ======= + float or array-like + Time delay :math:`\Delta t = \hat{\omega} \cdot (\vec{d}_2 - \vec{d}_1) / c` + in seconds, where :math:`\hat{\omega}` is the unit vector to the + source and :math:`c` is the speed of light. + """ xp = array_module(detector1) gmst = greenwich_mean_sidereal_time(time) % (2 * xp.pi) speed_of_light = 299792458.0 @@ -172,14 +318,50 @@ def time_delay_geocentric(detector1, detector2, ra, dec, time): @dispatch def time_delay_from_geocenter(detector1, ra, dec, time): - """""" + r""" + Calculate time delay from geocenter to a detector. + + Parameters + ========== + detector1: array-like + 3D position vector of the detector in meters. + ra: float or array-like + Right ascension of the source in radians. + dec: float or array-like + Declination of the source in radians. + time: float or array-like + GPS time of the observation. + + Returns + ======= + float or array-like + Time delay :math:`\Delta t = \hat{\omega} \cdot \vec{d} / c` in + seconds, where :math:`\vec{d}` is the detector position and + :math:`c` is the speed of light. + """ xp = array_module(detector1) return time_delay_geocentric(detector1, xp.zeros(3), ra, dec, time) @dispatch def zenith_azimuth_to_theta_phi(zenith, azimuth, delta_x): - """""" + """ + Convert zenith/azimuth angles to theta/phi in a rotated frame. + + Parameters + ========== + zenith: float or array-like + Zenith angle in radians. + azimuth: float or array-like + Azimuth angle in radians. + delta_x: array-like + 3D vector defining the rotation frame. + + Returns + ======= + tuple of array-like + (theta, phi) angles in the rotated frame, both in radians. + """ xp = array_module(delta_x) omega_prime = xp.stack( [ From 5ffa0d28855554be86213cbb6c889ec2b7c79bae Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Thu, 14 May 2026 20:29:07 +0000 Subject: [PATCH 113/140] Fix precommits --- bilby/compat/patches.py | 2 +- bilby/compat/utils.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bilby/compat/patches.py b/bilby/compat/patches.py index 8df16d52c..b5b49f1df 100644 --- a/bilby/compat/patches.py +++ b/bilby/compat/patches.py @@ -19,7 +19,7 @@ def multivariate_logpdf(xp, mean, cov): cov: array-like A two-dimensional array providing the covariance matrix of the distribution. - + Returns ======= logpdf: callable diff --git a/bilby/compat/utils.py b/bilby/compat/utils.py index f6b74c071..f6cd73387 100644 --- a/bilby/compat/utils.py +++ b/bilby/compat/utils.py @@ -30,7 +30,7 @@ def array_module(arr): - Builtin scalars: default to numpy - Pandas objects: default to numpy (treated as numpy-compatible) - Unknown types: log a warning and default to numpy - + This is a best-effort function, but will not cover all possible edge cases. Parameters From 618ca4f81887c222a682cac41d7cc64d333dd62e Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Fri, 15 May 2026 13:40:41 +0000 Subject: [PATCH 114/140] MAINT: Don't track uv lock file --- .gitignore | 1 + uv.lock | 3616 ---------------------------------------------------- 2 files changed, 1 insertion(+), 3616 deletions(-) delete mode 100644 uv.lock diff --git a/.gitignore b/.gitignore index 437f5654e..6ac816d92 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ MANIFEST **/outdir .idea/* bilby/_version.py +uv.lock diff --git a/uv.lock b/uv.lock deleted file mode 100644 index ce9c68897..000000000 --- a/uv.lock +++ /dev/null @@ -1,3616 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.10" -resolution-markers = [ - "python_full_version >= '3.14' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'win32'", - "python_full_version == '3.12.*' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version == '3.12.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version < '3.11'", -] - -[[package]] -name = "array-api-compat" -version = "1.14.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/89/e5/9a12dd1c2b0ad61f3c3ad0fc14b888c65fd735dd9d26805f77317303cbe5/array_api_compat-1.14.0.tar.gz", hash = "sha256:c819ba707f5c507800cb545f7e6348ff1ecc46538381d9ad9b371ffc9cd6d784", size = 106369, upload-time = "2026-02-26T12:02:42.452Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/d3/54cd560804a8c2b898824778e86c13c2a14600bc83532a9c4f69f2f469c3/array_api_compat-1.14.0-py3-none-any.whl", hash = "sha256:ed5af1f9b6595a199c942505f281ec994892556b6efc24679a0501e87a7d6279", size = 60124, upload-time = "2026-02-26T12:02:41.127Z" }, -] - -[[package]] -name = "array-api-extra" -version = "0.9.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -dependencies = [ - { name = "array-api-compat", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/58/a0/571d22e229aad80c4d200914db5218e7dae399aff097b34e8671c2fd6ff3/array_api_extra-0.9.2.tar.gz", hash = "sha256:630013895e6d1cb411ddb9a4a7aa9d3861281a448caaf4ce7a60f4284708d236", size = 87176, upload-time = "2026-01-03T13:37:45.703Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/21/2b/bfa1cfe370dd4ed51f834f2c6ad93b7f6263b83615ab96ad91094cc98ec6/array_api_extra-0.9.2-py3-none-any.whl", hash = "sha256:d0643a9a4e981746057649accad068ca0fe4066d890f6a95d8b4cd5131b3b661", size = 51056, upload-time = "2026-01-03T13:37:44.252Z" }, -] - -[[package]] -name = "array-api-extra" -version = "0.10.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'win32'", - "python_full_version == '3.12.*' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version == '3.12.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", -] -dependencies = [ - { name = "array-api-compat", marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/25/77/2ef35219b7cf612366da9b26c95307d8b3b0bb0c0e9767314554a15f4117/array_api_extra-0.10.1.tar.gz", hash = "sha256:712ea45bd122d51c05085fc5fa30cdf66a10cc5d1af81d3e7beaff620ef1e1f2", size = 89510, upload-time = "2026-02-27T17:14:47.06Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/46/f7/9e14be985fd77ae26fee9136c9735e8987772e0ecf5f1f4e6e2b84cadc46/array_api_extra-0.10.1-py3-none-any.whl", hash = "sha256:9c2003079ccd2a0c92b1cf797b5867b9d7ea9428e75f70c7f78c1c0842d54368", size = 88377, upload-time = "2026-02-27T17:14:45.422Z" }, -] - -[[package]] -name = "astropy" -version = "6.1.7" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -dependencies = [ - { name = "astropy-iers-data", marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "packaging", marker = "python_full_version < '3.11'" }, - { name = "pyerfa", marker = "python_full_version < '3.11'" }, - { name = "pyyaml", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8a/f8/9c6675ab4c646b95aae2762d108f6be4504033d91bd50da21daa62cab5ce/astropy-6.1.7.tar.gz", hash = "sha256:a405ac186306b6cb152e6df2f7444ab8bd764e4127d7519da1b3ae4dd65357ef", size = 7063411, upload-time = "2024-11-22T21:22:34.373Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/4f/27f91eb9cdaa37835e52496dcad00fd89969ef5154795697987d031d0605/astropy-6.1.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:be954c5f7707a089609053665aeb76493b79e5c4753c39486761bc6d137bf040", size = 6531137, upload-time = "2024-11-22T21:21:30.502Z" }, - { url = "https://files.pythonhosted.org/packages/4b/f2/fb2c6c1d31c21df0d4409ecd5e9788795be6f8f80b67008c8191488d55cf/astropy-6.1.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b5e48df5ab2e3e521e82a7233a4b1159d071e64e6cbb76c45415dc68d3b97af1", size = 6410376, upload-time = "2024-11-22T21:21:32.986Z" }, - { url = "https://files.pythonhosted.org/packages/fd/68/65ad3ea77440df2e8625d8fee585d5fc6049f33a61e49221f91d8de0e3df/astropy-6.1.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55c78252633c644361e2f7092d71f80ef9c2e6649f08d97711d9f19af514aedc", size = 9892774, upload-time = "2024-11-22T21:21:34.823Z" }, - { url = "https://files.pythonhosted.org/packages/b4/41/e366fc5baff41f7b433f07a46c053a24459e93d2912690d099f0eefabfc3/astropy-6.1.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:985e5e74489d23f1a11953b6b283fccde3f46cb6c68fee4f7228e5f6d8350ba9", size = 9962419, upload-time = "2024-11-22T21:21:36.863Z" }, - { url = "https://files.pythonhosted.org/packages/1e/a0/e6c1ef80f7e20fb600b3af742d227e6356704dbda3763ff1d76a53a0fd7b/astropy-6.1.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:dc2ea28ed41a3d92c39b1481d9c5be016ae58d68f144f3fd8cecffe503525bab", size = 9987760, upload-time = "2024-11-22T21:21:39.736Z" }, - { url = "https://files.pythonhosted.org/packages/49/93/6b23e75d690763a9d702038c74ea9a74181a278fe362fbeecea35b691e4a/astropy-6.1.7-cp310-cp310-win32.whl", hash = "sha256:4e4badadd8dfa5dca08fd86e9a50a3a91af321975859f5941579e6b7ce9ba199", size = 6274137, upload-time = "2024-11-22T21:21:42.178Z" }, - { url = "https://files.pythonhosted.org/packages/6e/e1/af92dc2132547e3998476a4b0ab19d15c50d8ec1d85e658fe6503e125fd1/astropy-6.1.7-cp310-cp310-win_amd64.whl", hash = "sha256:8d7f6727689288ee08fc0a4a297fc7e8089d01718321646bd00fea0906ad63dc", size = 6394810, upload-time = "2024-11-22T21:21:43.843Z" }, - { url = "https://files.pythonhosted.org/packages/4f/5e/d31204823764f6e5fa4820c1b4f49f8eef7cf691b796ec389f41b4f5a699/astropy-6.1.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:09edca01276ee63f7b2ff511da9bfb432068ba3242e27ef27d76e5a171087b7e", size = 6531221, upload-time = "2024-11-22T21:21:46.2Z" }, - { url = "https://files.pythonhosted.org/packages/22/e2/ae5dd6d9272e41619d85df4e4a03cf06acea8bcb44c42fe67e5cd04ae131/astropy-6.1.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:072f62a67992393beb016dc80bee8fb994fda9aa69e945f536ed8ac0e51291e6", size = 6409477, upload-time = "2024-11-22T21:21:47.862Z" }, - { url = "https://files.pythonhosted.org/packages/01/ed/9bc17beb457943ee04b8c85614ddb4a64a4a91597340dca28332e112209d/astropy-6.1.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2706156d3646f9c9a7fc810475d8ab0df4c717beefa8326552576a0f8ddca20", size = 10150734, upload-time = "2024-11-22T21:21:49.847Z" }, - { url = "https://files.pythonhosted.org/packages/39/38/1c5263f0d775def518707ccd1cf9d4df1d99d523fc148df9e38aa5ba9d54/astropy-6.1.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fcd99e627692f8e58bb3097d330bfbd109a22e00dab162a67f203b0a0601ad2c", size = 10210679, upload-time = "2024-11-22T21:21:52.011Z" }, - { url = "https://files.pythonhosted.org/packages/32/d1/7365e16b0158f755977a5bdbd329df40a9772b0423a1d5075aba9246673f/astropy-6.1.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b0ebbcb637b2e9bcb73011f2b7890d7a3f5a41b66ccaad7c28f065e81e28f0b2", size = 10245960, upload-time = "2024-11-22T21:21:54.785Z" }, - { url = "https://files.pythonhosted.org/packages/e9/b6/4dc6f9ef1c17738b8ebd8922bc1c6fec48542ccfe5124b6719737b012b8c/astropy-6.1.7-cp311-cp311-win32.whl", hash = "sha256:192b12ede49cd828362ab1a6ede2367fe203f4d851804ec22fa92e009a524281", size = 6272124, upload-time = "2024-11-22T21:21:57.504Z" }, - { url = "https://files.pythonhosted.org/packages/ba/c6/b5f33597bfbc1afad0640b20000633127dfa0a4295b607a0439f45546d9a/astropy-6.1.7-cp311-cp311-win_amd64.whl", hash = "sha256:3cac64bcdf570c947019bd2bc96711eeb2c7763afe192f18c9551e52a6c296b2", size = 6396627, upload-time = "2024-11-22T21:21:59.913Z" }, - { url = "https://files.pythonhosted.org/packages/46/2b/007c888fead170c714ecdcf56bc59e8d3252776bd3f16e1797158a46f65d/astropy-6.1.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f2a8bcbb1306052cc38c9eed2c9331bfafe2582b499a7321946abf74b26eb256", size = 6535604, upload-time = "2024-11-22T21:22:02.515Z" }, - { url = "https://files.pythonhosted.org/packages/8e/4c/cc30c9b1440f4a2f1f52845873ae3f8f7c4343261e516603a35546574ed7/astropy-6.1.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:eaf88878684f9d31aff36475c90d101f4cff22fdd4fd50098d9950fd56994df7", size = 6415117, upload-time = "2024-11-22T21:22:04.484Z" }, - { url = "https://files.pythonhosted.org/packages/12/2d/9985b8b4225c2495c4e64713d1630937c83af863db606d12676b72b4f651/astropy-6.1.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb8cd231e53556e4eebe0393ea95a8cea6b2ff4187c95ac4ff8b17e7a8da823", size = 10177861, upload-time = "2024-11-22T21:22:06.043Z" }, - { url = "https://files.pythonhosted.org/packages/b7/b6/63ccb085757638d15f0f9d6f2dffaccce7785236fe8bf23e4b380a333ce0/astropy-6.1.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ad36334d138a4f71d6fdcf225a98ad1dad6c343da4362d5a47a71f5c9da3ca9", size = 10258014, upload-time = "2024-11-22T21:22:08.164Z" }, - { url = "https://files.pythonhosted.org/packages/c8/ee/a6af891802de463f70e3fddf09f3aeb1d46dde87885e2245d25a2ac46948/astropy-6.1.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dd731c526869d0c68507be7b31dd10871b7c44d310bb5495476505560c83cd33", size = 10277363, upload-time = "2024-11-22T21:22:10.331Z" }, - { url = "https://files.pythonhosted.org/packages/dd/98/b253583f9de7033f03a7c5f5314b9e93177725a2020e0f36d338d242bf0e/astropy-6.1.7-cp312-cp312-win32.whl", hash = "sha256:662bacd7ae42561e038cbd85eea3b749308cf3575611a745b60f034d3350c97a", size = 6271741, upload-time = "2024-11-22T21:22:12.696Z" }, - { url = "https://files.pythonhosted.org/packages/7a/63/e1b5f01e6735ed8f9d62d3eed5f226bc0ab516ab8558ffaccf6d4185f91d/astropy-6.1.7-cp312-cp312-win_amd64.whl", hash = "sha256:5b4d02a98a0bf91ff7fd4ef0bd0ecca83c9497338cb88b61ec9f971350688222", size = 6396352, upload-time = "2024-11-22T21:22:14.525Z" }, - { url = "https://files.pythonhosted.org/packages/73/9d/21d2e61080a81e7e1f5e5006204a76e70588aa1a88aa9044c2d203578d07/astropy-6.1.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fbeaf04427987c0c6fa2e579eb40011802b06fba6b3a7870e082d5c693564e1b", size = 6528360, upload-time = "2024-11-22T21:22:16.275Z" }, - { url = "https://files.pythonhosted.org/packages/d5/3e/b999ec6cd607c512e66d8a138443361eb88899760c7cb8517a66155732ee/astropy-6.1.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ab6e88241a14185b9404b02246329185b70292984aa0616b20a0628dfe4f4ebb", size = 6407905, upload-time = "2024-11-22T21:22:18.035Z" }, - { url = "https://files.pythonhosted.org/packages/db/2d/44557c63688c2ed03d0d72b4f27fc30fc1ea250aeb5ebd939796c5f98bee/astropy-6.1.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0529c75565feaabb629946806b4763ae7b02069aeff4c3b56a69e8a9e638500", size = 10106849, upload-time = "2024-11-22T21:22:20.393Z" }, - { url = "https://files.pythonhosted.org/packages/66/bc/993552eb932dec528fe6b95f511e918473ea4406dee4b17c223f3fd8a919/astropy-6.1.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c5ec347631da77573fc729ba04e5d89a3bc94500bf6037152a2d0f9965ae1ce", size = 10194766, upload-time = "2024-11-22T21:22:23.116Z" }, - { url = "https://files.pythonhosted.org/packages/9f/f3/3c5282762c8a5746e7752e46a1e328c79a5d0186d96cfd0995bdf976e1f9/astropy-6.1.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dc496f87aaccaa5c6624acc985b8770f039c5bbe74b120c8ed7bad3698e24e1b", size = 10219291, upload-time = "2024-11-22T21:22:26.079Z" }, - { url = "https://files.pythonhosted.org/packages/d8/52/949bb79df9c03f56d0ae93ac62f2616fe3e67db51677bf412473bf6d077e/astropy-6.1.7-cp313-cp313-win32.whl", hash = "sha256:b1e01d534383c038dbf8664b964fa4ea818c7419318830d3c732c750c64115c6", size = 6269501, upload-time = "2024-11-22T21:22:28.468Z" }, - { url = "https://files.pythonhosted.org/packages/a1/da/f369561a67061dd42e13c7f758b393ae90319dbbcf7e301a18ce3fa43ec6/astropy-6.1.7-cp313-cp313-win_amd64.whl", hash = "sha256:af08cf2b0368f1ea585eb26a55d99a2de9e9b0bd30aba84b5329059c3ec33590", size = 6393207, upload-time = "2024-11-22T21:22:31.797Z" }, -] - -[[package]] -name = "astropy" -version = "7.2.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'win32'", - "python_full_version == '3.12.*' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version == '3.12.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", -] -dependencies = [ - { name = "astropy-iers-data", marker = "python_full_version >= '3.11'" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "packaging", marker = "python_full_version >= '3.11'" }, - { name = "pyerfa", marker = "python_full_version >= '3.11'" }, - { name = "pyyaml", marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7b/92/2dce2d48347efc3346d08ca7995b152d242ebd170c571f7c9346468d8427/astropy-7.2.0.tar.gz", hash = "sha256:ae48bc26b1feaeb603cd94bd1fa1aa39137a115fe931b7f13787ab420e8c3070", size = 7057774, upload-time = "2025-11-25T22:36:41.916Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b4/6d/6330a844bad8dfc4875e0f2fa1db1fee87837ba9805aa8a8d048c071363a/astropy-7.2.0-cp311-abi3-macosx_10_9_x86_64.whl", hash = "sha256:efac04df4cc488efe630c2fff1992d6516dfb16a06e197fb68bc9e8e3b85def1", size = 6442332, upload-time = "2025-11-25T22:36:23.6Z" }, - { url = "https://files.pythonhosted.org/packages/a6/ba/3418133ba144dfcd1530bca5a6b695f4cdd21a8abaaa2ac4e5450d11b028/astropy-7.2.0-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:52e9a7d9c86b21f1af911a2930cd0c4a275fb302d455c89e11eedaffef6f2ad0", size = 6413656, upload-time = "2025-11-25T22:36:26.548Z" }, - { url = "https://files.pythonhosted.org/packages/be/ba/05e43b5a7d738316a097fa78524d3eaaff5986294b4a052d4adb3c45e7c0/astropy-7.2.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97c370421b9bb13d4c762c7af06d172bad7c01bd5bcf88314f6913c3c235b770", size = 9758867, upload-time = "2025-11-25T22:36:28.661Z" }, - { url = "https://files.pythonhosted.org/packages/c3/1c/f06ad85180e7dd9855aa5ede901bfc2be858d7bee17d4e978a14c0ecec14/astropy-7.2.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2f39ce2c80211fbceb005d377a5478cd0d66c42aa1498d252f2239fe5a025c24", size = 9789007, upload-time = "2025-11-25T22:36:31.063Z" }, - { url = "https://files.pythonhosted.org/packages/f8/fb/e4d35194a5009d7a73333079481a4ef1380a255d67b9c1db578151a5fb50/astropy-7.2.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ad4d71db994d45f046a1a5449000cf0f88ab6367cb67658500654a0586d6ab19", size = 9748547, upload-time = "2025-11-25T22:36:33.154Z" }, - { url = "https://files.pythonhosted.org/packages/36/ea/f990730978ae0a7a34705f885d2f3806928c5f0bc22eefd6a1a23539cc32/astropy-7.2.0-cp311-abi3-win32.whl", hash = "sha256:95161f26602433176483e8bde8ab1a8ca09148f5b4bf5190569a26d381091598", size = 6237228, upload-time = "2025-11-25T22:36:35.236Z" }, - { url = "https://files.pythonhosted.org/packages/ec/bc/f4378f586dd63902c37d16f68f35f7d555b3b32e08ac6b1d633eb0a48805/astropy-7.2.0-cp311-abi3-win_amd64.whl", hash = "sha256:dc7c340ba1713e55c93071b32033f3153470a0f663a4d539c03a7c9b44020790", size = 6362868, upload-time = "2025-11-25T22:36:37.784Z" }, - { url = "https://files.pythonhosted.org/packages/77/79/b6d4bf01913cfd4ce0cd4c1be5916beccdb92b2970bab8c827984231eae6/astropy-7.2.0-cp311-abi3-win_arm64.whl", hash = "sha256:0c428735a3f15b05c2095bc6ccb5f98a64bc99fb7015866af19ff8492420ddaf", size = 6221756, upload-time = "2025-11-25T22:36:39.852Z" }, -] - -[[package]] -name = "astropy-iers-data" -version = "0.2026.5.11.1.8.52" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1e/a1/186ae52e05fe90cb9a02f6fa27201e066b91d951fe6d32cd5220def2f1dd/astropy_iers_data-0.2026.5.11.1.8.52.tar.gz", hash = "sha256:11d7d6caa141d1ab14196a645672989f25014cbf97d97940c407f278e26b400d", size = 1933045, upload-time = "2026-05-11T01:09:38.884Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/77/22a4fd8774e04aa2599fe02637b3cea107ac5234745ae91dbd3e7a8349dd/astropy_iers_data-0.2026.5.11.1.8.52-py3-none-any.whl", hash = "sha256:40c449c35bd8deabc20053f024f3d4c0cbea2947adf620fcbb6f4242b55e2090", size = 1989748, upload-time = "2026-05-11T01:09:36.91Z" }, -] - -[[package]] -name = "attrs" -version = "26.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, -] - -[[package]] -name = "beartype" -version = "0.22.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/94/1009e248bbfbab11397abca7193bea6626806be9a327d399810d523a07cb/beartype-0.22.9.tar.gz", hash = "sha256:8f82b54aa723a2848a56008d18875f91c1db02c32ef6a62319a002e3e25a975f", size = 1608866, upload-time = "2025-12-13T06:50:30.72Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/71/cc/18245721fa7747065ab478316c7fea7c74777d07f37ae60db2e84f8172e8/beartype-0.22.9-py3-none-any.whl", hash = "sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2", size = 1333658, upload-time = "2025-12-13T06:50:28.266Z" }, -] - -[[package]] -name = "bilby" -source = { editable = "." } -dependencies = [ - { name = "array-api-compat" }, - { name = "array-api-extra", version = "0.9.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "array-api-extra", version = "0.10.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "attrs" }, - { name = "corner" }, - { name = "dill" }, - { name = "dynesty" }, - { name = "emcee" }, - { name = "h5py" }, - { name = "matplotlib" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "pandas", version = "3.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "plum-dispatch" }, - { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "tqdm" }, -] - -[package.optional-dependencies] -all = [ - { name = "astropy", version = "6.1.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "astropy", version = "7.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "celerite" }, - { name = "george" }, - { name = "glasflow" }, - { name = "gwpy", version = "3.0.14", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "gwpy", version = "4.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "interpax" }, - { name = "jax", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "jax", version = "0.10.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "lalsuite" }, - { name = "parameterized" }, - { name = "plotly" }, - { name = "pyfftw", version = "0.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "pyfftw", version = "0.15.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "pytest-requires" }, - { name = "pytest-rerunfailures" }, - { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scikit-learn", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -gw = [ - { name = "astropy", version = "6.1.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "astropy", version = "7.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "gwpy", version = "3.0.14", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "gwpy", version = "4.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "lalsuite" }, - { name = "pyfftw", version = "0.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "pyfftw", version = "0.15.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scikit-learn", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -jax = [ - { name = "interpax" }, - { name = "jax", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "jax", version = "0.10.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -mcmc = [ - { name = "glasflow" }, - { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scikit-learn", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] - -[package.metadata] -requires-dist = [ - { name = "array-api-compat", specifier = ">=1.13" }, - { name = "array-api-extra" }, - { name = "astropy", marker = "extra == 'all'", specifier = ">=5" }, - { name = "astropy", marker = "extra == 'gw'", specifier = ">=5" }, - { name = "attrs" }, - { name = "celerite", marker = "extra == 'all'" }, - { name = "corner" }, - { name = "dill" }, - { name = "dynesty", specifier = ">=2.0.1" }, - { name = "emcee" }, - { name = "george", marker = "extra == 'all'" }, - { name = "glasflow", marker = "extra == 'all'" }, - { name = "glasflow", marker = "extra == 'mcmc'" }, - { name = "gwpy", marker = "extra == 'all'" }, - { name = "gwpy", marker = "extra == 'gw'" }, - { name = "h5py" }, - { name = "interpax", marker = "extra == 'all'" }, - { name = "interpax", marker = "extra == 'jax'" }, - { name = "jax", marker = "extra == 'all'" }, - { name = "jax", marker = "extra == 'jax'" }, - { name = "lalsuite", marker = "extra == 'all'" }, - { name = "lalsuite", marker = "extra == 'gw'" }, - { name = "matplotlib" }, - { name = "numpy" }, - { name = "pandas" }, - { name = "parameterized", marker = "extra == 'all'" }, - { name = "plotly", marker = "extra == 'all'" }, - { name = "plum-dispatch" }, - { name = "pyfftw", marker = "extra == 'all'" }, - { name = "pyfftw", marker = "extra == 'gw'" }, - { name = "pytest-requires", marker = "extra == 'all'" }, - { name = "pytest-rerunfailures", marker = "extra == 'all'" }, - { name = "scikit-learn", marker = "extra == 'all'" }, - { name = "scikit-learn", marker = "extra == 'gw'" }, - { name = "scikit-learn", marker = "extra == 'mcmc'" }, - { name = "scipy", specifier = ">=1.5" }, - { name = "tqdm" }, -] -provides-extras = ["all", "gw", "jax", "mcmc"] - -[[package]] -name = "celerite" -version = "0.4.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/33/7a/37e380a05055e730b38698bf5ec456bee6494936edb0dc96bb8e4dd5a0d9/celerite-0.4.3.tar.gz", hash = "sha256:aa8b487f57a7cc9793c4652b19b2df090beaa849730b50805f030694e94d0f6d", size = 14186766, upload-time = "2024-06-22T15:01:16.3Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/43/9bb3557b653113711341f7cee7deaf9c826f056dc9a54c8b77fb77de5629/celerite-0.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c433697026c144b3968342de380d30e5871e3dcb8f0c7db17e59786d67d943fb", size = 335537, upload-time = "2024-06-22T15:00:40.571Z" }, - { url = "https://files.pythonhosted.org/packages/bb/d2/b0005aa039765cb7aa0e07fa0bd29d2188a6fe54122d454a36db242fddc7/celerite-0.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3be153dc1a06948dcc35b394455af68b03e39b7bbfa5835d4cf62d040c090b4a", size = 302843, upload-time = "2024-06-22T15:00:42.884Z" }, - { url = "https://files.pythonhosted.org/packages/fb/e5/04b52438da6af7f6c0a4019ac800e0bccff180b291db4e4fe8cbbbf2a9cb/celerite-0.4.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2f1377789600a9fd66962d38fd4b35df255205512a2d5d6171d8569aa104fb5", size = 307963, upload-time = "2024-06-22T15:00:44.98Z" }, - { url = "https://files.pythonhosted.org/packages/54/f8/a02650ea97e072a9796b6fd7dcf362271366cf198b1b61f910000c87b893/celerite-0.4.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:845db1bd018a5c03399385aa5a8b1f5913aaaa613d51e4d8ba1092a84c4b73c5", size = 1302582, upload-time = "2024-06-22T15:00:47.111Z" }, - { url = "https://files.pythonhosted.org/packages/13/40/c2bf5ffa5d792c770e8b90d2254c1fdfa5f554b185f00ad8bc21bd893fdc/celerite-0.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:c06643359d5f2d02429c151d2fc567a8bea1e1f2f7fe2a1050729c9de843231a", size = 348900, upload-time = "2024-06-22T15:00:48.917Z" }, - { url = "https://files.pythonhosted.org/packages/3d/ee/381bc81913ee9eda49d5f894b2a677af8a8f918806a8be1b05fe320c86b6/celerite-0.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:702646a2fd7664648dcd126f0ecbefdacec16d2ff06b3b98b14f5321c1184af0", size = 337120, upload-time = "2024-06-22T15:00:50.949Z" }, - { url = "https://files.pythonhosted.org/packages/13/53/948a764200e8ccbda5f8253d969c49d0d0a7ea84047ae8d1cf564d918e41/celerite-0.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:383532f043a11a25c5d3889169273db3f8ef33026c86397bafebbedd7e013dfd", size = 304082, upload-time = "2024-06-22T15:00:52.297Z" }, - { url = "https://files.pythonhosted.org/packages/84/a1/e9d6558194f95f49c95bc0e1e9f1d2a08c835db73c64709b163ca0bd67b3/celerite-0.4.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a8003db2e725f93844ca40acb2919290b9b6b20395abeb3d252ccd275e18ab8", size = 309193, upload-time = "2024-06-22T15:00:54.534Z" }, - { url = "https://files.pythonhosted.org/packages/ac/80/5c73134c9dd72ad7ffd0e6e5d82605f7c9a40654164afa232ffd8b92efce/celerite-0.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:433c3a3ccc52afbcb360b66ad4b501bfe06d9b81e75cfd7495680640ddbbc45d", size = 1304175, upload-time = "2024-06-22T15:00:56.55Z" }, - { url = "https://files.pythonhosted.org/packages/3b/5c/cc1a5de3bf7340cf352d3ef4eae0a214eb3663525410bd0ecc44ae040e4d/celerite-0.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:760c9624d65a524da3ed04dedcab6a964310306faefcce20e94da0034964df5a", size = 349808, upload-time = "2024-06-22T15:00:58.082Z" }, - { url = "https://files.pythonhosted.org/packages/41/ab/a9522f03d8a92d989119e10266c77707ee6a61a24cc84ebccb26317b2dfe/celerite-0.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1e9a68739f1e051401a0bf40e18779563965f22ca36a2f10dfe58e7d31f71bbf", size = 335309, upload-time = "2024-06-22T15:00:59.381Z" }, - { url = "https://files.pythonhosted.org/packages/79/68/3f48c2b7232dcd752eb89dedb6b3d666fecd496b033f040691f73836ab6f/celerite-0.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68ac010dc3990d3339e1c7c35536b92ecc3d5a6bad2ee9add8646b241a290ce8", size = 302572, upload-time = "2024-06-22T15:01:00.71Z" }, - { url = "https://files.pythonhosted.org/packages/af/cf/06d4fc481b9678768c5f49fc89e8aff41700152fab5a8c6d9e029902dfb6/celerite-0.4.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ab8b71513fba2bf54b8a152d21071efc1282c9ae69a1fc193fa077a7997befd", size = 309164, upload-time = "2024-06-22T15:01:02.282Z" }, - { url = "https://files.pythonhosted.org/packages/14/1b/7c62ea4f0912026c6c7791dadcbc999eb94281a22db44bcc248958dda479/celerite-0.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a1ab89af0f99aaafc1f7f26d103c679410eb0698986f92309d67cadbb7063a9e", size = 1304095, upload-time = "2024-06-22T15:01:04.347Z" }, - { url = "https://files.pythonhosted.org/packages/a0/e0/2333cbc6c8a34da9fea8d471dae63447d38d31b06d1db3150482a1934779/celerite-0.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:1fa4e44a6a12941e1ff0cc6a8d0ce6a036ebfd253337705cb81c6bff529b29a0", size = 350091, upload-time = "2024-06-22T15:01:06.457Z" }, -] - -[[package]] -name = "certifi" -version = "2026.4.22" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/25/ee/6caf7a40c36a1220410afe15a1cc64993a1f864871f698c0f93acb72842a/certifi-2026.4.22.tar.gz", hash = "sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580", size = 137077, upload-time = "2026-04-22T11:26:11.191Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/22/30/7cd8fdcdfbc5b869528b079bfb76dcdf6056b1a2097a662e5e8c04f42965/certifi-2026.4.22-py3-none-any.whl", hash = "sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a", size = 135707, upload-time = "2026-04-22T11:26:09.372Z" }, -] - -[[package]] -name = "cffi" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pycparser", marker = "implementation_name != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283, upload-time = "2025-09-08T23:22:08.01Z" }, - { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504, upload-time = "2025-09-08T23:22:10.637Z" }, - { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811, upload-time = "2025-09-08T23:22:12.267Z" }, - { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402, upload-time = "2025-09-08T23:22:13.455Z" }, - { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217, upload-time = "2025-09-08T23:22:14.596Z" }, - { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079, upload-time = "2025-09-08T23:22:15.769Z" }, - { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475, upload-time = "2025-09-08T23:22:17.427Z" }, - { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829, upload-time = "2025-09-08T23:22:19.069Z" }, - { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211, upload-time = "2025-09-08T23:22:20.588Z" }, - { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036, upload-time = "2025-09-08T23:22:22.143Z" }, - { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184, upload-time = "2025-09-08T23:22:23.328Z" }, - { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790, upload-time = "2025-09-08T23:22:24.752Z" }, - { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" }, - { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" }, - { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" }, - { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" }, - { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" }, - { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" }, - { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" }, - { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" }, - { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" }, - { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" }, - { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" }, - { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" }, - { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" }, - { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, - { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, - { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, - { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, - { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, - { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, - { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, - { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, - { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, - { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, - { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, - { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, - { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, - { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, - { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, - { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, - { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, - { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, - { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, - { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, - { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, - { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, - { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, - { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, - { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, - { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, - { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, - { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, - { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, - { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, - { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, - { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, - { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, - { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, - { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, - { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, - { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, - { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, - { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, - { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/26/08/0f303cb0b529e456bb116f2d50565a482694fbb94340bf56d44677e7ed03/charset_normalizer-3.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d", size = 315182, upload-time = "2026-04-02T09:25:40.673Z" }, - { url = "https://files.pythonhosted.org/packages/24/47/b192933e94b546f1b1fe4df9cc1f84fcdbf2359f8d1081d46dd029b50207/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8", size = 209329, upload-time = "2026-04-02T09:25:42.354Z" }, - { url = "https://files.pythonhosted.org/packages/c2/b4/01fa81c5ca6141024d89a8fc15968002b71da7f825dd14113207113fabbd/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790", size = 231230, upload-time = "2026-04-02T09:25:44.281Z" }, - { url = "https://files.pythonhosted.org/packages/20/f7/7b991776844dfa058017e600e6e55ff01984a063290ca5622c0b63162f68/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc", size = 225890, upload-time = "2026-04-02T09:25:45.475Z" }, - { url = "https://files.pythonhosted.org/packages/20/e7/bed0024a0f4ab0c8a9c64d4445f39b30c99bd1acd228291959e3de664247/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393", size = 216930, upload-time = "2026-04-02T09:25:46.58Z" }, - { url = "https://files.pythonhosted.org/packages/e2/ab/b18f0ab31cdd7b3ddb8bb76c4a414aeb8160c9810fdf1bc62f269a539d87/charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153", size = 202109, upload-time = "2026-04-02T09:25:48.031Z" }, - { url = "https://files.pythonhosted.org/packages/82/e5/7e9440768a06dfb3075936490cb82dbf0ee20a133bf0dd8551fa096914ec/charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af", size = 214684, upload-time = "2026-04-02T09:25:49.245Z" }, - { url = "https://files.pythonhosted.org/packages/71/94/8c61d8da9f062fdf457c80acfa25060ec22bf1d34bbeaca4350f13bcfd07/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34", size = 212785, upload-time = "2026-04-02T09:25:50.671Z" }, - { url = "https://files.pythonhosted.org/packages/66/cd/6e9889c648e72c0ab2e5967528bb83508f354d706637bc7097190c874e13/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1", size = 203055, upload-time = "2026-04-02T09:25:51.802Z" }, - { url = "https://files.pythonhosted.org/packages/92/2e/7a951d6a08aefb7eb8e1b54cdfb580b1365afdd9dd484dc4bee9e5d8f258/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752", size = 232502, upload-time = "2026-04-02T09:25:53.388Z" }, - { url = "https://files.pythonhosted.org/packages/58/d5/abcf2d83bf8e0a1286df55cd0dc1d49af0da4282aa77e986df343e7de124/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53", size = 214295, upload-time = "2026-04-02T09:25:54.765Z" }, - { url = "https://files.pythonhosted.org/packages/47/3a/7d4cd7ed54be99973a0dc176032cba5cb1f258082c31fa6df35cff46acfc/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616", size = 227145, upload-time = "2026-04-02T09:25:55.904Z" }, - { url = "https://files.pythonhosted.org/packages/1d/98/3a45bf8247889cf28262ebd3d0872edff11565b2a1e3064ccb132db3fbb0/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a", size = 218884, upload-time = "2026-04-02T09:25:57.074Z" }, - { url = "https://files.pythonhosted.org/packages/ad/80/2e8b7f8915ed5c9ef13aa828d82738e33888c485b65ebf744d615040c7ea/charset_normalizer-3.4.7-cp310-cp310-win32.whl", hash = "sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374", size = 148343, upload-time = "2026-04-02T09:25:58.199Z" }, - { url = "https://files.pythonhosted.org/packages/35/1b/3b8c8c77184af465ee9ad88b5aea46ea6b2e1f7b9dc9502891e37af21e30/charset_normalizer-3.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943", size = 159174, upload-time = "2026-04-02T09:25:59.322Z" }, - { url = "https://files.pythonhosted.org/packages/be/c1/feb40dca40dbb21e0a908801782d9288c64fc8d8e562c2098e9994c8c21b/charset_normalizer-3.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008", size = 147805, upload-time = "2026-04-02T09:26:00.756Z" }, - { url = "https://files.pythonhosted.org/packages/c2/d7/b5b7020a0565c2e9fa8c09f4b5fa6232feb326b8c20081ccded47ea368fd/charset_normalizer-3.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7", size = 309705, upload-time = "2026-04-02T09:26:02.191Z" }, - { url = "https://files.pythonhosted.org/packages/5a/53/58c29116c340e5456724ecd2fff4196d236b98f3da97b404bc5e51ac3493/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7", size = 206419, upload-time = "2026-04-02T09:26:03.583Z" }, - { url = "https://files.pythonhosted.org/packages/b2/02/e8146dc6591a37a00e5144c63f29fb7c97a734ea8a111190783c0e60ab63/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e", size = 227901, upload-time = "2026-04-02T09:26:04.738Z" }, - { url = "https://files.pythonhosted.org/packages/fb/73/77486c4cd58f1267bf17db420e930c9afa1b3be3fe8c8b8ebbebc9624359/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c", size = 222742, upload-time = "2026-04-02T09:26:06.36Z" }, - { url = "https://files.pythonhosted.org/packages/a1/fa/f74eb381a7d94ded44739e9d94de18dc5edc9c17fb8c11f0a6890696c0a9/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df", size = 214061, upload-time = "2026-04-02T09:26:08.347Z" }, - { url = "https://files.pythonhosted.org/packages/dc/92/42bd3cefcf7687253fb86694b45f37b733c97f59af3724f356fa92b8c344/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265", size = 199239, upload-time = "2026-04-02T09:26:09.823Z" }, - { url = "https://files.pythonhosted.org/packages/4c/3d/069e7184e2aa3b3cddc700e3dd267413dc259854adc3380421c805c6a17d/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4", size = 210173, upload-time = "2026-04-02T09:26:10.953Z" }, - { url = "https://files.pythonhosted.org/packages/62/51/9d56feb5f2e7074c46f93e0ebdbe61f0848ee246e2f0d89f8e20b89ebb8f/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e", size = 209841, upload-time = "2026-04-02T09:26:12.142Z" }, - { url = "https://files.pythonhosted.org/packages/d2/59/893d8f99cc4c837dda1fe2f1139079703deb9f321aabcb032355de13b6c7/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38", size = 200304, upload-time = "2026-04-02T09:26:13.711Z" }, - { url = "https://files.pythonhosted.org/packages/7d/1d/ee6f3be3464247578d1ed5c46de545ccc3d3ff933695395c402c21fa6b77/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c", size = 229455, upload-time = "2026-04-02T09:26:14.941Z" }, - { url = "https://files.pythonhosted.org/packages/54/bb/8fb0a946296ea96a488928bdce8ef99023998c48e4713af533e9bb98ef07/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b", size = 210036, upload-time = "2026-04-02T09:26:16.478Z" }, - { url = "https://files.pythonhosted.org/packages/9a/bc/015b2387f913749f82afd4fcba07846d05b6d784dd16123cb66860e0237d/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c", size = 224739, upload-time = "2026-04-02T09:26:17.751Z" }, - { url = "https://files.pythonhosted.org/packages/17/ab/63133691f56baae417493cba6b7c641571a2130eb7bceba6773367ab9ec5/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d", size = 216277, upload-time = "2026-04-02T09:26:18.981Z" }, - { url = "https://files.pythonhosted.org/packages/06/6d/3be70e827977f20db77c12a97e6a9f973631a45b8d186c084527e53e77a4/charset_normalizer-3.4.7-cp311-cp311-win32.whl", hash = "sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad", size = 147819, upload-time = "2026-04-02T09:26:20.295Z" }, - { url = "https://files.pythonhosted.org/packages/20/d9/5f67790f06b735d7c7637171bbfd89882ad67201891b7275e51116ed8207/charset_normalizer-3.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00", size = 159281, upload-time = "2026-04-02T09:26:21.74Z" }, - { url = "https://files.pythonhosted.org/packages/ca/83/6413f36c5a34afead88ce6f66684d943d91f233d76dd083798f9602b75ae/charset_normalizer-3.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1", size = 147843, upload-time = "2026-04-02T09:26:22.901Z" }, - { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" }, - { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" }, - { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" }, - { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" }, - { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" }, - { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" }, - { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" }, - { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" }, - { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" }, - { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" }, - { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" }, - { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" }, - { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" }, - { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" }, - { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" }, - { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" }, - { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" }, - { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" }, - { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" }, - { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" }, - { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" }, - { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" }, - { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" }, - { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" }, - { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" }, - { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" }, - { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" }, - { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" }, - { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" }, - { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" }, - { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" }, - { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" }, - { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" }, - { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" }, - { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" }, - { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" }, - { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" }, - { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" }, - { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" }, - { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" }, - { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" }, - { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" }, - { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" }, - { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" }, - { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" }, - { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" }, - { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" }, - { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" }, - { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" }, - { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" }, - { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" }, - { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" }, - { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" }, - { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" }, - { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" }, - { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" }, - { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" }, - { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" }, - { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" }, - { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" }, - { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" }, - { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" }, - { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" }, - { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" }, - { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, -] - -[[package]] -name = "click" -version = "8.3.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bb/63/f9e1ea081ce35720d8b92acde70daaedace594dc93b693c869e0d5910718/click-8.3.3.tar.gz", hash = "sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2", size = 328061, upload-time = "2026-04-22T15:11:27.506Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/44/c1221527f6a71a01ec6fbad7fa78f1d50dfa02217385cf0fa3eec7087d59/click-8.3.3-py3-none-any.whl", hash = "sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613", size = 110502, upload-time = "2026-04-22T15:11:25.044Z" }, -] - -[[package]] -name = "colorama" -version = "0.4.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, -] - -[[package]] -name = "contourpy" -version = "1.3.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130, upload-time = "2025-04-15T17:47:53.79Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/12/a3/da4153ec8fe25d263aa48c1a4cbde7f49b59af86f0b6f7862788c60da737/contourpy-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba38e3f9f330af820c4b27ceb4b9c7feee5fe0493ea53a8720f4792667465934", size = 268551, upload-time = "2025-04-15T17:34:46.581Z" }, - { url = "https://files.pythonhosted.org/packages/2f/6c/330de89ae1087eb622bfca0177d32a7ece50c3ef07b28002de4757d9d875/contourpy-1.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc41ba0714aa2968d1f8674ec97504a8f7e334f48eeacebcaa6256213acb0989", size = 253399, upload-time = "2025-04-15T17:34:51.427Z" }, - { url = "https://files.pythonhosted.org/packages/c1/bd/20c6726b1b7f81a8bee5271bed5c165f0a8e1f572578a9d27e2ccb763cb2/contourpy-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9be002b31c558d1ddf1b9b415b162c603405414bacd6932d031c5b5a8b757f0d", size = 312061, upload-time = "2025-04-15T17:34:55.961Z" }, - { url = "https://files.pythonhosted.org/packages/22/fc/a9665c88f8a2473f823cf1ec601de9e5375050f1958cbb356cdf06ef1ab6/contourpy-1.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8d2e74acbcba3bfdb6d9d8384cdc4f9260cae86ed9beee8bd5f54fee49a430b9", size = 351956, upload-time = "2025-04-15T17:35:00.992Z" }, - { url = "https://files.pythonhosted.org/packages/25/eb/9f0a0238f305ad8fb7ef42481020d6e20cf15e46be99a1fcf939546a177e/contourpy-1.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e259bced5549ac64410162adc973c5e2fb77f04df4a439d00b478e57a0e65512", size = 320872, upload-time = "2025-04-15T17:35:06.177Z" }, - { url = "https://files.pythonhosted.org/packages/32/5c/1ee32d1c7956923202f00cf8d2a14a62ed7517bdc0ee1e55301227fc273c/contourpy-1.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad687a04bc802cbe8b9c399c07162a3c35e227e2daccf1668eb1f278cb698631", size = 325027, upload-time = "2025-04-15T17:35:11.244Z" }, - { url = "https://files.pythonhosted.org/packages/83/bf/9baed89785ba743ef329c2b07fd0611d12bfecbedbdd3eeecf929d8d3b52/contourpy-1.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cdd22595308f53ef2f891040ab2b93d79192513ffccbd7fe19be7aa773a5e09f", size = 1306641, upload-time = "2025-04-15T17:35:26.701Z" }, - { url = "https://files.pythonhosted.org/packages/d4/cc/74e5e83d1e35de2d28bd97033426b450bc4fd96e092a1f7a63dc7369b55d/contourpy-1.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b4f54d6a2defe9f257327b0f243612dd051cc43825587520b1bf74a31e2f6ef2", size = 1374075, upload-time = "2025-04-15T17:35:43.204Z" }, - { url = "https://files.pythonhosted.org/packages/0c/42/17f3b798fd5e033b46a16f8d9fcb39f1aba051307f5ebf441bad1ecf78f8/contourpy-1.3.2-cp310-cp310-win32.whl", hash = "sha256:f939a054192ddc596e031e50bb13b657ce318cf13d264f095ce9db7dc6ae81c0", size = 177534, upload-time = "2025-04-15T17:35:46.554Z" }, - { url = "https://files.pythonhosted.org/packages/54/ec/5162b8582f2c994721018d0c9ece9dc6ff769d298a8ac6b6a652c307e7df/contourpy-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c440093bbc8fc21c637c03bafcbef95ccd963bc6e0514ad887932c18ca2a759a", size = 221188, upload-time = "2025-04-15T17:35:50.064Z" }, - { url = "https://files.pythonhosted.org/packages/b3/b9/ede788a0b56fc5b071639d06c33cb893f68b1178938f3425debebe2dab78/contourpy-1.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6a37a2fb93d4df3fc4c0e363ea4d16f83195fc09c891bc8ce072b9d084853445", size = 269636, upload-time = "2025-04-15T17:35:54.473Z" }, - { url = "https://files.pythonhosted.org/packages/e6/75/3469f011d64b8bbfa04f709bfc23e1dd71be54d05b1b083be9f5b22750d1/contourpy-1.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b7cd50c38f500bbcc9b6a46643a40e0913673f869315d8e70de0438817cb7773", size = 254636, upload-time = "2025-04-15T17:35:58.283Z" }, - { url = "https://files.pythonhosted.org/packages/8d/2f/95adb8dae08ce0ebca4fd8e7ad653159565d9739128b2d5977806656fcd2/contourpy-1.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6658ccc7251a4433eebd89ed2672c2ed96fba367fd25ca9512aa92a4b46c4f1", size = 313053, upload-time = "2025-04-15T17:36:03.235Z" }, - { url = "https://files.pythonhosted.org/packages/c3/a6/8ccf97a50f31adfa36917707fe39c9a0cbc24b3bbb58185577f119736cc9/contourpy-1.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:70771a461aaeb335df14deb6c97439973d253ae70660ca085eec25241137ef43", size = 352985, upload-time = "2025-04-15T17:36:08.275Z" }, - { url = "https://files.pythonhosted.org/packages/1d/b6/7925ab9b77386143f39d9c3243fdd101621b4532eb126743201160ffa7e6/contourpy-1.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65a887a6e8c4cd0897507d814b14c54a8c2e2aa4ac9f7686292f9769fcf9a6ab", size = 323750, upload-time = "2025-04-15T17:36:13.29Z" }, - { url = "https://files.pythonhosted.org/packages/c2/f3/20c5d1ef4f4748e52d60771b8560cf00b69d5c6368b5c2e9311bcfa2a08b/contourpy-1.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3859783aefa2b8355697f16642695a5b9792e7a46ab86da1118a4a23a51a33d7", size = 326246, upload-time = "2025-04-15T17:36:18.329Z" }, - { url = "https://files.pythonhosted.org/packages/8c/e5/9dae809e7e0b2d9d70c52b3d24cba134dd3dad979eb3e5e71f5df22ed1f5/contourpy-1.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:eab0f6db315fa4d70f1d8ab514e527f0366ec021ff853d7ed6a2d33605cf4b83", size = 1308728, upload-time = "2025-04-15T17:36:33.878Z" }, - { url = "https://files.pythonhosted.org/packages/e2/4a/0058ba34aeea35c0b442ae61a4f4d4ca84d6df8f91309bc2d43bb8dd248f/contourpy-1.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d91a3ccc7fea94ca0acab82ceb77f396d50a1f67412efe4c526f5d20264e6ecd", size = 1375762, upload-time = "2025-04-15T17:36:51.295Z" }, - { url = "https://files.pythonhosted.org/packages/09/33/7174bdfc8b7767ef2c08ed81244762d93d5c579336fc0b51ca57b33d1b80/contourpy-1.3.2-cp311-cp311-win32.whl", hash = "sha256:1c48188778d4d2f3d48e4643fb15d8608b1d01e4b4d6b0548d9b336c28fc9b6f", size = 178196, upload-time = "2025-04-15T17:36:55.002Z" }, - { url = "https://files.pythonhosted.org/packages/5e/fe/4029038b4e1c4485cef18e480b0e2cd2d755448bb071eb9977caac80b77b/contourpy-1.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:5ebac872ba09cb8f2131c46b8739a7ff71de28a24c869bcad554477eb089a878", size = 222017, upload-time = "2025-04-15T17:36:58.576Z" }, - { url = "https://files.pythonhosted.org/packages/34/f7/44785876384eff370c251d58fd65f6ad7f39adce4a093c934d4a67a7c6b6/contourpy-1.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4caf2bcd2969402bf77edc4cb6034c7dd7c0803213b3523f111eb7460a51b8d2", size = 271580, upload-time = "2025-04-15T17:37:03.105Z" }, - { url = "https://files.pythonhosted.org/packages/93/3b/0004767622a9826ea3d95f0e9d98cd8729015768075d61f9fea8eeca42a8/contourpy-1.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:82199cb78276249796419fe36b7386bd8d2cc3f28b3bc19fe2454fe2e26c4c15", size = 255530, upload-time = "2025-04-15T17:37:07.026Z" }, - { url = "https://files.pythonhosted.org/packages/e7/bb/7bd49e1f4fa805772d9fd130e0d375554ebc771ed7172f48dfcd4ca61549/contourpy-1.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:106fab697af11456fcba3e352ad50effe493a90f893fca6c2ca5c033820cea92", size = 307688, upload-time = "2025-04-15T17:37:11.481Z" }, - { url = "https://files.pythonhosted.org/packages/fc/97/e1d5dbbfa170725ef78357a9a0edc996b09ae4af170927ba8ce977e60a5f/contourpy-1.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d14f12932a8d620e307f715857107b1d1845cc44fdb5da2bc8e850f5ceba9f87", size = 347331, upload-time = "2025-04-15T17:37:18.212Z" }, - { url = "https://files.pythonhosted.org/packages/6f/66/e69e6e904f5ecf6901be3dd16e7e54d41b6ec6ae3405a535286d4418ffb4/contourpy-1.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:532fd26e715560721bb0d5fc7610fce279b3699b018600ab999d1be895b09415", size = 318963, upload-time = "2025-04-15T17:37:22.76Z" }, - { url = "https://files.pythonhosted.org/packages/a8/32/b8a1c8965e4f72482ff2d1ac2cd670ce0b542f203c8e1d34e7c3e6925da7/contourpy-1.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b383144cf2d2c29f01a1e8170f50dacf0eac02d64139dcd709a8ac4eb3cfe", size = 323681, upload-time = "2025-04-15T17:37:33.001Z" }, - { url = "https://files.pythonhosted.org/packages/30/c6/12a7e6811d08757c7162a541ca4c5c6a34c0f4e98ef2b338791093518e40/contourpy-1.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c49f73e61f1f774650a55d221803b101d966ca0c5a2d6d5e4320ec3997489441", size = 1308674, upload-time = "2025-04-15T17:37:48.64Z" }, - { url = "https://files.pythonhosted.org/packages/2a/8a/bebe5a3f68b484d3a2b8ffaf84704b3e343ef1addea528132ef148e22b3b/contourpy-1.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3d80b2c0300583228ac98d0a927a1ba6a2ba6b8a742463c564f1d419ee5b211e", size = 1380480, upload-time = "2025-04-15T17:38:06.7Z" }, - { url = "https://files.pythonhosted.org/packages/34/db/fcd325f19b5978fb509a7d55e06d99f5f856294c1991097534360b307cf1/contourpy-1.3.2-cp312-cp312-win32.whl", hash = "sha256:90df94c89a91b7362e1142cbee7568f86514412ab8a2c0d0fca72d7e91b62912", size = 178489, upload-time = "2025-04-15T17:38:10.338Z" }, - { url = "https://files.pythonhosted.org/packages/01/c8/fadd0b92ffa7b5eb5949bf340a63a4a496a6930a6c37a7ba0f12acb076d6/contourpy-1.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:8c942a01d9163e2e5cfb05cb66110121b8d07ad438a17f9e766317bcb62abf73", size = 223042, upload-time = "2025-04-15T17:38:14.239Z" }, - { url = "https://files.pythonhosted.org/packages/2e/61/5673f7e364b31e4e7ef6f61a4b5121c5f170f941895912f773d95270f3a2/contourpy-1.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:de39db2604ae755316cb5967728f4bea92685884b1e767b7c24e983ef5f771cb", size = 271630, upload-time = "2025-04-15T17:38:19.142Z" }, - { url = "https://files.pythonhosted.org/packages/ff/66/a40badddd1223822c95798c55292844b7e871e50f6bfd9f158cb25e0bd39/contourpy-1.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3f9e896f447c5c8618f1edb2bafa9a4030f22a575ec418ad70611450720b5b08", size = 255670, upload-time = "2025-04-15T17:38:23.688Z" }, - { url = "https://files.pythonhosted.org/packages/1e/c7/cf9fdee8200805c9bc3b148f49cb9482a4e3ea2719e772602a425c9b09f8/contourpy-1.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71e2bd4a1c4188f5c2b8d274da78faab884b59df20df63c34f74aa1813c4427c", size = 306694, upload-time = "2025-04-15T17:38:28.238Z" }, - { url = "https://files.pythonhosted.org/packages/dd/e7/ccb9bec80e1ba121efbffad7f38021021cda5be87532ec16fd96533bb2e0/contourpy-1.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de425af81b6cea33101ae95ece1f696af39446db9682a0b56daaa48cfc29f38f", size = 345986, upload-time = "2025-04-15T17:38:33.502Z" }, - { url = "https://files.pythonhosted.org/packages/dc/49/ca13bb2da90391fa4219fdb23b078d6065ada886658ac7818e5441448b78/contourpy-1.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:977e98a0e0480d3fe292246417239d2d45435904afd6d7332d8455981c408b85", size = 318060, upload-time = "2025-04-15T17:38:38.672Z" }, - { url = "https://files.pythonhosted.org/packages/c8/65/5245ce8c548a8422236c13ffcdcdada6a2a812c361e9e0c70548bb40b661/contourpy-1.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:434f0adf84911c924519d2b08fc10491dd282b20bdd3fa8f60fd816ea0b48841", size = 322747, upload-time = "2025-04-15T17:38:43.712Z" }, - { url = "https://files.pythonhosted.org/packages/72/30/669b8eb48e0a01c660ead3752a25b44fdb2e5ebc13a55782f639170772f9/contourpy-1.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c66c4906cdbc50e9cba65978823e6e00b45682eb09adbb78c9775b74eb222422", size = 1308895, upload-time = "2025-04-15T17:39:00.224Z" }, - { url = "https://files.pythonhosted.org/packages/05/5a/b569f4250decee6e8d54498be7bdf29021a4c256e77fe8138c8319ef8eb3/contourpy-1.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8b7fc0cd78ba2f4695fd0a6ad81a19e7e3ab825c31b577f384aa9d7817dc3bef", size = 1379098, upload-time = "2025-04-15T17:43:29.649Z" }, - { url = "https://files.pythonhosted.org/packages/19/ba/b227c3886d120e60e41b28740ac3617b2f2b971b9f601c835661194579f1/contourpy-1.3.2-cp313-cp313-win32.whl", hash = "sha256:15ce6ab60957ca74cff444fe66d9045c1fd3e92c8936894ebd1f3eef2fff075f", size = 178535, upload-time = "2025-04-15T17:44:44.532Z" }, - { url = "https://files.pythonhosted.org/packages/12/6e/2fed56cd47ca739b43e892707ae9a13790a486a3173be063681ca67d2262/contourpy-1.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e1578f7eafce927b168752ed7e22646dad6cd9bca673c60bff55889fa236ebf9", size = 223096, upload-time = "2025-04-15T17:44:48.194Z" }, - { url = "https://files.pythonhosted.org/packages/54/4c/e76fe2a03014a7c767d79ea35c86a747e9325537a8b7627e0e5b3ba266b4/contourpy-1.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0475b1f6604896bc7c53bb070e355e9321e1bc0d381735421a2d2068ec56531f", size = 285090, upload-time = "2025-04-15T17:43:34.084Z" }, - { url = "https://files.pythonhosted.org/packages/7b/e2/5aba47debd55d668e00baf9651b721e7733975dc9fc27264a62b0dd26eb8/contourpy-1.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c85bb486e9be652314bb5b9e2e3b0d1b2e643d5eec4992c0fbe8ac71775da739", size = 268643, upload-time = "2025-04-15T17:43:38.626Z" }, - { url = "https://files.pythonhosted.org/packages/a1/37/cd45f1f051fe6230f751cc5cdd2728bb3a203f5619510ef11e732109593c/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:745b57db7758f3ffc05a10254edd3182a2a83402a89c00957a8e8a22f5582823", size = 310443, upload-time = "2025-04-15T17:43:44.522Z" }, - { url = "https://files.pythonhosted.org/packages/8b/a2/36ea6140c306c9ff6dd38e3bcec80b3b018474ef4d17eb68ceecd26675f4/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:970e9173dbd7eba9b4e01aab19215a48ee5dd3f43cef736eebde064a171f89a5", size = 349865, upload-time = "2025-04-15T17:43:49.545Z" }, - { url = "https://files.pythonhosted.org/packages/95/b7/2fc76bc539693180488f7b6cc518da7acbbb9e3b931fd9280504128bf956/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6c4639a9c22230276b7bffb6a850dfc8258a2521305e1faefe804d006b2e532", size = 321162, upload-time = "2025-04-15T17:43:54.203Z" }, - { url = "https://files.pythonhosted.org/packages/f4/10/76d4f778458b0aa83f96e59d65ece72a060bacb20cfbee46cf6cd5ceba41/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc829960f34ba36aad4302e78eabf3ef16a3a100863f0d4eeddf30e8a485a03b", size = 327355, upload-time = "2025-04-15T17:44:01.025Z" }, - { url = "https://files.pythonhosted.org/packages/43/a3/10cf483ea683f9f8ab096c24bad3cce20e0d1dd9a4baa0e2093c1c962d9d/contourpy-1.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d32530b534e986374fc19eaa77fcb87e8a99e5431499949b828312bdcd20ac52", size = 1307935, upload-time = "2025-04-15T17:44:17.322Z" }, - { url = "https://files.pythonhosted.org/packages/78/73/69dd9a024444489e22d86108e7b913f3528f56cfc312b5c5727a44188471/contourpy-1.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e298e7e70cf4eb179cc1077be1c725b5fd131ebc81181bf0c03525c8abc297fd", size = 1372168, upload-time = "2025-04-15T17:44:33.43Z" }, - { url = "https://files.pythonhosted.org/packages/0f/1b/96d586ccf1b1a9d2004dd519b25fbf104a11589abfd05484ff12199cca21/contourpy-1.3.2-cp313-cp313t-win32.whl", hash = "sha256:d0e589ae0d55204991450bb5c23f571c64fe43adaa53f93fc902a84c96f52fe1", size = 189550, upload-time = "2025-04-15T17:44:37.092Z" }, - { url = "https://files.pythonhosted.org/packages/b0/e6/6000d0094e8a5e32ad62591c8609e269febb6e4db83a1c75ff8868b42731/contourpy-1.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:78e9253c3de756b3f6a5174d024c4835acd59eb3f8e2ca13e775dbffe1558f69", size = 238214, upload-time = "2025-04-15T17:44:40.827Z" }, - { url = "https://files.pythonhosted.org/packages/33/05/b26e3c6ecc05f349ee0013f0bb850a761016d89cec528a98193a48c34033/contourpy-1.3.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fd93cc7f3139b6dd7aab2f26a90dde0aa9fc264dbf70f6740d498a70b860b82c", size = 265681, upload-time = "2025-04-15T17:44:59.314Z" }, - { url = "https://files.pythonhosted.org/packages/2b/25/ac07d6ad12affa7d1ffed11b77417d0a6308170f44ff20fa1d5aa6333f03/contourpy-1.3.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:107ba8a6a7eec58bb475329e6d3b95deba9440667c4d62b9b6063942b61d7f16", size = 315101, upload-time = "2025-04-15T17:45:04.165Z" }, - { url = "https://files.pythonhosted.org/packages/8f/4d/5bb3192bbe9d3f27e3061a6a8e7733c9120e203cb8515767d30973f71030/contourpy-1.3.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ded1706ed0c1049224531b81128efbd5084598f18d8a2d9efae833edbd2b40ad", size = 220599, upload-time = "2025-04-15T17:45:08.456Z" }, - { url = "https://files.pythonhosted.org/packages/ff/c0/91f1215d0d9f9f343e4773ba6c9b89e8c0cc7a64a6263f21139da639d848/contourpy-1.3.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5f5964cdad279256c084b69c3f412b7801e15356b16efa9d78aa974041903da0", size = 266807, upload-time = "2025-04-15T17:45:15.535Z" }, - { url = "https://files.pythonhosted.org/packages/d4/79/6be7e90c955c0487e7712660d6cead01fa17bff98e0ea275737cc2bc8e71/contourpy-1.3.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49b65a95d642d4efa8f64ba12558fcb83407e58a2dfba9d796d77b63ccfcaff5", size = 318729, upload-time = "2025-04-15T17:45:20.166Z" }, - { url = "https://files.pythonhosted.org/packages/87/68/7f46fb537958e87427d98a4074bcde4b67a70b04900cfc5ce29bc2f556c1/contourpy-1.3.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8c5acb8dddb0752bf252e01a3035b21443158910ac16a3b0d20e7fed7d534ce5", size = 221791, upload-time = "2025-04-15T17:45:24.794Z" }, -] - -[[package]] -name = "contourpy" -version = "1.3.3" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'win32'", - "python_full_version == '3.12.*' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version == '3.12.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", -] -dependencies = [ - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/91/2e/c4390a31919d8a78b90e8ecf87cd4b4c4f05a5b48d05ec17db8e5404c6f4/contourpy-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:709a48ef9a690e1343202916450bc48b9e51c049b089c7f79a267b46cffcdaa1", size = 288773, upload-time = "2025-07-26T12:01:02.277Z" }, - { url = "https://files.pythonhosted.org/packages/0d/44/c4b0b6095fef4dc9c420e041799591e3b63e9619e3044f7f4f6c21c0ab24/contourpy-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:23416f38bfd74d5d28ab8429cc4d63fa67d5068bd711a85edb1c3fb0c3e2f381", size = 270149, upload-time = "2025-07-26T12:01:04.072Z" }, - { url = "https://files.pythonhosted.org/packages/30/2e/dd4ced42fefac8470661d7cb7e264808425e6c5d56d175291e93890cce09/contourpy-1.3.3-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:929ddf8c4c7f348e4c0a5a3a714b5c8542ffaa8c22954862a46ca1813b667ee7", size = 329222, upload-time = "2025-07-26T12:01:05.688Z" }, - { url = "https://files.pythonhosted.org/packages/f2/74/cc6ec2548e3d276c71389ea4802a774b7aa3558223b7bade3f25787fafc2/contourpy-1.3.3-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9e999574eddae35f1312c2b4b717b7885d4edd6cb46700e04f7f02db454e67c1", size = 377234, upload-time = "2025-07-26T12:01:07.054Z" }, - { url = "https://files.pythonhosted.org/packages/03/b3/64ef723029f917410f75c09da54254c5f9ea90ef89b143ccadb09df14c15/contourpy-1.3.3-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf67e0e3f482cb69779dd3061b534eb35ac9b17f163d851e2a547d56dba0a3a", size = 380555, upload-time = "2025-07-26T12:01:08.801Z" }, - { url = "https://files.pythonhosted.org/packages/5f/4b/6157f24ca425b89fe2eb7e7be642375711ab671135be21e6faa100f7448c/contourpy-1.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51e79c1f7470158e838808d4a996fa9bac72c498e93d8ebe5119bc1e6becb0db", size = 355238, upload-time = "2025-07-26T12:01:10.319Z" }, - { url = "https://files.pythonhosted.org/packages/98/56/f914f0dd678480708a04cfd2206e7c382533249bc5001eb9f58aa693e200/contourpy-1.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:598c3aaece21c503615fd59c92a3598b428b2f01bfb4b8ca9c4edeecc2438620", size = 1326218, upload-time = "2025-07-26T12:01:12.659Z" }, - { url = "https://files.pythonhosted.org/packages/fb/d7/4a972334a0c971acd5172389671113ae82aa7527073980c38d5868ff1161/contourpy-1.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:322ab1c99b008dad206d406bb61d014cf0174df491ae9d9d0fac6a6fda4f977f", size = 1392867, upload-time = "2025-07-26T12:01:15.533Z" }, - { url = "https://files.pythonhosted.org/packages/75/3e/f2cc6cd56dc8cff46b1a56232eabc6feea52720083ea71ab15523daab796/contourpy-1.3.3-cp311-cp311-win32.whl", hash = "sha256:fd907ae12cd483cd83e414b12941c632a969171bf90fc937d0c9f268a31cafff", size = 183677, upload-time = "2025-07-26T12:01:17.088Z" }, - { url = "https://files.pythonhosted.org/packages/98/4b/9bd370b004b5c9d8045c6c33cf65bae018b27aca550a3f657cdc99acdbd8/contourpy-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:3519428f6be58431c56581f1694ba8e50626f2dd550af225f82fb5f5814d2a42", size = 225234, upload-time = "2025-07-26T12:01:18.256Z" }, - { url = "https://files.pythonhosted.org/packages/d9/b6/71771e02c2e004450c12b1120a5f488cad2e4d5b590b1af8bad060360fe4/contourpy-1.3.3-cp311-cp311-win_arm64.whl", hash = "sha256:15ff10bfada4bf92ec8b31c62bf7c1834c244019b4a33095a68000d7075df470", size = 193123, upload-time = "2025-07-26T12:01:19.848Z" }, - { url = "https://files.pythonhosted.org/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb", size = 293419, upload-time = "2025-07-26T12:01:21.16Z" }, - { url = "https://files.pythonhosted.org/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6", size = 273979, upload-time = "2025-07-26T12:01:22.448Z" }, - { url = "https://files.pythonhosted.org/packages/d4/1c/a12359b9b2ca3a845e8f7f9ac08bdf776114eb931392fcad91743e2ea17b/contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7", size = 332653, upload-time = "2025-07-26T12:01:24.155Z" }, - { url = "https://files.pythonhosted.org/packages/63/12/897aeebfb475b7748ea67b61e045accdfcf0d971f8a588b67108ed7f5512/contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8", size = 379536, upload-time = "2025-07-26T12:01:25.91Z" }, - { url = "https://files.pythonhosted.org/packages/43/8a/a8c584b82deb248930ce069e71576fc09bd7174bbd35183b7943fb1064fd/contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea", size = 384397, upload-time = "2025-07-26T12:01:27.152Z" }, - { url = "https://files.pythonhosted.org/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1", size = 362601, upload-time = "2025-07-26T12:01:28.808Z" }, - { url = "https://files.pythonhosted.org/packages/05/0a/a3fe3be3ee2dceb3e615ebb4df97ae6f3828aa915d3e10549ce016302bd1/contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7", size = 1331288, upload-time = "2025-07-26T12:01:31.198Z" }, - { url = "https://files.pythonhosted.org/packages/33/1d/acad9bd4e97f13f3e2b18a3977fe1b4a37ecf3d38d815333980c6c72e963/contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411", size = 1403386, upload-time = "2025-07-26T12:01:33.947Z" }, - { url = "https://files.pythonhosted.org/packages/cf/8f/5847f44a7fddf859704217a99a23a4f6417b10e5ab1256a179264561540e/contourpy-1.3.3-cp312-cp312-win32.whl", hash = "sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69", size = 185018, upload-time = "2025-07-26T12:01:35.64Z" }, - { url = "https://files.pythonhosted.org/packages/19/e8/6026ed58a64563186a9ee3f29f41261fd1828f527dd93d33b60feca63352/contourpy-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b", size = 226567, upload-time = "2025-07-26T12:01:36.804Z" }, - { url = "https://files.pythonhosted.org/packages/d1/e2/f05240d2c39a1ed228d8328a78b6f44cd695f7ef47beb3e684cf93604f86/contourpy-1.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc", size = 193655, upload-time = "2025-07-26T12:01:37.999Z" }, - { url = "https://files.pythonhosted.org/packages/68/35/0167aad910bbdb9599272bd96d01a9ec6852f36b9455cf2ca67bd4cc2d23/contourpy-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:177fb367556747a686509d6fef71d221a4b198a3905fe824430e5ea0fda54eb5", size = 293257, upload-time = "2025-07-26T12:01:39.367Z" }, - { url = "https://files.pythonhosted.org/packages/96/e4/7adcd9c8362745b2210728f209bfbcf7d91ba868a2c5f40d8b58f54c509b/contourpy-1.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d002b6f00d73d69333dac9d0b8d5e84d9724ff9ef044fd63c5986e62b7c9e1b1", size = 274034, upload-time = "2025-07-26T12:01:40.645Z" }, - { url = "https://files.pythonhosted.org/packages/73/23/90e31ceeed1de63058a02cb04b12f2de4b40e3bef5e082a7c18d9c8ae281/contourpy-1.3.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:348ac1f5d4f1d66d3322420f01d42e43122f43616e0f194fc1c9f5d830c5b286", size = 334672, upload-time = "2025-07-26T12:01:41.942Z" }, - { url = "https://files.pythonhosted.org/packages/ed/93/b43d8acbe67392e659e1d984700e79eb67e2acb2bd7f62012b583a7f1b55/contourpy-1.3.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:655456777ff65c2c548b7c454af9c6f33f16c8884f11083244b5819cc214f1b5", size = 381234, upload-time = "2025-07-26T12:01:43.499Z" }, - { url = "https://files.pythonhosted.org/packages/46/3b/bec82a3ea06f66711520f75a40c8fc0b113b2a75edb36aa633eb11c4f50f/contourpy-1.3.3-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:644a6853d15b2512d67881586bd03f462c7ab755db95f16f14d7e238f2852c67", size = 385169, upload-time = "2025-07-26T12:01:45.219Z" }, - { url = "https://files.pythonhosted.org/packages/4b/32/e0f13a1c5b0f8572d0ec6ae2f6c677b7991fafd95da523159c19eff0696a/contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4debd64f124ca62069f313a9cb86656ff087786016d76927ae2cf37846b006c9", size = 362859, upload-time = "2025-07-26T12:01:46.519Z" }, - { url = "https://files.pythonhosted.org/packages/33/71/e2a7945b7de4e58af42d708a219f3b2f4cff7386e6b6ab0a0fa0033c49a9/contourpy-1.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a15459b0f4615b00bbd1e91f1b9e19b7e63aea7483d03d804186f278c0af2659", size = 1332062, upload-time = "2025-07-26T12:01:48.964Z" }, - { url = "https://files.pythonhosted.org/packages/12/fc/4e87ac754220ccc0e807284f88e943d6d43b43843614f0a8afa469801db0/contourpy-1.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca0fdcd73925568ca027e0b17ab07aad764be4706d0a925b89227e447d9737b7", size = 1403932, upload-time = "2025-07-26T12:01:51.979Z" }, - { url = "https://files.pythonhosted.org/packages/a6/2e/adc197a37443f934594112222ac1aa7dc9a98faf9c3842884df9a9d8751d/contourpy-1.3.3-cp313-cp313-win32.whl", hash = "sha256:b20c7c9a3bf701366556e1b1984ed2d0cedf999903c51311417cf5f591d8c78d", size = 185024, upload-time = "2025-07-26T12:01:53.245Z" }, - { url = "https://files.pythonhosted.org/packages/18/0b/0098c214843213759692cc638fce7de5c289200a830e5035d1791d7a2338/contourpy-1.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:1cadd8b8969f060ba45ed7c1b714fe69185812ab43bd6b86a9123fe8f99c3263", size = 226578, upload-time = "2025-07-26T12:01:54.422Z" }, - { url = "https://files.pythonhosted.org/packages/8a/9a/2f6024a0c5995243cd63afdeb3651c984f0d2bc727fd98066d40e141ad73/contourpy-1.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9", size = 193524, upload-time = "2025-07-26T12:01:55.73Z" }, - { url = "https://files.pythonhosted.org/packages/c0/b3/f8a1a86bd3298513f500e5b1f5fd92b69896449f6cab6a146a5d52715479/contourpy-1.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:88df9880d507169449d434c293467418b9f6cbe82edd19284aa0409e7fdb933d", size = 306730, upload-time = "2025-07-26T12:01:57.051Z" }, - { url = "https://files.pythonhosted.org/packages/3f/11/4780db94ae62fc0c2053909b65dc3246bd7cecfc4f8a20d957ad43aa4ad8/contourpy-1.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d06bb1f751ba5d417047db62bca3c8fde202b8c11fb50742ab3ab962c81e8216", size = 287897, upload-time = "2025-07-26T12:01:58.663Z" }, - { url = "https://files.pythonhosted.org/packages/ae/15/e59f5f3ffdd6f3d4daa3e47114c53daabcb18574a26c21f03dc9e4e42ff0/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4e6b05a45525357e382909a4c1600444e2a45b4795163d3b22669285591c1ae", size = 326751, upload-time = "2025-07-26T12:02:00.343Z" }, - { url = "https://files.pythonhosted.org/packages/0f/81/03b45cfad088e4770b1dcf72ea78d3802d04200009fb364d18a493857210/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab3074b48c4e2cf1a960e6bbeb7f04566bf36b1861d5c9d4d8ac04b82e38ba20", size = 375486, upload-time = "2025-07-26T12:02:02.128Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ba/49923366492ffbdd4486e970d421b289a670ae8cf539c1ea9a09822b371a/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c3d53c796f8647d6deb1abe867daeb66dcc8a97e8455efa729516b997b8ed99", size = 388106, upload-time = "2025-07-26T12:02:03.615Z" }, - { url = "https://files.pythonhosted.org/packages/9f/52/5b00ea89525f8f143651f9f03a0df371d3cbd2fccd21ca9b768c7a6500c2/contourpy-1.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50ed930df7289ff2a8d7afeb9603f8289e5704755c7e5c3bbd929c90c817164b", size = 352548, upload-time = "2025-07-26T12:02:05.165Z" }, - { url = "https://files.pythonhosted.org/packages/32/1d/a209ec1a3a3452d490f6b14dd92e72280c99ae3d1e73da74f8277d4ee08f/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4feffb6537d64b84877da813a5c30f1422ea5739566abf0bd18065ac040e120a", size = 1322297, upload-time = "2025-07-26T12:02:07.379Z" }, - { url = "https://files.pythonhosted.org/packages/bc/9e/46f0e8ebdd884ca0e8877e46a3f4e633f6c9c8c4f3f6e72be3fe075994aa/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2b7e9480ffe2b0cd2e787e4df64270e3a0440d9db8dc823312e2c940c167df7e", size = 1391023, upload-time = "2025-07-26T12:02:10.171Z" }, - { url = "https://files.pythonhosted.org/packages/b9/70/f308384a3ae9cd2209e0849f33c913f658d3326900d0ff5d378d6a1422d2/contourpy-1.3.3-cp313-cp313t-win32.whl", hash = "sha256:283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3", size = 196157, upload-time = "2025-07-26T12:02:11.488Z" }, - { url = "https://files.pythonhosted.org/packages/b2/dd/880f890a6663b84d9e34a6f88cded89d78f0091e0045a284427cb6b18521/contourpy-1.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8", size = 240570, upload-time = "2025-07-26T12:02:12.754Z" }, - { url = "https://files.pythonhosted.org/packages/80/99/2adc7d8ffead633234817ef8e9a87115c8a11927a94478f6bb3d3f4d4f7d/contourpy-1.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301", size = 199713, upload-time = "2025-07-26T12:02:14.4Z" }, - { url = "https://files.pythonhosted.org/packages/72/8b/4546f3ab60f78c514ffb7d01a0bd743f90de36f0019d1be84d0a708a580a/contourpy-1.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fde6c716d51c04b1c25d0b90364d0be954624a0ee9d60e23e850e8d48353d07a", size = 292189, upload-time = "2025-07-26T12:02:16.095Z" }, - { url = "https://files.pythonhosted.org/packages/fd/e1/3542a9cb596cadd76fcef413f19c79216e002623158befe6daa03dbfa88c/contourpy-1.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:cbedb772ed74ff5be440fa8eee9bd49f64f6e3fc09436d9c7d8f1c287b121d77", size = 273251, upload-time = "2025-07-26T12:02:17.524Z" }, - { url = "https://files.pythonhosted.org/packages/b1/71/f93e1e9471d189f79d0ce2497007731c1e6bf9ef6d1d61b911430c3db4e5/contourpy-1.3.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22e9b1bd7a9b1d652cd77388465dc358dafcd2e217d35552424aa4f996f524f5", size = 335810, upload-time = "2025-07-26T12:02:18.9Z" }, - { url = "https://files.pythonhosted.org/packages/91/f9/e35f4c1c93f9275d4e38681a80506b5510e9327350c51f8d4a5a724d178c/contourpy-1.3.3-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a22738912262aa3e254e4f3cb079a95a67132fc5a063890e224393596902f5a4", size = 382871, upload-time = "2025-07-26T12:02:20.418Z" }, - { url = "https://files.pythonhosted.org/packages/b5/71/47b512f936f66a0a900d81c396a7e60d73419868fba959c61efed7a8ab46/contourpy-1.3.3-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:afe5a512f31ee6bd7d0dda52ec9864c984ca3d66664444f2d72e0dc4eb832e36", size = 386264, upload-time = "2025-07-26T12:02:21.916Z" }, - { url = "https://files.pythonhosted.org/packages/04/5f/9ff93450ba96b09c7c2b3f81c94de31c89f92292f1380261bd7195bea4ea/contourpy-1.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f64836de09927cba6f79dcd00fdd7d5329f3fccc633468507079c829ca4db4e3", size = 363819, upload-time = "2025-07-26T12:02:23.759Z" }, - { url = "https://files.pythonhosted.org/packages/3e/a6/0b185d4cc480ee494945cde102cb0149ae830b5fa17bf855b95f2e70ad13/contourpy-1.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1fd43c3be4c8e5fd6e4f2baeae35ae18176cf2e5cced681cca908addf1cdd53b", size = 1333650, upload-time = "2025-07-26T12:02:26.181Z" }, - { url = "https://files.pythonhosted.org/packages/43/d7/afdc95580ca56f30fbcd3060250f66cedbde69b4547028863abd8aa3b47e/contourpy-1.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6afc576f7b33cf00996e5c1102dc2a8f7cc89e39c0b55df93a0b78c1bd992b36", size = 1404833, upload-time = "2025-07-26T12:02:28.782Z" }, - { url = "https://files.pythonhosted.org/packages/e2/e2/366af18a6d386f41132a48f033cbd2102e9b0cf6345d35ff0826cd984566/contourpy-1.3.3-cp314-cp314-win32.whl", hash = "sha256:66c8a43a4f7b8df8b71ee1840e4211a3c8d93b214b213f590e18a1beca458f7d", size = 189692, upload-time = "2025-07-26T12:02:30.128Z" }, - { url = "https://files.pythonhosted.org/packages/7d/c2/57f54b03d0f22d4044b8afb9ca0e184f8b1afd57b4f735c2fa70883dc601/contourpy-1.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:cf9022ef053f2694e31d630feaacb21ea24224be1c3ad0520b13d844274614fd", size = 232424, upload-time = "2025-07-26T12:02:31.395Z" }, - { url = "https://files.pythonhosted.org/packages/18/79/a9416650df9b525737ab521aa181ccc42d56016d2123ddcb7b58e926a42c/contourpy-1.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:95b181891b4c71de4bb404c6621e7e2390745f887f2a026b2d99e92c17892339", size = 198300, upload-time = "2025-07-26T12:02:32.956Z" }, - { url = "https://files.pythonhosted.org/packages/1f/42/38c159a7d0f2b7b9c04c64ab317042bb6952b713ba875c1681529a2932fe/contourpy-1.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:33c82d0138c0a062380332c861387650c82e4cf1747aaa6938b9b6516762e772", size = 306769, upload-time = "2025-07-26T12:02:34.2Z" }, - { url = "https://files.pythonhosted.org/packages/c3/6c/26a8205f24bca10974e77460de68d3d7c63e282e23782f1239f226fcae6f/contourpy-1.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ea37e7b45949df430fe649e5de8351c423430046a2af20b1c1961cae3afcda77", size = 287892, upload-time = "2025-07-26T12:02:35.807Z" }, - { url = "https://files.pythonhosted.org/packages/66/06/8a475c8ab718ebfd7925661747dbb3c3ee9c82ac834ccb3570be49d129f4/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d304906ecc71672e9c89e87c4675dc5c2645e1f4269a5063b99b0bb29f232d13", size = 326748, upload-time = "2025-07-26T12:02:37.193Z" }, - { url = "https://files.pythonhosted.org/packages/b4/a3/c5ca9f010a44c223f098fccd8b158bb1cb287378a31ac141f04730dc49be/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca658cd1a680a5c9ea96dc61cdbae1e85c8f25849843aa799dfd3cb370ad4fbe", size = 375554, upload-time = "2025-07-26T12:02:38.894Z" }, - { url = "https://files.pythonhosted.org/packages/80/5b/68bd33ae63fac658a4145088c1e894405e07584a316738710b636c6d0333/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ab2fd90904c503739a75b7c8c5c01160130ba67944a7b77bbf36ef8054576e7f", size = 388118, upload-time = "2025-07-26T12:02:40.642Z" }, - { url = "https://files.pythonhosted.org/packages/40/52/4c285a6435940ae25d7410a6c36bda5145839bc3f0beb20c707cda18b9d2/contourpy-1.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7301b89040075c30e5768810bc96a8e8d78085b47d8be6e4c3f5a0b4ed478a0", size = 352555, upload-time = "2025-07-26T12:02:42.25Z" }, - { url = "https://files.pythonhosted.org/packages/24/ee/3e81e1dd174f5c7fefe50e85d0892de05ca4e26ef1c9a59c2a57e43b865a/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2a2a8b627d5cc6b7c41a4beff6c5ad5eb848c88255fda4a8745f7e901b32d8e4", size = 1322295, upload-time = "2025-07-26T12:02:44.668Z" }, - { url = "https://files.pythonhosted.org/packages/3c/b2/6d913d4d04e14379de429057cd169e5e00f6c2af3bb13e1710bcbdb5da12/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fd6ec6be509c787f1caf6b247f0b1ca598bef13f4ddeaa126b7658215529ba0f", size = 1391027, upload-time = "2025-07-26T12:02:47.09Z" }, - { url = "https://files.pythonhosted.org/packages/93/8a/68a4ec5c55a2971213d29a9374913f7e9f18581945a7a31d1a39b5d2dfe5/contourpy-1.3.3-cp314-cp314t-win32.whl", hash = "sha256:e74a9a0f5e3fff48fb5a7f2fd2b9b70a3fe014a67522f79b7cca4c0c7e43c9ae", size = 202428, upload-time = "2025-07-26T12:02:48.691Z" }, - { url = "https://files.pythonhosted.org/packages/fa/96/fd9f641ffedc4fa3ace923af73b9d07e869496c9cc7a459103e6e978992f/contourpy-1.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:13b68d6a62db8eafaebb8039218921399baf6e47bf85006fd8529f2a08ef33fc", size = 250331, upload-time = "2025-07-26T12:02:50.137Z" }, - { url = "https://files.pythonhosted.org/packages/ae/8c/469afb6465b853afff216f9528ffda78a915ff880ed58813ba4faf4ba0b6/contourpy-1.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b7448cb5a725bb1e35ce88771b86fba35ef418952474492cf7c764059933ff8b", size = 203831, upload-time = "2025-07-26T12:02:51.449Z" }, - { url = "https://files.pythonhosted.org/packages/a5/29/8dcfe16f0107943fa92388c23f6e05cff0ba58058c4c95b00280d4c75a14/contourpy-1.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cd5dfcaeb10f7b7f9dc8941717c6c2ade08f587be2226222c12b25f0483ed497", size = 278809, upload-time = "2025-07-26T12:02:52.74Z" }, - { url = "https://files.pythonhosted.org/packages/85/a9/8b37ef4f7dafeb335daee3c8254645ef5725be4d9c6aa70b50ec46ef2f7e/contourpy-1.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:0c1fc238306b35f246d61a1d416a627348b5cf0648648a031e14bb8705fcdfe8", size = 261593, upload-time = "2025-07-26T12:02:54.037Z" }, - { url = "https://files.pythonhosted.org/packages/0a/59/ebfb8c677c75605cc27f7122c90313fd2f375ff3c8d19a1694bda74aaa63/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70f9aad7de812d6541d29d2bbf8feb22ff7e1c299523db288004e3157ff4674e", size = 302202, upload-time = "2025-07-26T12:02:55.947Z" }, - { url = "https://files.pythonhosted.org/packages/3c/37/21972a15834d90bfbfb009b9d004779bd5a07a0ec0234e5ba8f64d5736f4/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ed3657edf08512fc3fe81b510e35c2012fbd3081d2e26160f27ca28affec989", size = 329207, upload-time = "2025-07-26T12:02:57.468Z" }, - { url = "https://files.pythonhosted.org/packages/0c/58/bd257695f39d05594ca4ad60df5bcb7e32247f9951fd09a9b8edb82d1daa/contourpy-1.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:3d1a3799d62d45c18bafd41c5fa05120b96a28079f2393af559b843d1a966a77", size = 225315, upload-time = "2025-07-26T12:02:58.801Z" }, -] - -[[package]] -name = "corner" -version = "2.2.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "matplotlib" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5c/61/2d728798e9ae3bf899d962f77486cb29888d57f6fbf9561bc1435a6b1a74/corner-2.2.3.tar.gz", hash = "sha256:471b7b63395d8f1dee176bb779348ade38d56abd23404a48802a593607745e1c", size = 5932840, upload-time = "2024-11-23T12:50:56.955Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/4a/5bd0a8b981c5a93153d9eb7c63143b407cc7f8dfc9f91eedc9b6f5289eca/corner-2.2.3-py3-none-any.whl", hash = "sha256:39674b223482456c3a78234dc7bdefd21188a2d47bb8cd468104a0501f6659ec", size = 15946, upload-time = "2024-11-23T12:50:53.639Z" }, -] - -[[package]] -name = "cryptography" -version = "48.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9f/a9/db8f313fdcd85d767d4973515e1db101f9c71f95fced83233de224673757/cryptography-48.0.0.tar.gz", hash = "sha256:5c3932f4436d1cccb036cb0eaef46e6e2db91035166f1ad6505c3c9d5a635920", size = 832984, upload-time = "2026-05-04T22:59:38.133Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/3d/01f6dd9190170a5a241e0e98c2d04be3664a9e6f5b9b872cde63aff1c3dd/cryptography-48.0.0-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:0c558d2cdffd8f4bbb30fc7134c74d2ca9a476f830bb053074498fbc86f41ed6", size = 8001587, upload-time = "2026-05-04T22:57:36.803Z" }, - { url = "https://files.pythonhosted.org/packages/b2/6e/e90527eef33f309beb811cf7c982c3aeffcce8e3edb178baa4ca3ae4a6fa/cryptography-48.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f5333311663ea94f75dd408665686aaf426563556bb5283554a3539177e03b8c", size = 4690433, upload-time = "2026-05-04T22:57:40.373Z" }, - { url = "https://files.pythonhosted.org/packages/90/04/673510ed51ddff56575f306cf1617d80411ee76831ccd3097599140efdfe/cryptography-48.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7995ef305d7165c3f11ae07f2517e5a4f1d5c18da1376a0a9ed496336b69e5f3", size = 4710620, upload-time = "2026-05-04T22:57:42.935Z" }, - { url = "https://files.pythonhosted.org/packages/14/d5/e9c4ef932c8d800490c34d8bd589d64a31d5890e27ec9e9ad532be893294/cryptography-48.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:40ba1f85eaa6959837b1d51c9767e230e14612eea4ef110ee8854ada22da1bf5", size = 4696283, upload-time = "2026-05-04T22:57:45.294Z" }, - { url = "https://files.pythonhosted.org/packages/0c/29/174b9dfb60b12d59ecfc6cfa04bc88c21b42a54f01b8aae09bb6e51e4c7f/cryptography-48.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:369a6348999f94bbd53435c894377b20ab95f25a9065c283570e70150d8abc3c", size = 5296573, upload-time = "2026-05-04T22:57:47.933Z" }, - { url = "https://files.pythonhosted.org/packages/95/38/0d29a6fd7d0d1373f0c0c88a04ba20e359b257753ac497564cd660fc1d55/cryptography-48.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a0e692c683f4df67815a2d258b324e66f4738bd7a96a218c826dce4f4bd05d8f", size = 4743677, upload-time = "2026-05-04T22:57:50.067Z" }, - { url = "https://files.pythonhosted.org/packages/30/be/eef653013d5c63b6a490529e0316f9ac14a37602965d4903efed1399f32b/cryptography-48.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:18349bbc56f4743c8b12dc32e2bccb2cf83ee8b69a3bba74ef8ae857e26b3d25", size = 4330808, upload-time = "2026-05-04T22:57:52.301Z" }, - { url = "https://files.pythonhosted.org/packages/84/9e/500463e87abb7a0a0f9f256ec21123ecde0a7b5541a15e840ea54551fd81/cryptography-48.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e8eac43dfca5c4cccc6dad9a80504436fca53bb9bc3100a2386d730fbe6b602", size = 4695941, upload-time = "2026-05-04T22:57:54.603Z" }, - { url = "https://files.pythonhosted.org/packages/e3/dc/7303087450c2ec9e7fbb750e17c2abfbc658f23cbd0e54009509b7cc4091/cryptography-48.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9ccdac7d40688ecb5a3b4a604b8a88c8002e3442d6c60aead1db2a89a041560c", size = 5252579, upload-time = "2026-05-04T22:57:57.207Z" }, - { url = "https://files.pythonhosted.org/packages/d0/c0/7101d3b7215edcdc90c45da544961fd8ed2d6448f77577460fa75a8443f7/cryptography-48.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:bd72e68b06bb1e96913f97dd4901119bc17f39d4586a5adf2d3e47bc2b9d58b5", size = 4743326, upload-time = "2026-05-04T22:57:59.535Z" }, - { url = "https://files.pythonhosted.org/packages/ac/d8/5b833bad13016f562ab9d063d68199a4bd121d18458e439515601d3357ec/cryptography-48.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:59baa2cb386c4f0b9905bd6eb4c2a79a69a128408fd31d32ca4d7102d4156321", size = 4826672, upload-time = "2026-05-04T22:58:01.996Z" }, - { url = "https://files.pythonhosted.org/packages/98/e1/7074eb8bf3c135558c73fc2bcf0f5633f912e6fb87e868a55c454080ef09/cryptography-48.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9249e3cd978541d665967ac2cb2787fd6a62bddf1e75b3e347a594d7dacf4f74", size = 4972574, upload-time = "2026-05-04T22:58:03.968Z" }, - { url = "https://files.pythonhosted.org/packages/04/70/e5a1b41d325f797f39427aa44ef8baf0be500065ab6d8e10369d850d4a4f/cryptography-48.0.0-cp311-abi3-win32.whl", hash = "sha256:9c459db21422be75e2809370b829a87eb37f74cd785fc4aa9ea1e5f43b47cda4", size = 3294868, upload-time = "2026-05-04T22:58:06.467Z" }, - { url = "https://files.pythonhosted.org/packages/f4/ac/8ac51b4a5fc5932eb7ee5c517ba7dc8cd834f0048962b6b352f00f41ebf9/cryptography-48.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:5b012212e08b8dd5edc78ef54da83dd9892fd9105323b3993eff6bea65dc21d7", size = 3817107, upload-time = "2026-05-04T22:58:08.845Z" }, - { url = "https://files.pythonhosted.org/packages/6b/84/70e3feea9feea87fd7cbe77efb2712ae1e3e6edf10749dc6e95f4e60e455/cryptography-48.0.0-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:3cb07a3ed6431663cd321ea8a000a1314c74211f823e4177fefa2255e057d1ec", size = 7986556, upload-time = "2026-05-04T22:58:11.172Z" }, - { url = "https://files.pythonhosted.org/packages/89/6e/18e07a618bb5442ba10cf4df16e99c071365528aa570dfcb8c02e25a303b/cryptography-48.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8c7378637d7d88016fa6791c159f698b3d3eed28ebf844ac36b9dc04a14dae18", size = 4684776, upload-time = "2026-05-04T22:58:13.712Z" }, - { url = "https://files.pythonhosted.org/packages/be/6a/4ea3b4c6c6759794d5ee2103c304a5076dc4b19ae1f9fe47dba439e159e9/cryptography-48.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc90c0b39b2e3c65ef52c804b72e3c58f8a04ab2a1871272798e5f9572c17d20", size = 4698121, upload-time = "2026-05-04T22:58:16.448Z" }, - { url = "https://files.pythonhosted.org/packages/2f/59/6ff6ad6cae03bb887da2a5860b2c9805f8dac969ef01ce563336c49bd1d1/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:76341972e1eff8b4bea859f09c0d3e64b96ce931b084f9b9b7db8ef364c30eff", size = 4690042, upload-time = "2026-05-04T22:58:18.544Z" }, - { url = "https://files.pythonhosted.org/packages/ca/b4/fc334ed8cfd705aca282fe4d8f5ae64a8e0f74932e9feecb344610cf6e4d/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:55b7718303bf06a5753dcdccf2f3945cf18ad7bffde41b61226e4db31ab89a9c", size = 5282526, upload-time = "2026-05-04T22:58:20.75Z" }, - { url = "https://files.pythonhosted.org/packages/11/08/9f8c5386cc4cd90d8255c7cdd0f5baf459a08502a09de30dc51f553d38dc/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:a64697c641c7b1b2178e573cbc31c7c6684cd56883a478d75143dbb7118036db", size = 4733116, upload-time = "2026-05-04T22:58:23.627Z" }, - { url = "https://files.pythonhosted.org/packages/b8/77/99307d7574045699f8805aa500fa0fb83422d115b5400a064ddd306d7750/cryptography-48.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:561215ea3879cb1cbbf272867e2efda62476f240fb58c64de6b393ae19246741", size = 4316030, upload-time = "2026-05-04T22:58:25.581Z" }, - { url = "https://files.pythonhosted.org/packages/fd/36/a608b98337af3cb2aff4818e406649d30572b7031918b04c87d979495348/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ad64688338ed4bc1a6618076ba75fd7194a5f1797ac60b47afe926285adb3166", size = 4689640, upload-time = "2026-05-04T22:58:27.747Z" }, - { url = "https://files.pythonhosted.org/packages/dd/a6/825010a291b4438aecc1f568bc428189fc1175515223632477c07dc0a6df/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:906cbf0670286c6e0044156bc7d4af9cbb0ef6db9f73e52c3ec56ba6bdde5336", size = 5237657, upload-time = "2026-05-04T22:58:29.848Z" }, - { url = "https://files.pythonhosted.org/packages/b9/09/4e76a09b4caa29aad535ddc806f5d4c5d01885bd978bd984fbc6ca032cae/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:ea8990436d914540a40ab24b6a77c0969695ed52f4a4874c5137ccf7045a7057", size = 4732362, upload-time = "2026-05-04T22:58:32.009Z" }, - { url = "https://files.pythonhosted.org/packages/18/78/444fa04a77d0cb95f417dda20d450e13c56ba8e5220fc892a1658f44f882/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c18684a7f0cc9a3cb60328f496b8e3372def7c5d2df39ac267878b05565aaaae", size = 4819580, upload-time = "2026-05-04T22:58:34.254Z" }, - { url = "https://files.pythonhosted.org/packages/38/85/ea67067c70a1fd4be2c63d35eeed82658023021affccc7b17705f8527dd2/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9be5aafa5736574f8f15f262adc81b2a9869e2cfe9014d52a44633905b40d52c", size = 4963283, upload-time = "2026-05-04T22:58:36.376Z" }, - { url = "https://files.pythonhosted.org/packages/75/54/cc6d0f3deac3e81c7f847e8a189a12b6cdd65059b43dad25d4316abd849a/cryptography-48.0.0-cp314-cp314t-win32.whl", hash = "sha256:c17dfe85494deaeddc5ce251aebd1d60bbe6afc8b62071bb0b469431a000124f", size = 3270954, upload-time = "2026-05-04T22:58:38.791Z" }, - { url = "https://files.pythonhosted.org/packages/49/67/cc947e288c0758a4e5473d1dcb743037ab7785541265a969240b8885441a/cryptography-48.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27241b1dc9962e056062a8eef1991d02c3a24569c95975bd2322a8a52c6e5e12", size = 3797313, upload-time = "2026-05-04T22:58:40.746Z" }, - { url = "https://files.pythonhosted.org/packages/f2/63/61d4a4e1c6b6bab6ce1e213cd36a24c415d90e76d78c5eb8577c5541d2e8/cryptography-48.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:58d00498e8933e4a194f3076aee1b4a97dfec1a6da444535755822fe5d8b0b86", size = 7983482, upload-time = "2026-05-04T22:58:43.769Z" }, - { url = "https://files.pythonhosted.org/packages/d5/ac/f5b5995b87770c693e2596559ffafe195b4033a57f14a82268a2842953f3/cryptography-48.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:614d0949f4790582d2cc25553abd09dd723025f0c0e7c67376a1d77196743d6e", size = 4683266, upload-time = "2026-05-04T22:58:46.064Z" }, - { url = "https://files.pythonhosted.org/packages/ec/c6/8b14f67e18338fbc4adb76f66c001f5c3610b3e2d1837f268f47a347dbbb/cryptography-48.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ce4bfae76319a532a2dc68f82cc32f5676ee792a983187dac07183690e5c66f", size = 4696228, upload-time = "2026-05-04T22:58:48.22Z" }, - { url = "https://files.pythonhosted.org/packages/ea/73/f808fbae9514bd91b47875b003f13e284c8c6bdfd904b7944e803937eec1/cryptography-48.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:2eb992bbd4661238c5a397594c83f5b4dc2bc5b848c365c8f991b6780efcc5c7", size = 4689097, upload-time = "2026-05-04T22:58:50.9Z" }, - { url = "https://files.pythonhosted.org/packages/93/01/d86632d7d28db8ae83221995752eeb6639ffb374c2d22955648cf8d52797/cryptography-48.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:22a5cb272895dce158b2cacdfdc3debd299019659f42947dbdac6f32d68fe832", size = 5283582, upload-time = "2026-05-04T22:58:53.017Z" }, - { url = "https://files.pythonhosted.org/packages/02/e1/50edc7a50334807cc4791fc4a0ce7468b4a1416d9138eab358bfc9a3d70b/cryptography-48.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2b4d59804e8408e2fea7d1fbaf218e5ec984325221db76e6a241a9abd6cdd95c", size = 4730479, upload-time = "2026-05-04T22:58:55.611Z" }, - { url = "https://files.pythonhosted.org/packages/6f/af/99a582b1b1641ff5911ac559beb45097cf79efd4ead4657f578ef1af2d47/cryptography-48.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:984a20b0f62a26f48a3396c72e4bc34c66e356d356bf370053066b3b6d54634a", size = 4326481, upload-time = "2026-05-04T22:58:57.607Z" }, - { url = "https://files.pythonhosted.org/packages/90/ee/89aa26a06ef0a7d7611788ffd571a7c50e368cc6a4d5eef8b4884e866edb/cryptography-48.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5a5ed8fde7a1d09376ca0b40e68cd59c69fe23b1f9768bd5824f54681626032a", size = 4688713, upload-time = "2026-05-04T22:59:00.077Z" }, - { url = "https://files.pythonhosted.org/packages/70/ba/bcb1b0bb7a33d4c7c0c4d4c7874b4a62ae4f56113a5f4baefa362dfb1f0f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:8cd666227ef7af430aa5914a9910e0ddd703e75f039cef0825cd0da71b6b711a", size = 5238165, upload-time = "2026-05-04T22:59:02.317Z" }, - { url = "https://files.pythonhosted.org/packages/c9/70/ca4003b1ce5ca3dc3186ada51908c8a9b9ff7d5cab83cc0d43ee14ec144f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9071196d81abc88b3516ac8cdfad32e2b66dd4a5393a8e68a961e9161ddc6239", size = 4729947, upload-time = "2026-05-04T22:59:05.255Z" }, - { url = "https://files.pythonhosted.org/packages/44/a0/4ec7cf774207905aef1a8d11c3750d5a1db805eb380ee4e16df317870128/cryptography-48.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1e2d54c8be6152856a36f0882ab231e70f8ec7f14e93cf87db8a2ed056bf160c", size = 4822059, upload-time = "2026-05-04T22:59:07.802Z" }, - { url = "https://files.pythonhosted.org/packages/1e/75/a2e55f99c16fcac7b5d6c1eb19ad8e00799854d6be5ca845f9259eae1681/cryptography-48.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a5da777e32ffed6f85a7b2b3f7c5cbc88c146bfcd0a1d7baf5fcc6c52ee35dd4", size = 4960575, upload-time = "2026-05-04T22:59:09.851Z" }, - { url = "https://files.pythonhosted.org/packages/b8/23/6e6f32143ab5d8b36ca848a502c4bcd477ae75b9e1677e3530d669062578/cryptography-48.0.0-cp39-abi3-win32.whl", hash = "sha256:77a2ccbbe917f6710e05ba9adaa25fb5075620bf3ea6fb751997875aff4ae4bd", size = 3279117, upload-time = "2026-05-04T22:59:12.019Z" }, - { url = "https://files.pythonhosted.org/packages/9d/9a/0fea98a70cf1749d41d738836f6349d97945f7c89433a259a6c2642eefeb/cryptography-48.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:16cd65b9330583e4619939b3a3843eec1e6e789744bb01e7c7e2e62e33c239c8", size = 3792100, upload-time = "2026-05-04T22:59:14.884Z" }, - { url = "https://files.pythonhosted.org/packages/be/d2/024b5e06be9d44cb021fb0e1a03d34d63989cf56a0fe62f3dfbab695b9b4/cryptography-48.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:84cf79f0dc8b36ac5da873481716e87aef31fcfa0444f9e1d8b4b2cece142855", size = 3950391, upload-time = "2026-05-04T22:59:17.415Z" }, - { url = "https://files.pythonhosted.org/packages/bc/17/3861e17c56fa0fd37491a14a8673fdb77c57fc5693cafe745ea8b06dba75/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:fdfef35d751d510fcef5252703621574364fec16418c4a1e5e1055248401054b", size = 4637126, upload-time = "2026-05-04T22:59:20.197Z" }, - { url = "https://files.pythonhosted.org/packages/f0/0a/7e226dbff530f21480727eb764973a7bff2b912f8e15cd4f129e71b56d1d/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:0890f502ddf7d9c6426129c3f49f5c0a39278ed7cd6322c8755ffca6ee675a13", size = 4667270, upload-time = "2026-05-04T22:59:22.647Z" }, - { url = "https://files.pythonhosted.org/packages/3b/f2/5a72274ca9f1b2a8b44a662ee0bf1b435909deb473d6f97bcd035bcdbc71/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:ecde28a596bead48b0cfd2a1b4416c3d43074c2d785e3a398d7ec1fc4d0f7fbb", size = 4636797, upload-time = "2026-05-04T22:59:24.912Z" }, - { url = "https://files.pythonhosted.org/packages/b4/e1/48cedb2fe63626e91ded1edad159e2a4fb8b6906c4425eb7749673077ce7/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:4defde8685ae324a9eb9d818717e93b4638ef67070ac9bc15b8ca85f63048355", size = 4666800, upload-time = "2026-05-04T22:59:27.474Z" }, - { url = "https://files.pythonhosted.org/packages/a2/ca/7e8365deec19afb2b2c7be7c1c0aa8f99633b54e90c570999acda93260fc/cryptography-48.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:db63bf618e5dea46c07de12e900fe1cdd2541e6dc9dbae772a70b7d4d4765f6a", size = 3739536, upload-time = "2026-05-04T22:59:29.61Z" }, -] - -[[package]] -name = "cuda-bindings" -version = "13.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cuda-pathfinder", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/fe/7351d7e586a8b4c9f89731bfe4cf0148223e8f9903ff09571f78b3fb0682/cuda_bindings-13.2.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08b395f79cb89ce0cd8effff07c4a1e20101b873c256a1aeb286e8fd7bd0f556", size = 5744254, upload-time = "2026-03-11T00:12:29.798Z" }, - { url = "https://files.pythonhosted.org/packages/aa/ef/184aa775e970fc089942cd9ec6302e6e44679d4c14549c6a7ea45bf7f798/cuda_bindings-13.2.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6f3682ec3c4769326aafc67c2ba669d97d688d0b7e63e659d36d2f8b72f32d6", size = 6329075, upload-time = "2026-03-11T00:12:32.319Z" }, - { url = "https://files.pythonhosted.org/packages/e0/a9/3a8241c6e19483ac1f1dcf5c10238205dcb8a6e9d0d4d4709240dff28ff4/cuda_bindings-13.2.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:721104c603f059780d287969be3d194a18d0cc3b713ed9049065a1107706759d", size = 5730273, upload-time = "2026-03-11T00:12:37.18Z" }, - { url = "https://files.pythonhosted.org/packages/e9/94/2748597f47bb1600cd466b20cab4159f1530a3a33fe7f70fee199b3abb9e/cuda_bindings-13.2.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1eba9504ac70667dd48313395fe05157518fd6371b532790e96fbb31bbb5a5e1", size = 6313924, upload-time = "2026-03-11T00:12:39.462Z" }, - { url = "https://files.pythonhosted.org/packages/52/c8/b2589d68acf7e3d63e2be330b84bc25712e97ed799affbca7edd7eae25d6/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e865447abfb83d6a98ad5130ed3c70b1fc295ae3eeee39fd07b4ddb0671b6788", size = 5722404, upload-time = "2026-03-11T00:12:44.041Z" }, - { url = "https://files.pythonhosted.org/packages/1f/92/f899f7bbb5617bb65ec52a6eac1e9a1447a86b916c4194f8a5001b8cde0c/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46d8776a55d6d5da9dd6e9858fba2efcda2abe6743871dee47dd06eb8cb6d955", size = 6320619, upload-time = "2026-03-11T00:12:45.939Z" }, - { url = "https://files.pythonhosted.org/packages/df/93/eef988860a3ca985f82c4f3174fc0cdd94e07331ba9a92e8e064c260337f/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6629ca2df6f795b784752409bcaedbd22a7a651b74b56a165ebc0c9dcbd504d0", size = 5614610, upload-time = "2026-03-11T00:12:50.337Z" }, - { url = "https://files.pythonhosted.org/packages/18/23/6db3aba46864aee357ab2415135b3fe3da7e9f1fa0221fa2a86a5968099c/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7dca0da053d3b4cc4869eff49c61c03f3c5dbaa0bcd712317a358d5b8f3f385d", size = 6149914, upload-time = "2026-03-11T00:12:52.374Z" }, - { url = "https://files.pythonhosted.org/packages/c0/87/87a014f045b77c6de5c8527b0757fe644417b184e5367db977236a141602/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6464b30f46692d6c7f65d4a0e0450d81dd29de3afc1bb515653973d01c2cd6e", size = 5685673, upload-time = "2026-03-11T00:12:56.371Z" }, - { url = "https://files.pythonhosted.org/packages/ee/5e/c0fe77a73aaefd3fff25ffaccaac69c5a63eafdf8b9a4c476626ef0ac703/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4af9f3e1be603fa12d5ad6cfca7844c9d230befa9792b5abdf7dd79979c3626", size = 6191386, upload-time = "2026-03-11T00:12:58.965Z" }, - { url = "https://files.pythonhosted.org/packages/5f/58/ed2c3b39c8dd5f96aa7a4abef0d47a73932c7a988e30f5fa428f00ed0da1/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df850a1ff8ce1b3385257b08e47b70e959932f5f432d0a4e46a355962b4e4771", size = 5507469, upload-time = "2026-03-11T00:13:04.063Z" }, - { url = "https://files.pythonhosted.org/packages/1f/01/0c941b112ceeb21439b05895eace78ca1aa2eaaf695c8521a068fd9b4c00/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8a16384c6494e5485f39314b0b4afb04bee48d49edb16d5d8593fd35bbd231b", size = 6059693, upload-time = "2026-03-11T00:13:06.003Z" }, -] - -[[package]] -name = "cuda-pathfinder" -version = "1.5.4" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/11/d0/c177e29701cf1d3008d7d2b16b5fc626592ce13bd535f8795c5f57187e0e/cuda_pathfinder-1.5.4-py3-none-any.whl", hash = "sha256:9563d3175ce1828531acf4b94e1c1c7d67208c347ca002493e2654878b26f4b7", size = 51657, upload-time = "2026-04-27T22:42:07.712Z" }, -] - -[[package]] -name = "cuda-toolkit" -version = "13.0.2" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/57/b2/453099f5f3b698d7d0eab38916aac44c7f76229f451709e2eb9db6615dcd/cuda_toolkit-13.0.2-py2.py3-none-any.whl", hash = "sha256:b198824cf2f54003f50d64ada3a0f184b42ca0846c1c94192fa269ecd97a66eb", size = 2364, upload-time = "2025-12-19T23:24:07.328Z" }, -] - -[package.optional-dependencies] -cudart = [ - { name = "nvidia-cuda-runtime", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, -] -cufft = [ - { name = "nvidia-cufft", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, -] -cufile = [ - { name = "nvidia-cufile", marker = "sys_platform == 'linux'" }, -] -cupti = [ - { name = "nvidia-cuda-cupti", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, -] -curand = [ - { name = "nvidia-curand", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, -] -cusolver = [ - { name = "nvidia-cusolver", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, -] -cusparse = [ - { name = "nvidia-cusparse", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, -] -nvjitlink = [ - { name = "nvidia-nvjitlink", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, -] -nvrtc = [ - { name = "nvidia-cuda-nvrtc", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, -] -nvtx = [ - { name = "nvidia-nvtx", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, -] - -[[package]] -name = "cycler" -version = "0.12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, -] - -[[package]] -name = "dateparser" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "python-dateutil" }, - { name = "pytz" }, - { name = "regex" }, - { name = "tzlocal" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/46/2d/a0ccdb78788064fa0dc901b8524e50615c42be1d78b78d646d0b28d09180/dateparser-1.4.0.tar.gz", hash = "sha256:97a21840d5ecdf7630c584f673338a5afac5dfe84f647baf4d7e8df98f9354a4", size = 321512, upload-time = "2026-03-26T09:56:10.292Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b4/0b/3c3bb7cbe757279e693a0be6049048012f794d01f81099609ecd53b899f0/dateparser-1.4.0-py3-none-any.whl", hash = "sha256:7902b8e85d603494bf70a5a0b1decdddb2270b9c6e6b2bc8a57b93476c0df378", size = 300379, upload-time = "2026-03-26T09:56:08.409Z" }, -] - -[[package]] -name = "dill" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/81/e1/56027a71e31b02ddc53c7d65b01e68edf64dea2932122fe7746a516f75d5/dill-0.4.1.tar.gz", hash = "sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa", size = 187315, upload-time = "2026-01-19T02:36:56.85Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl", hash = "sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d", size = 120019, upload-time = "2026-01-19T02:36:55.663Z" }, -] - -[[package]] -name = "dqsegdb2" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "igwn-auth-utils" }, - { name = "igwn-segments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c4/37/7874b39abede48fe05c3b5a25092e419e521145e9705c97b711965e5f05d/dqsegdb2-1.3.0.tar.gz", hash = "sha256:4e291899cd395daf5913c48a835d213ef20833d78354871cb848988cc2cc8ae4", size = 33661, upload-time = "2025-01-08T16:36:58.072Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/78/67/c977e7307d432911fe28acf55dba01f84f3a0a4a2a968350195c0f52d23e/dqsegdb2-1.3.0-py3-none-any.whl", hash = "sha256:c93087da332b7d91519a370abed3cfc91b64a6bf393ec64d79c2747c837aeb66", size = 27957, upload-time = "2025-01-08T16:36:56.881Z" }, -] - -[[package]] -name = "dynesty" -version = "3.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "matplotlib" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/56/90/b538da9a03522e91252acc36dceb9e38538db0303f2dc314e0e417404d2c/dynesty-3.0.0.tar.gz", hash = "sha256:806c83b21904cf7df73c2478a9e0e8a2b7d811cd0d613109f3f810a7111d12b0", size = 35559700, upload-time = "2025-10-04T21:34:19.578Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/45/68f4ab90f889abbeec5932751643f055bb811a33ef7361ac849426dcc38a/dynesty-3.0.0-py3-none-any.whl", hash = "sha256:4cdbbb39c61158f4d882445b064b742fcdc4d03ba9b2ba845801cd2ca6fcb761", size = 102885, upload-time = "2025-10-04T21:34:12.006Z" }, -] - -[[package]] -name = "emcee" -version = "3.1.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cb/53/1045ee878cb24281387079f8ee4f0ade1622c6aae1ed1fd91a53e4fa5b19/emcee-3.1.6.tar.gz", hash = "sha256:11af4daf6ab8f9ca69681e3c29054665db7bbd87fd4eb8e437d2c3a1248c637d", size = 2871117, upload-time = "2024-04-19T10:03:19.555Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/ef/2196b9bf88ffa1bde45853c72df021fbd07a8fa91a0f59a22d14a050dc04/emcee-3.1.6-py2.py3-none-any.whl", hash = "sha256:f2d63752023bdccf744461450e512a5b417ae7d28f18e12acd76a33de87580cb", size = 47351, upload-time = "2024-04-19T10:03:17.522Z" }, -] - -[[package]] -name = "equinox" -version = "0.13.8" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jax", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "jax", version = "0.10.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "jaxtyping", version = "0.3.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "jaxtyping", version = "0.3.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "typing-extensions" }, - { name = "wadler-lindig" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/ff/522336d2f8264f2ad97119710b76e2cddf66145d03a1e89899175d26b192/equinox-0.13.8.tar.gz", hash = "sha256:dd075050018e2dd02e252e9d29d3060f7e67f085622d8d27a8e89e24bb8523db", size = 145257, upload-time = "2026-05-05T10:03:43.258Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/d6/69a76c8ccdef14af687c497040292a46e59fc7a0ab24724b60e50ca61030/equinox-0.13.8-py3-none-any.whl", hash = "sha256:ca004348533cc30a63ebe8823d7dd4bb626dce17743d40bbddb89b402ef2a240", size = 185813, upload-time = "2026-05-05T10:03:41.673Z" }, -] - -[[package]] -name = "exceptiongroup" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, -] - -[[package]] -name = "filelock" -version = "3.29.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/fe/997687a931ab51049acce6fa1f23e8f01216374ea81374ddee763c493db5/filelock-3.29.0.tar.gz", hash = "sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90", size = 57571, upload-time = "2026-04-19T15:39:10.068Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl", hash = "sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258", size = 39812, upload-time = "2026-04-19T15:39:08.752Z" }, -] - -[[package]] -name = "fonttools" -version = "4.63.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/84/69/c97f2c18e0db87d2c7b15da1974dace76ae938f1cfa22e2727a648b7ed43/fonttools-4.63.0.tar.gz", hash = "sha256:caeb583deeb5168e694b65cda8b4ee62abedfa66cf88488734466f2366b9c4e0", size = 3597189, upload-time = "2026-05-14T12:04:30.958Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/c9/4141c90a90db20f807c7e10bfd689fe53eb8f7f4caff58ee4d4dfe46919f/fonttools-4.63.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e3297a6a4059b4acc3a1e9a8b04741f240a80044eef08ebd32e8b5bcdddce75b", size = 2884632, upload-time = "2026-05-14T12:02:38.56Z" }, - { url = "https://files.pythonhosted.org/packages/b8/46/ad12b5c10eae602d7ef814b02afa08aacbf89da917fed5b071282b7eadc2/fonttools-4.63.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b1cd75a03ad8cb5bc40c90bfde68c0c47de423aa19e5c0f362b43520645eea94", size = 2429441, upload-time = "2026-05-14T12:02:41.162Z" }, - { url = "https://files.pythonhosted.org/packages/90/8f/bdca24a84c81d56fffed052229cdcff368f6e05882e526f4558891481f65/fonttools-4.63.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0425b277a59cff3d80ca42162a8de360f318438a2ac83570842a678d826d579", size = 4946346, upload-time = "2026-05-14T12:02:43.41Z" }, - { url = "https://files.pythonhosted.org/packages/04/59/a639c0e136441ee91a65b56fdf89e5d075927e7a09c559d1b0f5276577db/fonttools-4.63.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d7e5c9973aa04c95650c96e5f5ad865fbf42d62079163ecfab1e01cbc2504c22", size = 4903184, upload-time = "2026-05-14T12:02:45.742Z" }, - { url = "https://files.pythonhosted.org/packages/e6/53/91b7e0cb45b536f3da1b29ba8cbab89f27e8b986809e0b1982303a3f4eca/fonttools-4.63.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cb014d58140a38135f16064c74c652ed57aa0b75cbf8bb59cac821f7edb5334e", size = 4922967, upload-time = "2026-05-14T12:02:48.386Z" }, - { url = "https://files.pythonhosted.org/packages/c7/b7/87439bf44e6b97c5538cd29d0b7e366a5b8ce2cc132a4134fb67fa3f2fa2/fonttools-4.63.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:032038247a96c1690f9f31e377c389383c902531b085aa4e4dabd6f57f870e69", size = 5042799, upload-time = "2026-05-14T12:02:50.424Z" }, - { url = "https://files.pythonhosted.org/packages/ad/7c/8b96c3263b89ef99cded544c0f0636686f85dbd3c211c4dceef0231fca23/fonttools-4.63.0-cp310-cp310-win32.whl", hash = "sha256:a8b33a82979e0a6a34ff435cc81317be1f95ec1ebb7a3a2d1c8a6a54f02ae44e", size = 1519704, upload-time = "2026-05-14T12:02:52.523Z" }, - { url = "https://files.pythonhosted.org/packages/e5/4d/2c2f0069970b6907de8fb5b05c5c0193cc22f717df151d1c7aef1c738f58/fonttools-4.63.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c18358a155d75034911c5ee397a5b44cd19dd325dbb8b35fb60bf421d6a72ac", size = 1568666, upload-time = "2026-05-14T12:02:54.917Z" }, - { url = "https://files.pythonhosted.org/packages/75/2b/a7f1545bdf5da69c4bda0cea2a5781f0ad2a6623e0277267672db43c5fe6/fonttools-4.63.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2b8ae05d9eacf6081414d759c0a352769ac28ce31280d6bb8e77b03f9e3c449f", size = 2881793, upload-time = "2026-05-14T12:02:56.645Z" }, - { url = "https://files.pythonhosted.org/packages/49/50/965308c703f085f225db2886813b27e015b8b3438c350b22dd65b52c2a2c/fonttools-4.63.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79cdc9f567aec74a72918fd060283911406750cbc9fd28c1316023deb6ce31a9", size = 2428130, upload-time = "2026-05-14T12:02:58.891Z" }, - { url = "https://files.pythonhosted.org/packages/d8/38/6937fbd7f2dc3a6b48725851bc2c15ec949b9af14d9bbcb5fe83cdf9bdf9/fonttools-4.63.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c14b4fd138c4bafcca294765c547914e1aa431ae1ca94ab99d8db08c958bd3b", size = 5111952, upload-time = "2026-05-14T12:03:01.263Z" }, - { url = "https://files.pythonhosted.org/packages/0b/43/a81f20050a3115b57d62c8e781446949512eac36690dc384ccea65ff4cc1/fonttools-4.63.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d76ac49f929aecaf82d83250b8347e099d7aecba0f4726c1d9b6df3b8bb5fe18", size = 5082308, upload-time = "2026-05-14T12:03:03.211Z" }, - { url = "https://files.pythonhosted.org/packages/67/00/cdd9d4944ca6ae280d01e69cc37bde3bf663630b837a6fc6d2cd65d80e0e/fonttools-4.63.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dcf076a4474fe0d7367e5bbf5b052c7284fa1feca729c04176ce513521afd8a0", size = 5087932, upload-time = "2026-05-14T12:03:05.147Z" }, - { url = "https://files.pythonhosted.org/packages/f5/f1/0aa0dbea778c75adbef223c42019fd47d22262b905974d62d829545d485f/fonttools-4.63.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7dd683fef0663e9f0f45cf541d788d24caa3ec9db50796b588e1757d8b3bc007", size = 5213271, upload-time = "2026-05-14T12:03:07.238Z" }, - { url = "https://files.pythonhosted.org/packages/a8/99/253e4056e1f0e67b9390125a154b73b5eb73ad521bece95c004858fdeec2/fonttools-4.63.0-cp311-cp311-win32.whl", hash = "sha256:afefc1ed0a59785a7fb06ea7e1678e849c193e1e387db783579bc7b3056fcfcb", size = 2304473, upload-time = "2026-05-14T12:03:09.271Z" }, - { url = "https://files.pythonhosted.org/packages/08/60/defa5e69641db890a63be281f41345f4c33b157824eaf0b9fad3e08b0dcb/fonttools-4.63.0-cp311-cp311-win_amd64.whl", hash = "sha256:063e08bd17bd5a90127a14123de0d6a952dbc847695fd98b63c043d58057f90c", size = 2356389, upload-time = "2026-05-14T12:03:11.53Z" }, - { url = "https://files.pythonhosted.org/packages/08/ef/b3c6b9b5be2f82416d73fe2ed2e96e2793cd80e7510bd6a17ca79cdd88ec/fonttools-4.63.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:37dd23e621e3b0aef1baa70a303b80aaf38449632cfc8fd2a55fb285bbccfc02", size = 2881131, upload-time = "2026-05-14T12:03:13.386Z" }, - { url = "https://files.pythonhosted.org/packages/44/a0/c815bea63117fa63e4e1c01f8a1110d2112fa003f838e6467094ec2432ce/fonttools-4.63.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a9faff9e0c1f76f9fd55899d2ce785832efebab37eb8ae13995853aef178bef0", size = 2426704, upload-time = "2026-05-14T12:03:15.801Z" }, - { url = "https://files.pythonhosted.org/packages/44/04/0b91d8e916e92ad1fac9e4624760baf0fd5ff2ead614c2f68fb21373f03f/fonttools-4.63.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef3048ef05dbb552b89817713d9cac912e00d0fde4a3105c00d29e52e10c89af", size = 5044298, upload-time = "2026-05-14T12:03:18.085Z" }, - { url = "https://files.pythonhosted.org/packages/77/c7/2342da9830e3e9d4870305ca5d2091d2a83284f2953079b7bdd3b5e029d8/fonttools-4.63.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58dc6bb86a78d782f00f9190ca02c119cf5bbe2807536e361e18d42019f877d8", size = 4999800, upload-time = "2026-05-14T12:03:20.161Z" }, - { url = "https://files.pythonhosted.org/packages/e6/6d/67fe16c48d7ce050979b33f47e0d28a318f02da030602e944c34f7a16ef3/fonttools-4.63.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ee08ebfa58f6e1aeff5697ab9582105bb620008c1caafb681e4c557e7483027b", size = 4982666, upload-time = "2026-05-14T12:03:22.87Z" }, - { url = "https://files.pythonhosted.org/packages/f2/00/3bbab338c07c71fa56269953845e92c951a61457bbbb0f1022551ea266d9/fonttools-4.63.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:27fdc65af8da6f88b9c6121c47a464cbe359fcfff7ff6fc2d37a1f395d755b78", size = 5133598, upload-time = "2026-05-14T12:03:25.168Z" }, - { url = "https://files.pythonhosted.org/packages/62/f2/aa27c7f98db5b064883dadcc5283947e81e034de42e22a33675878d98b54/fonttools-4.63.0-cp312-cp312-win32.whl", hash = "sha256:af2fd1664d00a397d75f806985ddb36282091c2131a73a6485c23b4a34722263", size = 2292575, upload-time = "2026-05-14T12:03:27.496Z" }, - { url = "https://files.pythonhosted.org/packages/87/36/cccb9bc2a6ab63d1b2980374f0dca72ce95ae267c9b4cfe77455bb70d0d4/fonttools-4.63.0-cp312-cp312-win_amd64.whl", hash = "sha256:59ac449f8cca9b4ffa08d2e7bbadad87ce710d69d1eda5c3c1ce579baa987272", size = 2343211, upload-time = "2026-05-14T12:03:30.057Z" }, - { url = "https://files.pythonhosted.org/packages/0f/8d/d8fec3dcde2963f8c908fb315e5ff2cd0ac34f82394bbbf73a2aa5145ce3/fonttools-4.63.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:cd7e9857e5e63738b9d9fd707bc1f59c8b09e5177726d23664db393c59bb08bd", size = 2876062, upload-time = "2026-05-14T12:03:32.554Z" }, - { url = "https://files.pythonhosted.org/packages/ef/71/d935dc54e4ff121bfdd11e08702db63a7e6f25af21d8a3d7b7212df53641/fonttools-4.63.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c2a2a42198b696a6f48fad91709afb55176e66a5e566131219dba372fb7f8c59", size = 2424594, upload-time = "2026-05-14T12:03:34.86Z" }, - { url = "https://files.pythonhosted.org/packages/8e/40/e76320afa1df918e146155ef239b1719ee266092e96f5423bfd075affba1/fonttools-4.63.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e874792a8212b44583ea02189d9e693906b2f78b261f372f95d6c563210ac1d", size = 5024840, upload-time = "2026-05-14T12:03:36.745Z" }, - { url = "https://files.pythonhosted.org/packages/ce/36/0b805d8c485f872f65a509cbe3b58a5d0d17bee855333b54a150c79d3061/fonttools-4.63.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:22135da48a348785c5e2d5d2d9d6bec5ed44adacbaeb9db12d9493bf6c6bfa68", size = 4975801, upload-time = "2026-05-14T12:03:38.833Z" }, - { url = "https://files.pythonhosted.org/packages/c8/26/2cee03d0aa083ab022da5c07aff9ed3f689da1defb81ad6917c9627896da/fonttools-4.63.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ccf41f2efdf56994d22d73bef4ced1052161958169428d06ba9724ea9e9a64be", size = 4965009, upload-time = "2026-05-14T12:03:41.494Z" }, - { url = "https://files.pythonhosted.org/packages/7e/48/cc4b66d9058c0d0982c833fad10127c4b0e9324606aafa41382295ca4102/fonttools-4.63.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9ced0bd02ac751dd6319b0da88aaef24414e3b0dbc32bb4f24944821a3741a27", size = 5105892, upload-time = "2026-05-14T12:03:43.525Z" }, - { url = "https://files.pythonhosted.org/packages/d8/1f/a98a30a814b9ddef3a2e706025f90b9e0bc94890e6cb15254bc86547d11a/fonttools-4.63.0-cp313-cp313-win32.whl", hash = "sha256:85be818f5506e8a7753153def2c9550178f0ecae6a47b5e0e8dbb23f7cc90380", size = 2291313, upload-time = "2026-05-14T12:03:45.594Z" }, - { url = "https://files.pythonhosted.org/packages/92/46/5177b01f3b4abfdd4409f31cca4ab279c9343a26efbe9ec78c97fc612e02/fonttools-4.63.0-cp313-cp313-win_amd64.whl", hash = "sha256:ba04cb5891d4c0c21b6da95eda8d7b090021508a294fff33464fc7d241e0856b", size = 2342299, upload-time = "2026-05-14T12:03:47.414Z" }, - { url = "https://files.pythonhosted.org/packages/27/d2/23d25e3f247b328be58d04a4c9f894178a0d1eda7d42867cfb388adaf416/fonttools-4.63.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fd1e3094f42d806d3d7c79162fc59e5910fcbe3a7360c385b8da969bc4493745", size = 2875338, upload-time = "2026-05-14T12:03:50.052Z" }, - { url = "https://files.pythonhosted.org/packages/cd/58/7dfa0c761cb3b2964e2a84c4dc986c926a87de0cb9fb60d5b28ded3f2914/fonttools-4.63.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:6e528da43bc3791085f8cb6141b1d13e459226790240340fcbb4625649238b03", size = 2422661, upload-time = "2026-05-14T12:03:52.154Z" }, - { url = "https://files.pythonhosted.org/packages/dd/87/64cfa18a7a1621d17b7f4502b2b0ed8a135a90c3db51ea590ee99043e76b/fonttools-4.63.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b2248c5decb223562f7902ff6325077a073f608ee8e33e88ad88db734eb9f49", size = 5010526, upload-time = "2026-05-14T12:03:54.647Z" }, - { url = "https://files.pythonhosted.org/packages/36/e1/a8933a72c45a87177fbde2696e0d0755c8c9062f8c077a961c6215fa27b1/fonttools-4.63.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:308f957cdeaf8abe4e5f2f124902ef405448af92c90f80e302a3b771c2e6116b", size = 4923946, upload-time = "2026-05-14T12:03:56.984Z" }, - { url = "https://files.pythonhosted.org/packages/27/60/872e6e233b8c5e8b41413796ff18b7fe479661bd40147e071b450dfad7a1/fonttools-4.63.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bf00f21eb5fb721dbaf73d1e9da6d02a1af7768f2ebcf9798be98beab8ba90f6", size = 4962489, upload-time = "2026-05-14T12:03:59.443Z" }, - { url = "https://files.pythonhosted.org/packages/30/c4/83c24f2ec38b90cfda84bf4b1a1f49df80e84a1db4e7ac6e0d41bf23bc39/fonttools-4.63.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c1aaa4b9c75798400ac043ce04d74e7830376c85095a5a6ed7cba2f17a266bf4", size = 5071870, upload-time = "2026-05-14T12:04:02.122Z" }, - { url = "https://files.pythonhosted.org/packages/de/40/3ae22b60ff1d41ce0bd044b31238cdc72cef99f28b976f1e128ebd618c9b/fonttools-4.63.0-cp314-cp314-win32.whl", hash = "sha256:22693918177bd9ceabec4736d338045f357769416fc6b0b2508eefef75b08616", size = 2295026, upload-time = "2026-05-14T12:04:04.47Z" }, - { url = "https://files.pythonhosted.org/packages/c3/d4/98078064ccc76b45cb0f6c002452011e93c4bd26f6850344f0951cc1fe89/fonttools-4.63.0-cp314-cp314-win_amd64.whl", hash = "sha256:7d782fac32985914c351556f68ac0855391572bcd87de50e05970d3cd4c96fc5", size = 2347454, upload-time = "2026-05-14T12:04:06.752Z" }, - { url = "https://files.pythonhosted.org/packages/49/4e/652d1580c5f4e39f7d103b0c793e4773129ad633dce4addd0cf4dfebde02/fonttools-4.63.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:6db5140a60a5d731d21ec076745b40a310607731b0a565b50776393188649001", size = 2958152, upload-time = "2026-05-14T12:04:08.706Z" }, - { url = "https://files.pythonhosted.org/packages/0e/55/ad864c9a9b219f552eb46b32cd7906c466e5a578ba0c3abfcc0fe7413eb6/fonttools-4.63.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7d76edbff9014094dbf03bd2d074709dfa6ec7aba13d838c937a2b33d2d6a86e", size = 2460809, upload-time = "2026-05-14T12:04:10.783Z" }, - { url = "https://files.pythonhosted.org/packages/ea/2b/0aa8db70f18cf52e49b4ed5ecec68547f981160bf5ded3b5aed6faa0a6f9/fonttools-4.63.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0eac00b9118c3c2f87d272e45341871c5b3066baa3c86897fa634a7c3fb59096", size = 5148649, upload-time = "2026-05-14T12:04:12.747Z" }, - { url = "https://files.pythonhosted.org/packages/7f/63/18e4369c25043096f1048e0c9915951adc4f842bd81c6b18155824d6fa99/fonttools-4.63.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:51394295f1a51de8b5f30bdb1e1b9a4231536c7064ef5c6e211eec19fa36036f", size = 4932147, upload-time = "2026-05-14T12:04:14.806Z" }, - { url = "https://files.pythonhosted.org/packages/a1/3f/67f3eac2ffd8a98446c5022f8ed3864eac878a5ff7af8df4c8286dba16cc/fonttools-4.63.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9e12f105d2b6342c559c298afb674006bb2893afc7102dcf8a1b55b0486b4e40", size = 5027237, upload-time = "2026-05-14T12:04:17.675Z" }, - { url = "https://files.pythonhosted.org/packages/1a/ba/4e6214cb38a7b04779e97bb7636de9a5c7f20af7018d03dee0b64c08510a/fonttools-4.63.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:796f27556dbe094c4824f75ca85267e4df776c79036c8441469a4df37038c196", size = 5053933, upload-time = "2026-05-14T12:04:20.818Z" }, - { url = "https://files.pythonhosted.org/packages/34/3b/214dcc19ee31d3d38fb5ad2755c11ef0514e5dc300bbaf41c0b69f393799/fonttools-4.63.0-cp314-cp314t-win32.whl", hash = "sha256:948428a275741f0b64b113c955425a953314f4b9ab9997f73a72c83e68e569c8", size = 2359326, upload-time = "2026-05-14T12:04:24.22Z" }, - { url = "https://files.pythonhosted.org/packages/dd/1e/3ff1a9b523058c2eeb6a9d50f5574e2a738200d0d94107d5bc4105e8da3f/fonttools-4.63.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6d4741eb179121cab9eea4cb2393d24492373a260d7945006358c08cfbf45419", size = 2425829, upload-time = "2026-05-14T12:04:26.829Z" }, - { url = "https://files.pythonhosted.org/packages/2c/47/c99d5268f354002ce80f8d029cd9d7d872969da1de8b93d32de4dc56d6f4/fonttools-4.63.0-py3-none-any.whl", hash = "sha256:445af2eab030a16b9171ea8bdda7ebf7d96bda2df88ee182a464252f6e05e20d", size = 1164562, upload-time = "2026-05-14T12:04:29.092Z" }, -] - -[[package]] -name = "fsspec" -version = "2026.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d5/8d/1c51c094345df128ca4a990d633fe1a0ff28726c9e6b3c41ba65087bba1d/fsspec-2026.4.0.tar.gz", hash = "sha256:301d8ac70ae90ef3ad05dcf94d6c3754a097f9b5fe4667d2787aa359ec7df7e4", size = 312760, upload-time = "2026-04-29T20:42:38.635Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/0c/043d5e551459da400957a1395e0febbf771446ff34291afcbe3d8be2a279/fsspec-2026.4.0-py3-none-any.whl", hash = "sha256:11ef7bb35dab8a394fde6e608221d5cf3e8499401c249bebaeaad760a1a8dec2", size = 203402, upload-time = "2026-04-29T20:42:36.842Z" }, -] - -[[package]] -name = "george" -version = "0.4.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/dc/35/1d0a2a7d190dc7406678c78f5a5aeeeab39a3d4ac4021867cd5680928c5f/george-0.4.4.tar.gz", hash = "sha256:dbf5e10aded012a67c48dc52bed5636523552a4ff2775946bad8b9cca9b501e8", size = 5465405, upload-time = "2025-04-12T10:42:22.272Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/91/bcd8ba2923767ac5ea5472331a87736c9c2a2d555953852661c224ba73d4/george-0.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9289a224c39aa382189bfd9ee492ccc6f97431aa3425b7651de3ba7922024758", size = 284613, upload-time = "2025-04-12T10:41:59.361Z" }, - { url = "https://files.pythonhosted.org/packages/5e/c1/c6aaf530580f78c3d89b36ee74d6f774e1d0d17dfd11fcf987119d9aee6f/george-0.4.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e8c66370ae3ce44f0c2d1759bd4fbffdf18daf05eb1712049892a0e2f806722", size = 397262, upload-time = "2025-04-12T10:42:01.323Z" }, - { url = "https://files.pythonhosted.org/packages/91/db/7b0f5abaab57ea4c7e2bc88d63828dbac383f881fffd6a0afa45437c5c56/george-0.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:6c03dbe18799c746e0d22d3667507b2b7d088bb087ffd07d6b3bcfaa2e23e59e", size = 297557, upload-time = "2025-04-12T10:42:02.576Z" }, - { url = "https://files.pythonhosted.org/packages/3a/1f/9a03c0b5d5cbf5c2e2cf2dd4d0d23272b5ca0b852eee62dc025bf10d72dc/george-0.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:be3a6a19e8fa42f8d884c71910e13c18a274097a8368485718a9d025b383378a", size = 287161, upload-time = "2025-04-12T10:42:04.135Z" }, - { url = "https://files.pythonhosted.org/packages/b2/ab/268f3a44becbfe32f7918f4283d480b57ce9b68f773677faae14810ebde0/george-0.4.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdf71de76baab42e4fba16e9c564652d86c1910777b3722d9f5f6b42669604a9", size = 399075, upload-time = "2025-04-12T10:42:05.681Z" }, - { url = "https://files.pythonhosted.org/packages/ad/80/8bf71d45c7c6f750bbab5b5d0cb9fa1b140d912ffa6d9b0fc1ee855255fd/george-0.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:dbd6c64e223394372a8084c5f17d86f8d8cdfe70eee93f0503905a0c4a3505c2", size = 300973, upload-time = "2025-04-12T10:42:06.976Z" }, - { url = "https://files.pythonhosted.org/packages/46/d0/650bba57c684ba9527875c63b2fcb5ea7ee38f61c69f071605ff8c0b318b/george-0.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6ad5d2e31f1916d2816b033eafb23db99b9b9e61a2cda80a41b6a332ba15f77c", size = 285049, upload-time = "2025-04-12T10:42:08.585Z" }, - { url = "https://files.pythonhosted.org/packages/a1/14/0dd927b3644beb3b1ed91f502db797659f484c10ebd9821dbb8c6fae2412/george-0.4.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff67d0e306f2a2c0409b565ae0bd7e225d9d6c17e83a596c6e704f5cee962e5", size = 395606, upload-time = "2025-04-12T10:42:10.221Z" }, - { url = "https://files.pythonhosted.org/packages/cb/5e/20a210bd5c6a5e564b8d556cf43af21b7eaff8fde6c878c4d2039cfdd583/george-0.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:9fd173697c07a31b36d3b471b44dc0f0ecf3b021cb4ab18198a93516381c99b9", size = 308648, upload-time = "2025-04-12T10:42:11.754Z" }, - { url = "https://files.pythonhosted.org/packages/f8/d6/d7d04e73e047114d747fcff4b5a2580e852bac1916a7a28853e4df56ae9f/george-0.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:23dcf23f1af7feab163e280a2c59744b6b1cd50b220abe99b5d3a7b565719400", size = 285176, upload-time = "2025-04-12T10:42:12.864Z" }, - { url = "https://files.pythonhosted.org/packages/41/fd/84151712469ba168eedf73f6f44cc9f85316428b9ba05d38068093f1ee62/george-0.4.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f39c133bab7e6aa06363e8e8bcbb49d1754b64dede950d907d9e152ab54d37a5", size = 395425, upload-time = "2025-04-12T10:42:14.08Z" }, - { url = "https://files.pythonhosted.org/packages/49/05/3d9b1dfaa6bc735232f0d4711d7c4ba6a632484c8793b4aa9535a1c6eea7/george-0.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:e1011dafc3a4d4f8c358e98c0b3e3f11cf523dee9e271c8c15c294080287950d", size = 308736, upload-time = "2025-04-12T10:42:15.698Z" }, -] - -[[package]] -name = "glasflow" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "torch" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/39/6d/3b6c3468c2b1c306dc30d848652ef295e772a6f37d624573b726eca904d7/glasflow-0.4.1.tar.gz", hash = "sha256:e97eaf549743bc4ca6d05434f89ea93102f429f56322a490fca3888c44522c3d", size = 64176, upload-time = "2024-10-10T16:55:45.93Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/09/8d/11398cd06b2b2d8f7e5e34deff766bb8d93b869c88a29fb75a9c961d480a/glasflow-0.4.1-py3-none-any.whl", hash = "sha256:5579bcb53753e5f35f99398f82260947940bd5849565d64436164984158f5245", size = 76605, upload-time = "2024-10-10T16:55:44.539Z" }, -] - -[[package]] -name = "gwdatafind" -version = "2.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "igwn-auth-utils" }, - { name = "igwn-segments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/69/dd/4f517a2a36f71d5bb74b8b0796e3fec87703587503d4583f4a5b962ae60b/gwdatafind-2.1.1.tar.gz", hash = "sha256:e4710256daa7b47e901da2f2846620c551e9caaaaf22b7773c81a8ae052da43e", size = 41311, upload-time = "2025-10-31T09:46:57.9Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/00/07a1a9473ea5bbbc0be3cea79778dfd31e7e199404777907930badc9f399/gwdatafind-2.1.1-py3-none-any.whl", hash = "sha256:6e6d430fa243e6241ca0c214f1916f7973cf1937716bbca52d99a9b88650faeb", size = 45178, upload-time = "2025-10-31T09:46:56.468Z" }, -] - -[[package]] -name = "gwosc" -version = "0.8.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "requests" }, - { name = "tenacity" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/db/28/3dfccc399aca4da49354fb612ade049950ec73b4b34ec1089bb7dbb5a7fb/gwosc-0.8.2.tar.gz", hash = "sha256:6a7a97f1fd86184841f2f9d83eeccf9b3fc889104d8018ecb08276a1a4f6336b", size = 35171, upload-time = "2026-04-02T19:00:17.198Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/da/f3f9983547f257de9d359bcc358cdc8254b8cb00d78fe692a60b463f0ff0/gwosc-0.8.2-py3-none-any.whl", hash = "sha256:ec1ecc667e9b148e8229c0a71290921eec4e60c674096d3d639c9f44c198efeb", size = 35151, upload-time = "2026-04-02T19:00:16.09Z" }, -] - -[[package]] -name = "gwpy" -version = "3.0.14" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -dependencies = [ - { name = "astropy", version = "6.1.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "dateparser", marker = "python_full_version < '3.11'" }, - { name = "dqsegdb2", marker = "python_full_version < '3.11'" }, - { name = "gwdatafind", marker = "python_full_version < '3.11'" }, - { name = "gwosc", marker = "python_full_version < '3.11'" }, - { name = "h5py", marker = "python_full_version < '3.11'" }, - { name = "igwn-segments", marker = "python_full_version < '3.11'" }, - { name = "ligotimegps", version = "2.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "matplotlib", marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "packaging", marker = "python_full_version < '3.11'" }, - { name = "python-dateutil", marker = "python_full_version < '3.11'" }, - { name = "requests", marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "tqdm", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/05/cd/bb165e47b18cd255c1ee1fcd59802544d8de57566b449cfdaf848b69108f/gwpy-3.0.14.tar.gz", hash = "sha256:bf25d19763c9128f515144349d3f70925a8334bbd22950c01d35bb954f323f52", size = 1543294, upload-time = "2026-01-16T13:02:28.432Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/46/c893a66bd97f08ddf4a7cfde52cc58fb8e2c83acfa915f3c02c00f0aa721/gwpy-3.0.14-py3-none-any.whl", hash = "sha256:f61ed9d4b3eba7f9f534f72f03956f158ed7f9101726b5387c29fe57ce45c77e", size = 1395825, upload-time = "2026-01-16T13:02:26.55Z" }, -] - -[[package]] -name = "gwpy" -version = "4.0.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'win32'", - "python_full_version == '3.12.*' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version == '3.12.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", -] -dependencies = [ - { name = "astropy", version = "7.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "dateparser", marker = "python_full_version >= '3.11'" }, - { name = "dqsegdb2", marker = "python_full_version >= '3.11'" }, - { name = "gwdatafind", marker = "python_full_version >= '3.11'" }, - { name = "gwosc", marker = "python_full_version >= '3.11'" }, - { name = "h5py", marker = "python_full_version >= '3.11'" }, - { name = "igwn-segments", marker = "python_full_version >= '3.11'" }, - { name = "ligotimegps", version = "2.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "matplotlib", marker = "python_full_version >= '3.11'" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "packaging", marker = "python_full_version >= '3.11'" }, - { name = "python-dateutil", marker = "python_full_version >= '3.11'" }, - { name = "requests", marker = "python_full_version >= '3.11'" }, - { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "tqdm", marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/92/71/1e652734a0e8f23e0f9dbf5df5698e66fa49aef4446d2c6c9eb914921192/gwpy-4.0.1.tar.gz", hash = "sha256:477aa69bd40506bfb0df5ec779d9dd6fef562a3bfabe58a52b297e25c57f61f0", size = 1615849, upload-time = "2026-02-03T10:14:59.045Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/71/ac/1debaaec791da4475687d22c8c2cbccc2008885046754f5e9f65997886b8/gwpy-4.0.1-py3-none-any.whl", hash = "sha256:587eb66443e41450bd89bf64891e323943afad3be385fccc1645eda2710908b7", size = 1559010, upload-time = "2026-02-03T10:14:56.928Z" }, -] - -[[package]] -name = "h5py" -version = "3.16.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/db/33/acd0ce6863b6c0d7735007df01815403f5589a21ff8c2e1ee2587a38f548/h5py-3.16.0.tar.gz", hash = "sha256:a0dbaad796840ccaa67a4c144a0d0c8080073c34c76d5a6941d6818678ef2738", size = 446526, upload-time = "2026-03-06T13:49:08.07Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/6b/231413e58a787a89b316bb0d1777da3c62257e4797e09afd8d17ad3549dc/h5py-3.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e06f864bedb2c8e7c1358e6c73af48519e317457c444d6f3d332bb4e8fa6d7d9", size = 3724137, upload-time = "2026-03-06T13:47:35.242Z" }, - { url = "https://files.pythonhosted.org/packages/74/f9/557ce3aad0fe8471fb5279bab0fc56ea473858a022c4ce8a0b8f303d64e9/h5py-3.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ec86d4fffd87a0f4cb3d5796ceb5a50123a2a6d99b43e616e5504e66a953eca3", size = 3090112, upload-time = "2026-03-06T13:47:37.634Z" }, - { url = "https://files.pythonhosted.org/packages/7a/f5/e15b3d0dc8a18e56409a839e6468d6fb589bc5207c917399c2e0706eeb44/h5py-3.16.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:86385ea895508220b8a7e45efa428aeafaa586bd737c7af9ee04661d8d84a10d", size = 4844847, upload-time = "2026-03-06T13:47:39.811Z" }, - { url = "https://files.pythonhosted.org/packages/cb/92/a8851d936547efe30cc0ce5245feac01f3ec6171f7899bc3f775c72030b3/h5py-3.16.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:8975273c2c5921c25700193b408e28d6bdd0111c37468b2d4e25dcec4cd1d84d", size = 5065352, upload-time = "2026-03-06T13:47:41.489Z" }, - { url = "https://files.pythonhosted.org/packages/2b/ae/f2adc5d0ca9626db3277a3d87516e124cbc5d0eea0bd79bc085702d04f2c/h5py-3.16.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1677ad48b703f44efc9ea0c3ab284527f81bc4f318386aaaebc5fede6bbae56f", size = 4839173, upload-time = "2026-03-06T13:47:43.586Z" }, - { url = "https://files.pythonhosted.org/packages/64/0b/e0c8c69da1d8838da023a50cd3080eae5d475691f7636b35eff20bb6ef20/h5py-3.16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c4dd4cf5f0a4e36083f73172f6cfc25a5710789269547f132a20975bfe2434c", size = 5076216, upload-time = "2026-03-06T13:47:45.315Z" }, - { url = "https://files.pythonhosted.org/packages/66/35/d88fd6718832133c885004c61ceeeb24dbd6397ef877dbed6b3a64d6a286/h5py-3.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:bdef06507725b455fccba9c16529121a5e1fbf56aa375f7d9713d9e8ff42454d", size = 3183639, upload-time = "2026-03-06T13:47:47.041Z" }, - { url = "https://files.pythonhosted.org/packages/ba/95/a825894f3e45cbac7554c4e97314ce886b233a20033787eda755ca8fecc7/h5py-3.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:719439d14b83f74eeb080e9650a6c7aa6d0d9ea0ca7f804347b05fac6fbf18af", size = 3721663, upload-time = "2026-03-06T13:47:49.599Z" }, - { url = "https://files.pythonhosted.org/packages/bf/3b/38ff88b347c3e346cda1d3fc1b65a7aa75d40632228d8b8a5d7b58508c24/h5py-3.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c3f0a0e136f2e95dd0b67146abb6668af4f1a69c81ef8651a2d316e8e01de447", size = 3087630, upload-time = "2026-03-06T13:47:51.249Z" }, - { url = "https://files.pythonhosted.org/packages/98/a8/2594cef906aee761601eff842c7dc598bea2b394a3e1c00966832b8eeb7c/h5py-3.16.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:a6fbc5367d4046801f9b7db9191b31895f22f1c6df1f9987d667854cac493538", size = 4823472, upload-time = "2026-03-06T13:47:53.085Z" }, - { url = "https://files.pythonhosted.org/packages/52/a0/c1f604538ff6db22a0690be2dc44ab59178e115f63c917794e529356ab23/h5py-3.16.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:fb1720028d99040792bb2fb31facb8da44a6f29df7697e0b84f0d79aff2e9bd3", size = 5027150, upload-time = "2026-03-06T13:47:55.043Z" }, - { url = "https://files.pythonhosted.org/packages/2e/fd/301739083c2fc4fd89950f9bcfce75d6e14b40b0ca3d40e48a8993d1722c/h5py-3.16.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:314b6054fe0b1051c2b0cb2df5cbdab15622fb05e80f202e3b6a5eee0d6fe365", size = 4814544, upload-time = "2026-03-06T13:47:56.893Z" }, - { url = "https://files.pythonhosted.org/packages/4c/42/2193ed41ccee78baba8fcc0cff2c925b8b9ee3793305b23e1f22c20bf4c7/h5py-3.16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ffbab2fedd6581f6aa31cf1639ca2cb86e02779de525667892ebf4cc9fd26434", size = 5034013, upload-time = "2026-03-06T13:47:59.01Z" }, - { url = "https://files.pythonhosted.org/packages/f7/20/e6c0ff62ca2ad1a396a34f4380bafccaaf8791ff8fccf3d995a1fc12d417/h5py-3.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:17d1f1630f92ad74494a9a7392ab25982ce2b469fc62da6074c0ce48366a2999", size = 3191673, upload-time = "2026-03-06T13:48:00.626Z" }, - { url = "https://files.pythonhosted.org/packages/f2/48/239cbe352ac4f2b8243a8e620fa1a2034635f633731493a7ff1ed71e8658/h5py-3.16.0-cp311-cp311-win_arm64.whl", hash = "sha256:85b9c49dd58dc44cf70af944784e2c2038b6f799665d0dcbbc812a26e0faa859", size = 2673834, upload-time = "2026-03-06T13:48:02.579Z" }, - { url = "https://files.pythonhosted.org/packages/c8/c0/5d4119dba94093bbafede500d3defd2f5eab7897732998c04b54021e530b/h5py-3.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c5313566f4643121a78503a473f0fb1e6dcc541d5115c44f05e037609c565c4d", size = 3685604, upload-time = "2026-03-06T13:48:04.198Z" }, - { url = "https://files.pythonhosted.org/packages/b0/42/c84efcc1d4caebafb1ecd8be4643f39c85c47a80fe254d92b8b43b1eadaf/h5py-3.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:42b012933a83e1a558c673176676a10ce2fd3759976a0fedee1e672d1e04fc9d", size = 3061940, upload-time = "2026-03-06T13:48:05.783Z" }, - { url = "https://files.pythonhosted.org/packages/89/84/06281c82d4d1686fde1ac6b0f307c50918f1c0151062445ab3b6fa5a921d/h5py-3.16.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:ff24039e2573297787c3063df64b60aab0591980ac898329a08b0320e0cf2527", size = 5198852, upload-time = "2026-03-06T13:48:07.482Z" }, - { url = "https://files.pythonhosted.org/packages/9e/e9/1a19e42cd43cc1365e127db6aae85e1c671da1d9a5d746f4d34a50edb577/h5py-3.16.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:dfc21898ff025f1e8e67e194965a95a8d4754f452f83454538f98f8a3fcb207e", size = 5405250, upload-time = "2026-03-06T13:48:09.628Z" }, - { url = "https://files.pythonhosted.org/packages/b7/8e/9790c1655eabeb85b92b1ecab7d7e62a2069e53baefd58c98f0909c7a948/h5py-3.16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:698dd69291272642ffda44a0ecd6cd3bda5faf9621452d255f57ce91487b9794", size = 5190108, upload-time = "2026-03-06T13:48:11.26Z" }, - { url = "https://files.pythonhosted.org/packages/51/d7/ab693274f1bd7e8c5f9fdd6c7003a88d59bedeaf8752716a55f532924fbb/h5py-3.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2b2c02b0a160faed5fb33f1ba8a264a37ee240b22e049ecc827345d0d9043074", size = 5419216, upload-time = "2026-03-06T13:48:13.322Z" }, - { url = "https://files.pythonhosted.org/packages/03/c1/0976b235cf29ead553e22f2fb6385a8252b533715e00d0ae52ed7b900582/h5py-3.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:96b422019a1c8975c2d5dadcf61d4ba6f01c31f92bbde6e4649607885fe502d6", size = 3182868, upload-time = "2026-03-06T13:48:15.759Z" }, - { url = "https://files.pythonhosted.org/packages/14/d9/866b7e570b39070f92d47b0ff1800f0f8239b6f9e45f02363d7112336c1f/h5py-3.16.0-cp312-cp312-win_arm64.whl", hash = "sha256:39c2838fb1e8d97bcf1755e60ad1f3dd76a7b2a475928dc321672752678b96db", size = 2653286, upload-time = "2026-03-06T13:48:17.279Z" }, - { url = "https://files.pythonhosted.org/packages/0f/9e/6142ebfda0cb6e9349c091eae73c2e01a770b7659255248d637bec54a88b/h5py-3.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:370a845f432c2c9619db8eed334d1e610c6015796122b0e57aa46312c22617d9", size = 3671808, upload-time = "2026-03-06T13:48:19.737Z" }, - { url = "https://files.pythonhosted.org/packages/b0/65/5e088a45d0f43cd814bc5bec521c051d42005a472e804b1a36c48dada09b/h5py-3.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:42108e93326c50c2810025aade9eac9d6827524cdccc7d4b75a546e5ab308edb", size = 3045837, upload-time = "2026-03-06T13:48:21.854Z" }, - { url = "https://files.pythonhosted.org/packages/da/1e/6172269e18cc5a484e2913ced33339aad588e02ba407fafd00d369e22ef3/h5py-3.16.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:099f2525c9dcf28de366970a5fb34879aab20491589fa89ce2863a84218bb524", size = 5193860, upload-time = "2026-03-06T13:48:24.071Z" }, - { url = "https://files.pythonhosted.org/packages/bd/98/ef2b6fe2903e377cbe870c3b2800d62552f1e3dbe81ce49e1923c53d1c5c/h5py-3.16.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:9300ad32dea9dfc5171f94d5f6948e159ed93e4701280b0f508773b3f582f402", size = 5400417, upload-time = "2026-03-06T13:48:25.728Z" }, - { url = "https://files.pythonhosted.org/packages/bc/81/5b62d760039eed64348c98129d17061fdfc7839fc9c04eaaad6dee1004e4/h5py-3.16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:171038f23bccddfc23f344cadabdfc9917ff554db6a0d417180d2747fe4c75a7", size = 5185214, upload-time = "2026-03-06T13:48:27.436Z" }, - { url = "https://files.pythonhosted.org/packages/28/c4/532123bcd9080e250696779c927f2cb906c8bf3447df98f5ceb8dcded539/h5py-3.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7e420b539fb6023a259a1b14d4c9f6df8cf50d7268f48e161169987a57b737ff", size = 5414598, upload-time = "2026-03-06T13:48:29.49Z" }, - { url = "https://files.pythonhosted.org/packages/c3/d9/a27997f84341fc0dfcdd1fe4179b6ba6c32a7aa880fdb8c514d4dad6fba3/h5py-3.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:18f2bbcd545e6991412253b98727374c356d67caa920e68dc79eab36bf5fedad", size = 3175509, upload-time = "2026-03-06T13:48:31.131Z" }, - { url = "https://files.pythonhosted.org/packages/a5/23/bb8647521d4fd770c30a76cfc6cb6a2f5495868904054e92f2394c5a78ff/h5py-3.16.0-cp313-cp313-win_arm64.whl", hash = "sha256:656f00e4d903199a1d58df06b711cf3ca632b874b4207b7dbec86185b5c8c7d4", size = 2647362, upload-time = "2026-03-06T13:48:33.411Z" }, - { url = "https://files.pythonhosted.org/packages/48/3c/7fcd9b4c9eed82e91fb15568992561019ae7a829d1f696b2c844355d95dd/h5py-3.16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9c9d307c0ef862d1cd5714f72ecfafe0a5d7529c44845afa8de9f46e5ba8bd65", size = 3678608, upload-time = "2026-03-06T13:48:35.183Z" }, - { url = "https://files.pythonhosted.org/packages/6a/b7/9366ed44ced9b7ef357ab48c94205280276db9d7f064aa3012a97227e966/h5py-3.16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8c1eff849cdd53cbc73c214c30ebdb6f1bb8b64790b4b4fc36acdb5e43570210", size = 3054773, upload-time = "2026-03-06T13:48:37.139Z" }, - { url = "https://files.pythonhosted.org/packages/58/a5/4964bc0e91e86340c2bbda83420225b2f770dcf1eb8a39464871ad769436/h5py-3.16.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:e2c04d129f180019e216ee5f9c40b78a418634091c8782e1f723a6ca3658b965", size = 5198886, upload-time = "2026-03-06T13:48:38.879Z" }, - { url = "https://files.pythonhosted.org/packages/f1/16/d905e7f53e661ce2c24686c38048d8e2b750ffc4350009d41c4e6c6c9826/h5py-3.16.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:e4360f15875a532bc7b98196c7592ed4fc92672a57c0a621355961cafb17a6dd", size = 5404883, upload-time = "2026-03-06T13:48:41.324Z" }, - { url = "https://files.pythonhosted.org/packages/4b/f2/58f34cb74af46d39f4cd18ea20909a8514960c5a3e5b92fd06a28161e0a8/h5py-3.16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3fae9197390c325e62e0a1aa977f2f62d994aa87aab182abbea85479b791197c", size = 5192039, upload-time = "2026-03-06T13:48:43.117Z" }, - { url = "https://files.pythonhosted.org/packages/ce/ca/934a39c24ce2e2db017268c08da0537c20fa0be7e1549be3e977313fc8f5/h5py-3.16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:43259303989ac8adacc9986695b31e35dba6fd1e297ff9c6a04b7da5542139cc", size = 5421526, upload-time = "2026-03-06T13:48:44.838Z" }, - { url = "https://files.pythonhosted.org/packages/3e/14/615a450205e1b56d16c6783f5ccd116cde05550faad70ae077c955654a75/h5py-3.16.0-cp314-cp314-win_amd64.whl", hash = "sha256:fa48993a0b799737ba7fd21e2350fa0a60701e58180fae9f2de834bc39a147ab", size = 3183263, upload-time = "2026-03-06T13:48:47.117Z" }, - { url = "https://files.pythonhosted.org/packages/7b/48/a6faef5ed632cae0c65ac6b214a6614a0b510c3183532c521bdb0055e117/h5py-3.16.0-cp314-cp314-win_arm64.whl", hash = "sha256:1897a771a7f40d05c262fc8f37376ec37873218544b70216872876c627640f63", size = 2663450, upload-time = "2026-03-06T13:48:48.707Z" }, - { url = "https://files.pythonhosted.org/packages/5d/32/0c8bb8aedb62c772cf7c1d427c7d1951477e8c2835f872bc0a13d1f85f86/h5py-3.16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:15922e485844f77c0b9d275396d435db3baa58292a9c2176a386e072e0cf2491", size = 3760693, upload-time = "2026-03-06T13:48:50.453Z" }, - { url = "https://files.pythonhosted.org/packages/1d/1f/fcc5977d32d6387c5c9a694afee716a5e20658ac08b3ff24fdec79fb05f2/h5py-3.16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:df02dd29bd247f98674634dfe41f89fd7c16ba3d7de8695ec958f58404a4e618", size = 3181305, upload-time = "2026-03-06T13:48:52.221Z" }, - { url = "https://files.pythonhosted.org/packages/f5/a1/af87f64b9f986889884243643621ebbd4ac72472ba8ec8cec891ac8e2ca1/h5py-3.16.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:0f456f556e4e2cebeebd9d66adf8dc321770a42593494a0b6f0af54a7567b242", size = 5074061, upload-time = "2026-03-06T13:48:54.089Z" }, - { url = "https://files.pythonhosted.org/packages/cc/d0/146f5eaff3dc246a9c7f6e5e4f42bd45cc613bce16693bcd4d1f7c958bf5/h5py-3.16.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:3e6cb3387c756de6a9492d601553dffea3fe11b5f22b443aac708c69f3f55e16", size = 5279216, upload-time = "2026-03-06T13:48:56.75Z" }, - { url = "https://files.pythonhosted.org/packages/a1/9d/12a13424f1e604fc7df9497b73c0356fb78c2fb206abd7465ce47226e8fd/h5py-3.16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8389e13a1fd745ad2856873e8187fd10268b2d9677877bb667b41aebd771d8b7", size = 5070068, upload-time = "2026-03-06T13:48:59.169Z" }, - { url = "https://files.pythonhosted.org/packages/41/8c/bbe98f813722b4873818a8db3e15aa3e625b59278566905ac439725e8070/h5py-3.16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:346df559a0f7dcb31cf8e44805319e2ab24b8957c45e7708ce503b2ec79ba725", size = 5300253, upload-time = "2026-03-06T13:49:02.033Z" }, - { url = "https://files.pythonhosted.org/packages/32/9e/87e6705b4d6890e7cecdf876e2a7d3e40654a2ae37482d79a6f1b87f7b92/h5py-3.16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:4c6ab014ab704b4feaa719ae783b86522ed0bf1f82184704ed3c9e4e3228796e", size = 3381671, upload-time = "2026-03-06T13:49:04.351Z" }, - { url = "https://files.pythonhosted.org/packages/96/91/9fad90cfc5f9b2489c7c26ad897157bce82f0e9534a986a221b99760b23b/h5py-3.16.0-cp314-cp314t-win_arm64.whl", hash = "sha256:faca8fb4e4319c09d83337adc80b2ca7d5c5a343c2d6f1b6388f32cfecca13c1", size = 2740706, upload-time = "2026-03-06T13:49:06.347Z" }, -] - -[[package]] -name = "idna" -version = "3.15" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/82/77/7b3966d0b9d1d31a36ddf1746926a11dface89a83409bf1483f0237aa758/idna-3.15.tar.gz", hash = "sha256:ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc", size = 199245, upload-time = "2026-05-12T22:45:57.011Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/23/408243171aa9aaba178d3e2559159c24c1171a641aa83b67bdd3394ead8e/idna-3.15-py3-none-any.whl", hash = "sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8", size = 72340, upload-time = "2026-05-12T22:45:55.733Z" }, -] - -[[package]] -name = "igwn-auth-utils" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, - { name = "requests" }, - { name = "safe-netrc" }, - { name = "scitokens" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e9/88/a94ef60db81bb38137a3a7278a71efe729623bfa4a5bf4cabed8a8f44b69/igwn_auth_utils-1.4.0.tar.gz", hash = "sha256:8ebd331a1d6de16e843e94cde2dc0a09d07a7fbc089bc525fa0eabddd89ea187", size = 40693, upload-time = "2025-05-30T14:35:33.009Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/70/a0/6d3b9b7ff91c1cc73a0f45972a5032a4e89439ffb16e617806867cee905b/igwn_auth_utils-1.4.0-py3-none-any.whl", hash = "sha256:3936dbc4e630b1a31eeb1376abf25ac7b518629f84e34ecc37a9d88ebd86e582", size = 34292, upload-time = "2025-05-30T14:35:31.861Z" }, -] - -[[package]] -name = "igwn-ligolw" -version = "2.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "igwn-segments" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "python-dateutil" }, - { name = "pyyaml" }, - { name = "tqdm" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/74/56/7bd63ee4a7bd64b5104b2c3a3cf4cd291865021fd4e50b14974b054a9928/igwn_ligolw-2.1.1.tar.gz", hash = "sha256:4ca1860e79ec5e25d575ae4e27fa8035a269af6fcda9da33f090ceb85b34b64b", size = 2339801, upload-time = "2026-01-19T19:01:35.462Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d3/29/0c3d0c0f1664b49793125169d61c3c8e3d2af82cc13b8f4fb24e6865a56f/igwn_ligolw-2.1.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:94986bdc229a70b714498453ba62edb6e1ebed0da757d2b9b824fe753253ece1", size = 139994, upload-time = "2026-01-19T19:00:50.836Z" }, - { url = "https://files.pythonhosted.org/packages/fc/17/3569ca16c7520ba8a4933913e6fb8c83605a882d75dba56db22ddcb64063/igwn_ligolw-2.1.1-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:b5c42d9e55ed73bda5e69268ac7a4ba3a7dcaa42bf6915ba498685a2a1d54583", size = 139955, upload-time = "2026-01-19T19:00:52.114Z" }, - { url = "https://files.pythonhosted.org/packages/41/61/e9874d55a9b6a48f4733adeced394d06f0856bf0367ead158443f9620b9e/igwn_ligolw-2.1.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cb3eb5f4b1f3412f4780f126216efbc2b0a42548f5b319e530bb57f729478410", size = 163027, upload-time = "2026-01-19T19:00:53.886Z" }, - { url = "https://files.pythonhosted.org/packages/18/e4/709e8d1ea210e582ee9afb2c7116f9b276a445d6b0c54229aee32821e8ef/igwn_ligolw-2.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1375a3a7a72b2841f5a3929d345d7ea86f0d9e068ca8fef1b32b87eab8417d14", size = 164281, upload-time = "2026-01-19T19:00:55.39Z" }, - { url = "https://files.pythonhosted.org/packages/e5/87/522af9c807a4ac0b22b5c3b2c490d28f04d215f2800e045c28f52ca04843/igwn_ligolw-2.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c1e39ce8160f512f68a2a98f3d5e4077de45c96bbd7e0a4e43627c92d1dbe4f4", size = 163312, upload-time = "2026-01-19T19:00:56.418Z" }, - { url = "https://files.pythonhosted.org/packages/f3/e1/f15beefcf28134e40aba0b4a6090ddf802d210ec07259d5e4cd9b1ba1744/igwn_ligolw-2.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6c03363fb6abb5f5e7a801a6e32e83c8af3249211b800bd33046bff9c779a4ad", size = 162864, upload-time = "2026-01-19T19:00:57.641Z" }, - { url = "https://files.pythonhosted.org/packages/4d/46/3966e1c23a961440698c00010622e0a2f32b11e46c660915d8a72bd259e8/igwn_ligolw-2.1.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:d38825dff55331a35f7280bdfc7b46f82e649499679dfc5e7af4fecfba5fa0ae", size = 139997, upload-time = "2026-01-19T19:00:58.792Z" }, - { url = "https://files.pythonhosted.org/packages/42/08/8c9a9a1c56abfbf7736721c2b430d9b03067c9e7190a348af70b9ef6b61e/igwn_ligolw-2.1.1-cp311-cp311-macosx_13_0_x86_64.whl", hash = "sha256:5b1ebbdece02c73c112f7bfcf54687b8885d2399c904e3bb953840c2461b7d97", size = 139958, upload-time = "2026-01-19T19:01:00.061Z" }, - { url = "https://files.pythonhosted.org/packages/e9/d6/c8da65edf532a1d166aa433227cabd3a0193769c928362304900e5bd9209/igwn_ligolw-2.1.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ff984d9ea6d83e03ea604b8761fca5c43862a22c856f619a624150b55aad4850", size = 165917, upload-time = "2026-01-19T19:01:01.092Z" }, - { url = "https://files.pythonhosted.org/packages/ec/e0/08766c66048cb905e2c3ab4b5f3864786a79845ed7c3cc7163c52c3cfea5/igwn_ligolw-2.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56d359a2a2271b8db33021f9dcc09d331953451a7b56b980ae245fd9d21f4de5", size = 167203, upload-time = "2026-01-19T19:01:02.272Z" }, - { url = "https://files.pythonhosted.org/packages/57/1d/cbf99ca2a6f29b169662cb66ed02246521b1f638761e725532962ba32e37/igwn_ligolw-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e9bde91cf3208bac074400e339c06f9ffd0836f8021981d3637f3ee7786de746", size = 166137, upload-time = "2026-01-19T19:01:03.323Z" }, - { url = "https://files.pythonhosted.org/packages/58/26/abc641f6f311853b44905c36e9f5cfd3d58d1b766c13adf1259655091489/igwn_ligolw-2.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4330d2350f779b8265df79b39b44520e4545a3ca3e25121521d6423f09e717d7", size = 165710, upload-time = "2026-01-19T19:01:04.607Z" }, - { url = "https://files.pythonhosted.org/packages/37/cd/9b72058881126d7fcdd8b29c306c345417996259faf44f2b1c3f5e52e7cd/igwn_ligolw-2.1.1-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:c45d42d7b3752c7452e88c5bbf98ebb71b84446e6d2dcb84f6779acd0f5a02ec", size = 140111, upload-time = "2026-01-19T19:01:05.841Z" }, - { url = "https://files.pythonhosted.org/packages/7e/a5/0941742b1bd313a451087f37448033d2a053c0260741d2648a1daa7a6b01/igwn_ligolw-2.1.1-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:9a31c61dae5eb5ab48dc8f8ec16e7fe454710d40154fecea9d25272a490d5c0b", size = 140387, upload-time = "2026-01-19T19:01:06.993Z" }, - { url = "https://files.pythonhosted.org/packages/29/3b/54173d9a3691c6cdcce6b7971e46af0436feebab00585966c37a5e6bbd3c/igwn_ligolw-2.1.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5b72a5a5d2b630fbfb659102bc9dc06a5f9a0b4740ca2202d319dcba172642d1", size = 168723, upload-time = "2026-01-19T19:01:08.286Z" }, - { url = "https://files.pythonhosted.org/packages/2b/1c/ab0e78d5fe594ff700eb17367e4fd92ba5ed4c95c24fcaa1e70327b24b90/igwn_ligolw-2.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8d05aab5f4e9e681203fcfd4b2162312b03ae496afbd6734c8de4833e1efc585", size = 169556, upload-time = "2026-01-19T19:01:09.629Z" }, - { url = "https://files.pythonhosted.org/packages/53/b8/332b9ff77ae0b35add7fd0f0b833c82987a4c0e057a750e57d1f246f4227/igwn_ligolw-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:444a9d8b3f60c9ca964fc6735094faf9e4d4ba188ac7268cf27ed408be052130", size = 168091, upload-time = "2026-01-19T19:01:10.621Z" }, - { url = "https://files.pythonhosted.org/packages/8e/1b/274a3c85f976ce929d77599a07d5acafffa5363f7a5c33dd19b7693f0c73/igwn_ligolw-2.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d6893c9642b14e7972e156e37ca5867bbd80a3f5e5c1c7ab2a8cc98f9389795f", size = 167987, upload-time = "2026-01-19T19:01:11.625Z" }, - { url = "https://files.pythonhosted.org/packages/db/14/115b6f1afc6f6e1185a0c8248fcc2dab0cb5ec638b7d8edf89b22e742182/igwn_ligolw-2.1.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:8ed8e9aa2e22201ae10932c2027185553eb40ca4a358444434cc0764faca64fd", size = 140034, upload-time = "2026-01-19T19:01:13.268Z" }, - { url = "https://files.pythonhosted.org/packages/22/29/72a83bf1ee9e39090359319874f97c38aa5a3cb0903329dc8fb470e40eb7/igwn_ligolw-2.1.1-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:03fca460d68563f98b9f6792598c1050b0e5178f5278c6a6a11f4fa457ecbf18", size = 140077, upload-time = "2026-01-19T19:01:14.342Z" }, - { url = "https://files.pythonhosted.org/packages/82/41/3b4e4a12c4eac61a9a286070f79d71aac595467898229ea8712aa31d2994/igwn_ligolw-2.1.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a3b3573d6f0704702412cb3bf73e50ae91d5a8748bd55ea36b5da49bdc4f4a95", size = 168633, upload-time = "2026-01-19T19:01:15.627Z" }, - { url = "https://files.pythonhosted.org/packages/d6/f8/aa58ba53e8a6224282477a15897c2f1bf30f05000ce81d75c88317e895f5/igwn_ligolw-2.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:504e8554659489da8a574d616f3f1d7e5c0a3e63a31d1ae3bf10b2b673461c59", size = 169552, upload-time = "2026-01-19T19:01:17.029Z" }, - { url = "https://files.pythonhosted.org/packages/d3/d0/1a27a0d51332c486ac50019056f909e788a90c7eea00f5cba9263800eec8/igwn_ligolw-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdf07edc462ddae640b9fd327205b83123599efac05463d8be6badbe3473adde", size = 168149, upload-time = "2026-01-19T19:01:18.3Z" }, - { url = "https://files.pythonhosted.org/packages/39/cf/dae49ce938a438af61dfa857b36dfd1d6ec098fa5389b59fc76b431eb757/igwn_ligolw-2.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:de5ba73f5d05135e40b9e46befd36fe187a8f884367bed9450b0cbe7ef6e9212", size = 168044, upload-time = "2026-01-19T19:01:19.443Z" }, - { url = "https://files.pythonhosted.org/packages/a0/82/dc028303a14ca51c9cb6f909bb3c934a8399d97e5e52700f8cc3ea9b2977/igwn_ligolw-2.1.1-cp314-cp314-macosx_13_0_x86_64.whl", hash = "sha256:169ae1f74a72c5191b18060231ae5662e32946ea20939ec34be20cfc86df71b7", size = 140101, upload-time = "2026-01-19T19:01:20.465Z" }, - { url = "https://files.pythonhosted.org/packages/3c/8e/308c4f105e1209507d11220ce034b5030045b53e6f4b29f83e9165ba8efe/igwn_ligolw-2.1.1-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:e4971dc9eaff99f39706019d8a2596dd164e8a352005f4395ac91d99d31add34", size = 140404, upload-time = "2026-01-19T19:01:21.937Z" }, - { url = "https://files.pythonhosted.org/packages/a2/c0/fe74d6c94935a8ab3b345f24d9539bbafa60880b33735a6b2ec083e891d2/igwn_ligolw-2.1.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cc20fc62c9948cf82ee49bc615ed8c1ddf233a9d6d011f9e978de23448ef8d63", size = 168515, upload-time = "2026-01-19T19:01:22.96Z" }, - { url = "https://files.pythonhosted.org/packages/f1/de/d97acd60af7fdd5fd6192be7279f9db1615000363e8c5418c610e6636ebb/igwn_ligolw-2.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ab01dd3320c0ef8bba208962384161cbfc02f16b300852a9839bd7dcae27fff4", size = 169634, upload-time = "2026-01-19T19:01:24.367Z" }, - { url = "https://files.pythonhosted.org/packages/ed/fa/1924a653a6b8751736907cc119ebd3aa98b868cd1dd6c5f293db0e45367f/igwn_ligolw-2.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:02de381133d8775f6dfa79c12bdabe8973aba5a33788b06b44cb12c0044dcc98", size = 168124, upload-time = "2026-01-19T19:01:25.677Z" }, - { url = "https://files.pythonhosted.org/packages/33/c0/acf7db509ca842b80100e782ff3f6c0c40547f29c3e9dfcfba4c2ec0e845/igwn_ligolw-2.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:314960894bb73d1b0d5ca2e9a754d344facd966fac499f1cf43c914745c0ff03", size = 167829, upload-time = "2026-01-19T19:01:26.717Z" }, -] - -[[package]] -name = "igwn-segments" -version = "2.1.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/92/ef/3500687ef4a61887bc218ab7ef8352ab7110afababaa82e958f2ca95967b/igwn_segments-2.1.1.tar.gz", hash = "sha256:672814d78f5de7582b5f39189cdc6016d7ffde95d2b8b8b8b82aa9b45e53f3dd", size = 60805, upload-time = "2026-01-19T17:51:05.142Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/87/f5dc189af8817fc722dfac1798bf0d70a4b795701be4adafd215a45e3b8d/igwn_segments-2.1.1-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:185993971cf4cac593156f47361ff62f51158bc197f2cdbad6f8e0165bae43dd", size = 49090, upload-time = "2026-01-19T17:50:25.921Z" }, - { url = "https://files.pythonhosted.org/packages/51/2b/cf74ffaff55de002a5717c3843c5c40b019587a8f55c26c62d1fdf6f9c7b/igwn_segments-2.1.1-cp310-cp310-macosx_15_0_arm64.whl", hash = "sha256:48867076f643508cb9f66fd460d72548ea60dab5192f49770d77883be7f00275", size = 49251, upload-time = "2026-01-19T17:50:27.427Z" }, - { url = "https://files.pythonhosted.org/packages/8d/d7/42cda68c97f11532dc8c8a7db93ee9593ef9c0f52b5d26db5dfe06934155/igwn_segments-2.1.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:edd31f115f8cc3411491300f56279be3417afde486b6cab6ee80c43c1bae5034", size = 108410, upload-time = "2026-01-19T17:50:28.603Z" }, - { url = "https://files.pythonhosted.org/packages/4c/b5/fdea6bf16f756811c6c9225e1e9c79ddf57a37c1d9609fb374afb8912573/igwn_segments-2.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:76b5626ed1f81197333fdafeac262c15339c5e5e21a92a1e4254b01487fd49ad", size = 109833, upload-time = "2026-01-19T17:50:29.948Z" }, - { url = "https://files.pythonhosted.org/packages/1e/16/1a272ce8fe797a962d6a66b2c5a43528df3b5368ef432e5537785d2a617b/igwn_segments-2.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74b765df7d2ac6e46d48a74ee8dda6663748e9341b6d06f7f5968443564b5822", size = 108421, upload-time = "2026-01-19T17:50:30.933Z" }, - { url = "https://files.pythonhosted.org/packages/91/47/ac87852a682e548ebc926fd97975b92352f63b54943a96b6bbcb7ffa7d1a/igwn_segments-2.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:22abde65098e53b41a299f0e7606b23671e07cae87128c95dfb0baf80f0ff6a8", size = 107778, upload-time = "2026-01-19T17:50:31.951Z" }, - { url = "https://files.pythonhosted.org/packages/bf/c3/3ba15630cf8b873cb10bf188793b7828cf2f7e13ba0bd5673bdad42e8636/igwn_segments-2.1.1-cp311-cp311-macosx_13_0_x86_64.whl", hash = "sha256:774c84a5c0ebf4fe6e4de3e1b8c73c02b6f53db96598a07e7d26de9cd156ef64", size = 49068, upload-time = "2026-01-19T17:50:32.953Z" }, - { url = "https://files.pythonhosted.org/packages/b4/b1/7ffbe8b7fe6b6597d913bfd32db3898ee3d1495003bfedd497b14ea696b9/igwn_segments-2.1.1-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:8cc0dde1d4f9de3a36eddf38e25baa166492737c93ad71658db25dfc46745ddd", size = 49249, upload-time = "2026-01-19T17:50:34.158Z" }, - { url = "https://files.pythonhosted.org/packages/71/f7/9110ee0e83f43ba97155897be0a864d53801ff259a674bc8e37f80e854b9/igwn_segments-2.1.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6c2231f6a8e2adeb6403e4ba5fa8fb9cd9bcb7c55fa9e67e90139020a32aff79", size = 112480, upload-time = "2026-01-19T17:50:35.064Z" }, - { url = "https://files.pythonhosted.org/packages/50/06/eb25c8ffefcbe8c9f3275855bbfad607e20d61ea6c3f5be778f8b5c0d82f/igwn_segments-2.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cfbef53fcd95c56e200b9211cd3bad4fa48c5b82586166c6fdb44306ea3b9289", size = 114264, upload-time = "2026-01-19T17:50:36.092Z" }, - { url = "https://files.pythonhosted.org/packages/84/18/ce2b83e8993ddb4d8f6c409ff3405f3e424215a88876b019f7a0d9b38d70/igwn_segments-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5ba2483b066c8f95e6e916437f7a375c0497313e1a425642b89ef1d647980f80", size = 112686, upload-time = "2026-01-19T17:50:37.479Z" }, - { url = "https://files.pythonhosted.org/packages/13/78/ec606472610da3f545cbdd03b134fc09cfb86eae3674f5198ee8a4c36501/igwn_segments-2.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5351901272f8a33b4cb8546930e744bcd912c5d899879e8477f064cd11d0d874", size = 111852, upload-time = "2026-01-19T17:50:38.627Z" }, - { url = "https://files.pythonhosted.org/packages/71/78/6764ceb793728334df4dc87d9089ca5a8010ad280c7ca13c0e4304818f83/igwn_segments-2.1.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:d8a1e36f21f9420f254a7f78c289035c8d9dc4e0f44b5a77495e87d71469bd7b", size = 49329, upload-time = "2026-01-19T17:50:39.843Z" }, - { url = "https://files.pythonhosted.org/packages/0c/76/eb614178340cbae66af617303f80748d61d74c57f0b02105b9ca85c90033/igwn_segments-2.1.1-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:139abaec24edc25e7409fe48445c5aa8d9448e98c038079d84485497abc838c0", size = 49704, upload-time = "2026-01-19T17:50:41.013Z" }, - { url = "https://files.pythonhosted.org/packages/e0/5c/f10140682e77bdf4160cd474bbc21a8e8d362dfec46131b4e66d5929b04e/igwn_segments-2.1.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f550aa55fa4165c1d3d8571d67523a010b5b365ee363516e542f732cbe54a1d8", size = 114953, upload-time = "2026-01-19T17:50:42.126Z" }, - { url = "https://files.pythonhosted.org/packages/61/05/b347e6ac22c5cbae568e410c56729a188d9d64ce340e859fc8db5b82f195/igwn_segments-2.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9aa802dfd96d24909975d92c24aee088d4be0fb3636a2d893cb196bbeb19f175", size = 115998, upload-time = "2026-01-19T17:50:43.169Z" }, - { url = "https://files.pythonhosted.org/packages/5d/a5/f6a2b14786e62630fa3adad1b87155c4d71c3b1360e8563092793b81dfae/igwn_segments-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:74abd9ab5502bfba09343d2d6fcffb7c75f935385e1ca389b8ced82e9fe67a8f", size = 114540, upload-time = "2026-01-19T17:50:44.438Z" }, - { url = "https://files.pythonhosted.org/packages/48/9e/604b28e22207db111c9f43e4214a7aa086d6d65be38c024d393a5aaab781/igwn_segments-2.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:029a7d55c786ef24f1c07850a291e111ef2fceeaae2518347203dcaa019f811c", size = 114640, upload-time = "2026-01-19T17:50:45.749Z" }, - { url = "https://files.pythonhosted.org/packages/53/e2/7eed3f40e8df9892377d638c50d653fde9b0d8fa208d451c30b19a48e3a1/igwn_segments-2.1.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:ae73f8aa905a156c25e288ddb32a03aed69b960f05d95e5b84fede85a46676d9", size = 49262, upload-time = "2026-01-19T17:50:46.676Z" }, - { url = "https://files.pythonhosted.org/packages/ec/6e/655970d647e9eb725d1cc0240971dc07606914a059704413a70e90eb3502/igwn_segments-2.1.1-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:2b6900a212aadac1fb94da7e113d351b6289ddaf8957a715b19c5ca5f2a40a22", size = 49536, upload-time = "2026-01-19T17:50:47.574Z" }, - { url = "https://files.pythonhosted.org/packages/ed/ec/24bd1f5b5fce5ba944dcdadcb7e083655d822a5ab245636c6df64dae195a/igwn_segments-2.1.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b4507bdea2de5dcbc623518c5c51e66c04ecb550341ed542f42092a8f2c38305", size = 114080, upload-time = "2026-01-19T17:50:48.467Z" }, - { url = "https://files.pythonhosted.org/packages/8a/a3/b31b6db442aca5e39f0286e6f561a19b76b759d5ef4736a20bd2c06b27ff/igwn_segments-2.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f7fa75ab219400a195485d3686871231dd653c636c8052656411041f7f7a44a", size = 115381, upload-time = "2026-01-19T17:50:49.461Z" }, - { url = "https://files.pythonhosted.org/packages/c5/ff/3f8bd0fa10ace48437d08134fcb17ff869f9fd2fd8e50f4c2a0d70886414/igwn_segments-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c230878d7d95562948f3a7067fc05abb4956632d49981d872e3bd49c11cf5835", size = 113893, upload-time = "2026-01-19T17:50:50.409Z" }, - { url = "https://files.pythonhosted.org/packages/1e/62/e50dbdeaf7503febb55be1becd3ef8f6d647945c39dc722ace0ffb577ca9/igwn_segments-2.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:95022b867b8c1eb667228822e4c2933efdf675f7d7a89798cd08809f6f1bf980", size = 113759, upload-time = "2026-01-19T17:50:51.327Z" }, - { url = "https://files.pythonhosted.org/packages/89/f8/8cde0ca1c4b4b272c012ef8913c913e24e4976ba64c4a7509b8d2a2dfba2/igwn_segments-2.1.1-cp314-cp314-macosx_13_0_x86_64.whl", hash = "sha256:16c241bae04926e415715007768e08a93bf5ad3abef71814297625e460e711f1", size = 49695, upload-time = "2026-01-19T17:50:52.636Z" }, - { url = "https://files.pythonhosted.org/packages/21/28/35643a4fdcb43748bc95acb74d4a1393e82298cc12b0d367e828c8be6416/igwn_segments-2.1.1-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:06b3333204053f9ed094b0a4476bdd817881913cc2f6be28e99acfec9f683748", size = 49610, upload-time = "2026-01-19T17:50:53.679Z" }, - { url = "https://files.pythonhosted.org/packages/92/9f/303dc735a15cc07b4264ece860325481d7a58b026408c3c52929f4f9ffe1/igwn_segments-2.1.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:24cd58c55b1bd811df1e7de76132c6f3718b5e46be6ae3d3952738d4cf438666", size = 112671, upload-time = "2026-01-19T17:50:54.72Z" }, - { url = "https://files.pythonhosted.org/packages/96/f9/aa989728fa9ae042d16ffafe73f2ed264fbbffc8e7a3b3e63eae3bc33989/igwn_segments-2.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c6d08ca43df100ae497b4e83f9c76a87e21d06fb664b17c759b24c3a56ac9b1b", size = 114766, upload-time = "2026-01-19T17:50:55.694Z" }, - { url = "https://files.pythonhosted.org/packages/4d/d8/b0d8b6d5f4d03f478a239ddca19b3de90cd5a6f4c78f581d4ea456e1438c/igwn_segments-2.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d2718a139d3e72301ce362e7a1dd5b0220a8f533118acfd846eb4b0576c6b275", size = 113475, upload-time = "2026-01-19T17:50:56.634Z" }, - { url = "https://files.pythonhosted.org/packages/39/01/d12122ca1698433f0da7b19fd2e49bccc53ffca2c6106a8cb952c74c3970/igwn_segments-2.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:482ef5b3a5f9bf07d22e1d6a46e8892f54dbfa8c088817d99ab424c09b24c60d", size = 112427, upload-time = "2026-01-19T17:50:57.554Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, -] - -[[package]] -name = "interpax" -version = "0.3.14" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "equinox" }, - { name = "jax", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "jax", version = "0.10.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "jaxtyping", version = "0.3.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "jaxtyping", version = "0.3.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "lineax" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/38/b2/06d071b6eade1ea7e3bb0660aa3c1f8fd6484fa611cc45cd424d2bbebecf/interpax-0.3.14.tar.gz", hash = "sha256:5018c18a07d946be7466504f385b4e5ac632cd7b006c1cead42b1aefe37e16e3", size = 58669, upload-time = "2026-04-18T00:35:09.305Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/77/3b4c3db18738ffc146887f3b47e44159d370509c3ee302f0456a8f0dfcac/interpax-0.3.14-py3-none-any.whl", hash = "sha256:070c5242227fa65bd7dafc472ae2ed0c4df9891fbbbede73a8b04e21859a5d47", size = 28104, upload-time = "2026-04-18T00:35:07.774Z" }, -] - -[[package]] -name = "jax" -version = "0.6.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -dependencies = [ - { name = "jaxlib", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "ml-dtypes", marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "opt-einsum", marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cf/1e/267f59c8fb7f143c3f778c76cb7ef1389db3fd7e4540f04b9f42ca90764d/jax-0.6.2.tar.gz", hash = "sha256:a437d29038cbc8300334119692744704ca7941490867b9665406b7f90665cd96", size = 2334091, upload-time = "2025-06-17T23:10:27.186Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/31/a8/97ef0cbb7a17143ace2643d600a7b80d6705b2266fc31078229e406bdef2/jax-0.6.2-py3-none-any.whl", hash = "sha256:bb24a82dc60ccf704dcaf6dbd07d04957f68a6c686db19630dd75260d1fb788c", size = 2722396, upload-time = "2025-06-17T23:10:25.293Z" }, -] - -[[package]] -name = "jax" -version = "0.10.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'win32'", - "python_full_version == '3.12.*' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version == '3.12.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", -] -dependencies = [ - { name = "jaxlib", version = "0.10.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "ml-dtypes", marker = "python_full_version >= '3.11'" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "opt-einsum", marker = "python_full_version >= '3.11'" }, - { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/50/f0/bcb81d28267d2054d0daed766c7fa16bcee5e481331b4d1e14f5fbe662be/jax-0.10.0.tar.gz", hash = "sha256:0119c767de1645f407df72345d28a3837dc904f1d698911c121d8f2b396fdece", size = 2663397, upload-time = "2026-04-22T13:22:28.563Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/70/aa/dfac6d72cc35bc07e7587115b6946e333ef4ccb2e6cd26ecf639438c5d26/jax-0.10.0-py3-none-any.whl", hash = "sha256:76c42ba163c8db3dc2e449e225b888c0edfb623ded31efdc96d85e0fda1d26e8", size = 3094950, upload-time = "2026-04-16T12:32:11.576Z" }, -] - -[[package]] -name = "jaxlib" -version = "0.6.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -dependencies = [ - { name = "ml-dtypes", marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/15/c5/41598634c99cbebba46e6777286fb76abc449d33d50aeae5d36128ca8803/jaxlib-0.6.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:da4601b2b5dc8c23d6afb293eacfb9aec4e1d1871cb2f29c5a151d103e73b0f8", size = 54298019, upload-time = "2025-06-17T23:10:36.916Z" }, - { url = "https://files.pythonhosted.org/packages/81/af/db07d746cd5867d5967528e7811da53374e94f64e80a890d6a5a4b95b130/jaxlib-0.6.2-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:4205d098ce8efb5f7fe2fe5098bae6036094dc8d8829f5e0e0d7a9b155326336", size = 79440052, upload-time = "2025-06-17T23:10:41.282Z" }, - { url = "https://files.pythonhosted.org/packages/7e/d8/b7ae9e819c62c1854dbc2c70540a5c041173fbc8bec5e78ab7fd615a4aee/jaxlib-0.6.2-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:c087a0eb6fb7f6f8f54d56f4730328dfde5040dd3b5ddfa810e7c28ea7102b42", size = 89917034, upload-time = "2025-06-17T23:10:45.897Z" }, - { url = "https://files.pythonhosted.org/packages/fd/e5/87e91bc70569ac5c3e3449eefcaf47986e892f10cfe1d5e5720dceae3068/jaxlib-0.6.2-cp310-cp310-win_amd64.whl", hash = "sha256:153eaa51f778b60851720729d4f461a91edd9ba3932f6f3bc598d4413870038b", size = 57896337, upload-time = "2025-06-17T23:10:50.179Z" }, - { url = "https://files.pythonhosted.org/packages/a4/ee/6899b0aed36a4acc51319465ddd83c7c300a062a9e236cceee00984ffe0b/jaxlib-0.6.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a208ff61c58128d306bb4e5ad0858bd2b0960f2c1c10ad42c548f74a60c0020e", size = 54300346, upload-time = "2025-06-17T23:10:54.591Z" }, - { url = "https://files.pythonhosted.org/packages/e6/03/34bb6b346609079a71942cfbf507892e3c877a06a430a0df8429c455cebc/jaxlib-0.6.2-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:11eae7e05bc5a79875da36324afb9eddd4baeaef2a0386caf6d4f3720b9aef28", size = 79438425, upload-time = "2025-06-17T23:10:58.356Z" }, - { url = "https://files.pythonhosted.org/packages/80/02/49b05cbab519ffd3cb79586336451fbbf8b6523f67128a794acc9f179000/jaxlib-0.6.2-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:335d7e3515ce78b52a410136f46aa4a7ea14d0e7d640f34e1e137409554ad0ac", size = 89920354, upload-time = "2025-06-17T23:11:03.086Z" }, - { url = "https://files.pythonhosted.org/packages/a7/7a/93b28d9452b46c15fc28dd65405672fc8a158b35d46beabaa0fe9631afb0/jaxlib-0.6.2-cp311-cp311-win_amd64.whl", hash = "sha256:c6815509997d6b05e5c9daa7994b9ad473ce3e8c8a17bdbbcacc3c744f76f7a0", size = 57895707, upload-time = "2025-06-17T23:11:07.074Z" }, - { url = "https://files.pythonhosted.org/packages/ac/db/05e702d2534e87abf606b1067b46a273b120e6adc7d459696e3ce7399317/jaxlib-0.6.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d8a684a8be949dd87dd4acc97101b4106a0dc9ad151ec891da072319a57b99", size = 54301644, upload-time = "2025-06-17T23:11:10.977Z" }, - { url = "https://files.pythonhosted.org/packages/0d/8a/b0a96887b97a25d45ae2c30e4acecd2f95acd074c18ec737dda8c5cc7016/jaxlib-0.6.2-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:87ec2dc9c3ed9ab936eec8535160c5fbd2c849948559f1c5daa75f63fabe5942", size = 79439161, upload-time = "2025-06-17T23:11:14.822Z" }, - { url = "https://files.pythonhosted.org/packages/ba/e8/71c2555431edb5dd115cf86a7b599aa7e1be26728d89ae59aa11251d299c/jaxlib-0.6.2-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:f1dd09b481a93c1d4c750013f467f74194493ba7bd29fcd4d1cec16e3a214f65", size = 89942952, upload-time = "2025-06-17T23:11:19.181Z" }, - { url = "https://files.pythonhosted.org/packages/de/3a/06849113c844b86d20174df54735c84202ccf82cbd36d805f478c834418b/jaxlib-0.6.2-cp312-cp312-win_amd64.whl", hash = "sha256:921dbd4db214eba19a29ba9f2450d880e08b2b2c7b968f28cc89da3e62366af4", size = 57919603, upload-time = "2025-06-17T23:11:23.207Z" }, - { url = "https://files.pythonhosted.org/packages/af/38/bed4279c2a3407820ed8bcd72dbad43c330ada35f88fafe9952b35abf785/jaxlib-0.6.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bff67b188133ce1f0111c7b163ac321fd646b59ed221ea489063e2e0f85cb967", size = 54300638, upload-time = "2025-06-17T23:11:26.372Z" }, - { url = "https://files.pythonhosted.org/packages/52/dc/9e35a1dc089ddf3d6be53ef2e6ba4718c5b6c0f90bccc535a20edac0c895/jaxlib-0.6.2-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:70498837caf538bd458ff6858c8bfd404db82015aba8f663670197fa9900ff02", size = 79439983, upload-time = "2025-06-17T23:11:30.016Z" }, - { url = "https://files.pythonhosted.org/packages/34/16/e93f0184b80a4e1ad38c6998aa3a2f7569c0b0152cbae39f7572393eda04/jaxlib-0.6.2-cp313-cp313-manylinux2014_x86_64.whl", hash = "sha256:f94163f14c8fd3ba93ae14b631abacf14cb031bba0b59138869984b4d10375f8", size = 89941720, upload-time = "2025-06-17T23:11:34.62Z" }, - { url = "https://files.pythonhosted.org/packages/06/b9/ea50792ee0333dba764e06c305fe098bce1cb938dcb66fbe2fc47ef5dd02/jaxlib-0.6.2-cp313-cp313-win_amd64.whl", hash = "sha256:b977604cd36c74b174d25ed685017379468138eb747d865f75e466cb273c801d", size = 57919073, upload-time = "2025-06-17T23:11:39.344Z" }, - { url = "https://files.pythonhosted.org/packages/09/ce/9596391c104a0547fcaf6a8c72078bbae79dbc8e7f0843dc8318f6606328/jaxlib-0.6.2-cp313-cp313t-manylinux2014_aarch64.whl", hash = "sha256:39cf9555f85ae1ce2e2c1a59fc71f2eca4f9867a7cb934fef881ba56b11371d1", size = 79579638, upload-time = "2025-06-17T23:11:43.054Z" }, - { url = "https://files.pythonhosted.org/packages/10/79/f6e80f7f4cacfc9f03e64ac57ecb856b140de7c2f939b25f8dcf1aff63f9/jaxlib-0.6.2-cp313-cp313t-manylinux2014_x86_64.whl", hash = "sha256:3abd536e44b05fb1657507e3ff1fc3691f99613bae3921ecab9e82f27255f784", size = 90066675, upload-time = "2025-06-17T23:11:47.454Z" }, -] - -[[package]] -name = "jaxlib" -version = "0.10.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'win32'", - "python_full_version == '3.12.*' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version == '3.12.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", -] -dependencies = [ - { name = "ml-dtypes", marker = "python_full_version >= '3.11'" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/48/5c/64a60f90d48bb6ab68ece63b7fa78855e8f8cefc4045f198a5c8695bfd99/jaxlib-0.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:277032e9f074c3fd5ffd1e0cb03d4fe66e272de472667cdbc418ad99b21b646a", size = 60115498, upload-time = "2026-04-16T12:33:15.93Z" }, - { url = "https://files.pythonhosted.org/packages/71/bc/b75d9e09bcf46e00fd9cdd6c457219a8fe2033d351c2d133917662e8cbaa/jaxlib-0.10.0-cp311-cp311-manylinux_2_27_aarch64.whl", hash = "sha256:3db94ebc859375d955de3504182add7ce1733ce3d30c15e0ef031602cb51a559", size = 79395106, upload-time = "2026-04-16T12:33:19.648Z" }, - { url = "https://files.pythonhosted.org/packages/64/13/a94b53b0acd3fccce0441e3811e86224e5b21ac122f2dea4be1ccdeb7dc0/jaxlib-0.10.0-cp311-cp311-manylinux_2_27_x86_64.whl", hash = "sha256:9be229993a41e5b2b84f234ecc19a5de02f35eddb1195cf027bd539e1601e15d", size = 85005588, upload-time = "2026-04-16T12:33:23.368Z" }, - { url = "https://files.pythonhosted.org/packages/d2/36/fbc303c0a41ac26daceeba0a9884d9206657e8eb1981f3f76da17f1ecc7f/jaxlib-0.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:421cdf3a4a5c2ee41471035e586954c8dc599d677ce9b11b063c3926a82a7850", size = 64195649, upload-time = "2026-04-16T12:33:26.972Z" }, - { url = "https://files.pythonhosted.org/packages/79/0c/279cb4dc009fe87a8315d1b182f520693236ad07b852152df344ea4e4021/jaxlib-0.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7c1d9b463327c7a2333f210114ecb04f28fefc51ba8233a85a2280cce75bdb42", size = 60137156, upload-time = "2026-04-16T12:33:30.306Z" }, - { url = "https://files.pythonhosted.org/packages/e3/cd/59ead5a90df739d1b8c1d1d00443558fd30adf5abb0319966ce340d49ff3/jaxlib-0.10.0-cp312-cp312-manylinux_2_27_aarch64.whl", hash = "sha256:aa1d70f1a4e27eb403654e71e2fb28d5786d3e9b77fc1847e8c5389880927ca4", size = 79398938, upload-time = "2026-04-16T12:33:34.14Z" }, - { url = "https://files.pythonhosted.org/packages/b5/20/9b07fc8b327b222b6f72a4978eb4f2ebe856ee71237d63c4d808ec3945e0/jaxlib-0.10.0-cp312-cp312-manylinux_2_27_x86_64.whl", hash = "sha256:b0bfb865a07df2e6d7418c0b0c292dd294b5500523b1dd5872b180db2aa480d4", size = 85028702, upload-time = "2026-04-16T12:33:37.815Z" }, - { url = "https://files.pythonhosted.org/packages/08/3b/4f798fffed4229a2d7de07c1f4feabac7676a26c695a418796dbe29bae7f/jaxlib-0.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:25bf167e0d8b594e0ec50783ff4892c0b7ec37236c88b2b425a7c252823f8680", size = 64221923, upload-time = "2026-04-16T12:33:41.343Z" }, - { url = "https://files.pythonhosted.org/packages/d4/b6/b66b0abb9df8f9f8f19a5244b849cb07fc7389a4a5e1fb7794f7cefd7f26/jaxlib-0.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:384635fff55899a295bbc82ee6c6f773a300e787dc472ca92bbe79abfaac8369", size = 60138213, upload-time = "2026-04-16T12:33:45.13Z" }, - { url = "https://files.pythonhosted.org/packages/30/1e/844e525a72a08a2744ae2722e2332a0159a6d0efdc1e561cf378f7259a01/jaxlib-0.10.0-cp313-cp313-manylinux_2_27_aarch64.whl", hash = "sha256:6d8d78b7070b34e4c5bba5f7e10927e7f4aac9b69be17e9b0a5898553a4338f3", size = 79401054, upload-time = "2026-04-16T12:33:49.263Z" }, - { url = "https://files.pythonhosted.org/packages/f7/95/305854c2ef2b645f7df1666be66b1167c392cc39384d09aca2e9499b71bf/jaxlib-0.10.0-cp313-cp313-manylinux_2_27_x86_64.whl", hash = "sha256:d303dc31b65e8b793d5600f81b1583be03dc9b876a4c10b3e259b6609a1cbe3b", size = 85027218, upload-time = "2026-04-16T12:33:54.325Z" }, - { url = "https://files.pythonhosted.org/packages/a8/63/a5e1dcb65dca6efbae7189f185588fc939e17c284f272254fbeb68a39817/jaxlib-0.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:3869be623c2f3391be2ee86f8b412372b102492e67cac0a5f0ab1037bbc3a5cc", size = 64221972, upload-time = "2026-04-16T12:46:24.762Z" }, - { url = "https://files.pythonhosted.org/packages/ce/d6/411e580b70f64a5a4b095cb2c03c1e2c7b3b35c6754e5cacd4a8f8a2d480/jaxlib-0.10.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9050ce2ae7eeca62b1a235065056cad62cac590ddc035486faa4472a47eed9f6", size = 60250897, upload-time = "2026-04-16T12:46:13.185Z" }, - { url = "https://files.pythonhosted.org/packages/2c/5c/f40ac9d40eb39c359f268e087ff1f21bdad664f86691c52a288d0f9152e8/jaxlib-0.10.0-cp313-cp313t-manylinux_2_27_aarch64.whl", hash = "sha256:59e07aab3bdfaad9bdd3cf32e0d3d4f228837b9b231c53f5ae1c0fc284481094", size = 79518774, upload-time = "2026-04-16T12:46:16.684Z" }, - { url = "https://files.pythonhosted.org/packages/49/36/dea7a0ea64a5551244e2140ef6ad36e2dff308b6f5facaa6f1c1272bb47f/jaxlib-0.10.0-cp313-cp313t-manylinux_2_27_x86_64.whl", hash = "sha256:3088503812cfe49f34a3083d3b7ef5cb3aaf33d89ceb1b3f647fa52713aee59d", size = 85134776, upload-time = "2026-04-16T12:46:20.855Z" }, - { url = "https://files.pythonhosted.org/packages/a7/25/e1e52a21786b321fb6a2edf9ef9971aa70f06bb2738aef9afd6d8f46a441/jaxlib-0.10.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:98b26672943672742873f65bc03216819fc55325c99f146590d007c0172bff30", size = 60141273, upload-time = "2026-04-16T12:46:27.922Z" }, - { url = "https://files.pythonhosted.org/packages/9c/3b/21e3382ce6f4ee84bcce52810f3786ae3663991ec863acadcd0765b6f767/jaxlib-0.10.0-cp314-cp314-manylinux_2_27_aarch64.whl", hash = "sha256:ad47e072430979ec21637aa487d4dc464028b8e9be27268f37de69536c76e341", size = 79416404, upload-time = "2026-04-16T12:46:31.326Z" }, - { url = "https://files.pythonhosted.org/packages/a1/8e/b2a08ffc51c93842de71f7f988865cebfa7f43d6721957812dc8cc8b9d40/jaxlib-0.10.0-cp314-cp314-manylinux_2_27_x86_64.whl", hash = "sha256:2a42cf04c0f88bc03b150a17fa7ddbb2f40e096667ec8a1b840ed87913e6e735", size = 85035152, upload-time = "2026-04-16T12:46:36.129Z" }, - { url = "https://files.pythonhosted.org/packages/24/08/26e6a3ecf0a95f1ec0dcd7a668d5c9a72e581c40fe4ae51e102ca63174c5/jaxlib-0.10.0-cp314-cp314-win_amd64.whl", hash = "sha256:450b771c01b3662c3497e2dceada3f6fc893112ae637ef85ef1dcc7dc68892a8", size = 66661443, upload-time = "2026-04-16T12:46:51.088Z" }, - { url = "https://files.pythonhosted.org/packages/37/d7/06383d19217824134c4a6119d2efe7b53cde6a0a66fb1d643d9f725d2697/jaxlib-0.10.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f62026c9fb1f05998592082a6dcb62f70b466342bc139f711802a9b184ba9a46", size = 60253088, upload-time = "2026-04-16T12:46:39.666Z" }, - { url = "https://files.pythonhosted.org/packages/6b/ce/f66f955c01cce1ffda0cfbb1c02bb9234e0cac1d40b46fe17c315155d62f/jaxlib-0.10.0-cp314-cp314t-manylinux_2_27_aarch64.whl", hash = "sha256:e66bdc0b57ed5649950799d3f0d67a6bb67f03d06b49ea3fced0bdd6140a9943", size = 79517974, upload-time = "2026-04-16T12:46:43.147Z" }, - { url = "https://files.pythonhosted.org/packages/5e/74/b358923d0cce13fc7608051d0cc60ce3379f14350dc42540bdbabdbffab2/jaxlib-0.10.0-cp314-cp314t-manylinux_2_27_x86_64.whl", hash = "sha256:4dccd9065b30954879869641472d5d12fe4d7914175a5cad56293af8429ce7e0", size = 85134286, upload-time = "2026-04-16T12:46:47.416Z" }, -] - -[[package]] -name = "jaxtyping" -version = "0.3.7" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -dependencies = [ - { name = "wadler-lindig", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/38/40/a2ea3ce0e3e5f540eb970de7792c90fa58fef1b27d34c83f9fa94fea4729/jaxtyping-0.3.7.tar.gz", hash = "sha256:3bd7d9beb7d3cb01a89f93f90581c6f4fff3e5c5dc3c9307e8f8687a040d10c4", size = 45721, upload-time = "2026-01-30T14:18:47.409Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/78/42/caf65e9a0576a3abadc537e2f831701ba9081f21317fb3be87d64451587a/jaxtyping-0.3.7-py3-none-any.whl", hash = "sha256:303ab8599edf412eeb40bf06c863e3168fa186cf0e7334703fa741ddd7046e66", size = 56101, upload-time = "2026-01-30T14:18:45.954Z" }, -] - -[[package]] -name = "jaxtyping" -version = "0.3.9" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'win32'", - "python_full_version == '3.12.*' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version == '3.12.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", -] -dependencies = [ - { name = "wadler-lindig", marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c2/be/00294e369938937e31b094437d5ea040e4fd1a20b998ebe572c4a1dcfa68/jaxtyping-0.3.9.tar.gz", hash = "sha256:f8c02d1b623d5f1b6665d4f3ddaec675d70004f16a792102c2fc51264190951d", size = 45857, upload-time = "2026-02-16T10:35:13.263Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/05/3e39d416fb92b2738a76e8265e6bfc5d10542f90a7c32ad1eb831eea3fa3/jaxtyping-0.3.9-py3-none-any.whl", hash = "sha256:a00557a9d616eff157491f06ed2e21ed94886fad3832399273eb912b345da378", size = 56274, upload-time = "2026-02-16T10:35:11.795Z" }, -] - -[[package]] -name = "jinja2" -version = "3.1.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markupsafe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, -] - -[[package]] -name = "joblib" -version = "1.5.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/41/f2/d34e8b3a08a9cc79a50b2208a93dce981fe615b64d5a4d4abee421d898df/joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3", size = 331603, upload-time = "2025-12-15T08:41:46.427Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071, upload-time = "2025-12-15T08:41:44.973Z" }, -] - -[[package]] -name = "kiwisolver" -version = "1.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d0/67/9c61eccb13f0bdca9307614e782fec49ffdde0f7a2314935d489fa93cd9c/kiwisolver-1.5.0.tar.gz", hash = "sha256:d4193f3d9dc3f6f79aaed0e5637f45d98850ebf01f7ca20e69457f3e8946b66a", size = 103482, upload-time = "2026-03-09T13:15:53.382Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ac/f8/06549565caa026e540b7e7bab5c5a90eb7ca986015f4c48dace243cd24d9/kiwisolver-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:32cc0a5365239a6ea0c6ed461e8838d053b57e397443c0ca894dcc8e388d4374", size = 122802, upload-time = "2026-03-09T13:12:37.515Z" }, - { url = "https://files.pythonhosted.org/packages/84/eb/8476a0818850c563ff343ea7c9c05dcdcbd689a38e01aa31657df01f91fa/kiwisolver-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cc0b66c1eec9021353a4b4483afb12dfd50e3669ffbb9152d6842eb34c7e29fd", size = 66216, upload-time = "2026-03-09T13:12:38.812Z" }, - { url = "https://files.pythonhosted.org/packages/f3/c4/f9c8a6b4c21aed4198566e45923512986d6cef530e7263b3a5f823546561/kiwisolver-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86e0287879f75621ae85197b0877ed2f8b7aa57b511c7331dce2eb6f4de7d476", size = 63917, upload-time = "2026-03-09T13:12:40.053Z" }, - { url = "https://files.pythonhosted.org/packages/f1/0e/ba4ae25d03722f64de8b2c13e80d82ab537a06b30fc7065183c6439357e3/kiwisolver-1.5.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:62f59da443c4f4849f73a51a193b1d9d258dcad0c41bc4d1b8fb2bcc04bfeb22", size = 1628776, upload-time = "2026-03-09T13:12:41.976Z" }, - { url = "https://files.pythonhosted.org/packages/8a/e4/3f43a011bc8a0860d1c96f84d32fa87439d3feedf66e672fef03bf5e8bac/kiwisolver-1.5.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9190426b7aa26c5229501fa297b8d0653cfd3f5a36f7990c264e157cbf886b3b", size = 1228164, upload-time = "2026-03-09T13:12:44.002Z" }, - { url = "https://files.pythonhosted.org/packages/4b/34/3a901559a1e0c218404f9a61a93be82d45cb8f44453ba43088644980f033/kiwisolver-1.5.0-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c8277104ded0a51e699c8c3aff63ce2c56d4ed5519a5f73e0fd7057f959a2b9e", size = 1246656, upload-time = "2026-03-09T13:12:45.557Z" }, - { url = "https://files.pythonhosted.org/packages/87/9e/f78c466ea20527822b95ad38f141f2de1dcd7f23fb8716b002b0d91bbe59/kiwisolver-1.5.0-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8f9baf6f0a6e7571c45c8863010b45e837c3ee1c2c77fcd6ef423be91b21fedb", size = 1295562, upload-time = "2026-03-09T13:12:47.562Z" }, - { url = "https://files.pythonhosted.org/packages/0a/66/fd0e4a612e3a286c24e6d6f3a5428d11258ed1909bc530ba3b59807fd980/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cff8e5383db4989311f99e814feeb90c4723eb4edca425b9d5d9c3fefcdd9537", size = 2178473, upload-time = "2026-03-09T13:12:50.254Z" }, - { url = "https://files.pythonhosted.org/packages/dc/8e/6cac929e0049539e5ee25c1ee937556f379ba5204840d03008363ced662d/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ebae99ed6764f2b5771c522477b311be313e8841d2e0376db2b10922daebbba4", size = 2274035, upload-time = "2026-03-09T13:12:51.785Z" }, - { url = "https://files.pythonhosted.org/packages/ca/d3/9d0c18f1b52ea8074b792452cf17f1f5a56bd0302a85191f405cfbf9da16/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:d5cd5189fc2b6a538b75ae45433140c4823463918f7b1617c31e68b085c0022c", size = 2443217, upload-time = "2026-03-09T13:12:53.329Z" }, - { url = "https://files.pythonhosted.org/packages/45/2a/6e19368803a038b2a90857bf4ee9e3c7b667216d045866bf22d3439fd75e/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f42c23db5d1521218a3276bb08666dcb662896a0be7347cba864eca45ff64ede", size = 2249196, upload-time = "2026-03-09T13:12:55.057Z" }, - { url = "https://files.pythonhosted.org/packages/75/2b/3f641dfcbe72e222175d626bacf2f72c3b34312afec949dd1c50afa400f5/kiwisolver-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:94eff26096eb5395136634622515b234ecb6c9979824c1f5004c6e3c3c85ccd2", size = 73389, upload-time = "2026-03-09T13:12:56.496Z" }, - { url = "https://files.pythonhosted.org/packages/da/88/299b137b9e0025d8982e03d2d52c123b0a2b159e84b0ef1501ef446339cf/kiwisolver-1.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:dd952e03bfbb096cfe2dd35cd9e00f269969b67536cb4370994afc20ff2d0875", size = 64782, upload-time = "2026-03-09T13:12:57.609Z" }, - { url = "https://files.pythonhosted.org/packages/12/dd/a495a9c104be1c476f0386e714252caf2b7eca883915422a64c50b88c6f5/kiwisolver-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9eed0f7edbb274413b6ee781cca50541c8c0facd3d6fd289779e494340a2b85c", size = 122798, upload-time = "2026-03-09T13:12:58.963Z" }, - { url = "https://files.pythonhosted.org/packages/11/60/37b4047a2af0cf5ef6d8b4b26e91829ae6fc6a2d1f74524bcb0e7cd28a32/kiwisolver-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c4923e404d6bcd91b6779c009542e5647fef32e4a5d75e115e3bbac6f2335eb", size = 66216, upload-time = "2026-03-09T13:13:00.155Z" }, - { url = "https://files.pythonhosted.org/packages/0a/aa/510dc933d87767584abfe03efa445889996c70c2990f6f87c3ebaa0a18c5/kiwisolver-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0df54df7e686afa55e6f21fb86195224a6d9beb71d637e8d7920c95cf0f89aac", size = 63911, upload-time = "2026-03-09T13:13:01.671Z" }, - { url = "https://files.pythonhosted.org/packages/80/46/bddc13df6c2a40741e0cc7865bb1c9ed4796b6760bd04ce5fae3928ef917/kiwisolver-1.5.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2517e24d7315eb51c10664cdb865195df38ab74456c677df67bb47f12d088a27", size = 1438209, upload-time = "2026-03-09T13:13:03.385Z" }, - { url = "https://files.pythonhosted.org/packages/fd/d6/76621246f5165e5372f02f5e6f3f48ea336a8f9e96e43997d45b240ed8cd/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff710414307fefa903e0d9bdf300972f892c23477829f49504e59834f4195398", size = 1248888, upload-time = "2026-03-09T13:13:05.231Z" }, - { url = "https://files.pythonhosted.org/packages/b2/c1/31559ec6fb39a5b48035ce29bb63ade628f321785f38c384dee3e2c08bc1/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6176c1811d9d5a04fa391c490cc44f451e240697a16977f11c6f722efb9041db", size = 1266304, upload-time = "2026-03-09T13:13:06.743Z" }, - { url = "https://files.pythonhosted.org/packages/5e/ef/1cb8276f2d29cc6a41e0a042f27946ca347d3a4a75acf85d0a16aa6dcc82/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50847dca5d197fcbd389c805aa1a1cf32f25d2e7273dc47ab181a517666b68cc", size = 1319650, upload-time = "2026-03-09T13:13:08.607Z" }, - { url = "https://files.pythonhosted.org/packages/4c/e4/5ba3cecd7ce6236ae4a80f67e5d5531287337d0e1f076ca87a5abe4cd5d0/kiwisolver-1.5.0-cp311-cp311-manylinux_2_39_riscv64.whl", hash = "sha256:01808c6d15f4c3e8559595d6d1fe6411c68e4a3822b4b9972b44473b24f4e679", size = 970949, upload-time = "2026-03-09T13:13:10.299Z" }, - { url = "https://files.pythonhosted.org/packages/5a/69/dc61f7ae9a2f071f26004ced87f078235b5507ab6e5acd78f40365655034/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f1f9f4121ec58628c96baa3de1a55a4e3a333c5102c8e94b64e23bf7b2083309", size = 2199125, upload-time = "2026-03-09T13:13:11.841Z" }, - { url = "https://files.pythonhosted.org/packages/e5/7b/abbe0f1b5afa85f8d084b73e90e5f801c0939eba16ac2e49af7c61a6c28d/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b7d335370ae48a780c6e6a6bbfa97342f563744c39c35562f3f367665f5c1de2", size = 2293783, upload-time = "2026-03-09T13:13:14.399Z" }, - { url = "https://files.pythonhosted.org/packages/8a/80/5908ae149d96d81580d604c7f8aefd0e98f4fd728cf172f477e9f2a81744/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:800ee55980c18545af444d93fdd60c56b580db5cc54867d8cbf8a1dc0829938c", size = 1960726, upload-time = "2026-03-09T13:13:16.047Z" }, - { url = "https://files.pythonhosted.org/packages/84/08/a78cb776f8c085b7143142ce479859cfec086bd09ee638a317040b6ef420/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c438f6ca858697c9ab67eb28246c92508af972e114cac34e57a6d4ba17a3ac08", size = 2464738, upload-time = "2026-03-09T13:13:17.897Z" }, - { url = "https://files.pythonhosted.org/packages/b1/e1/65584da5356ed6cb12c63791a10b208860ac40a83de165cb6a6751a686e3/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8c63c91f95173f9c2a67c7c526b2cea976828a0e7fced9cdcead2802dc10f8a4", size = 2270718, upload-time = "2026-03-09T13:13:19.421Z" }, - { url = "https://files.pythonhosted.org/packages/be/6c/28f17390b62b8f2f520e2915095b3c94d88681ecf0041e75389d9667f202/kiwisolver-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:beb7f344487cdcb9e1efe4b7a29681b74d34c08f0043a327a74da852a6749e7b", size = 73480, upload-time = "2026-03-09T13:13:20.818Z" }, - { url = "https://files.pythonhosted.org/packages/d8/0e/2ee5debc4f77a625778fec5501ff3e8036fe361b7ee28ae402a485bb9694/kiwisolver-1.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:ad4ae4ffd1ee9cd11357b4c66b612da9888f4f4daf2f36995eda64bd45370cac", size = 64930, upload-time = "2026-03-09T13:13:21.997Z" }, - { url = "https://files.pythonhosted.org/packages/4d/b2/818b74ebea34dabe6d0c51cb1c572e046730e64844da6ed646d5298c40ce/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4e9750bc21b886308024f8a54ccb9a2cc38ac9fa813bf4348434e3d54f337ff9", size = 123158, upload-time = "2026-03-09T13:13:23.127Z" }, - { url = "https://files.pythonhosted.org/packages/bf/d9/405320f8077e8e1c5c4bd6adc45e1e6edf6d727b6da7f2e2533cf58bff71/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:72ec46b7eba5b395e0a7b63025490d3214c11013f4aacb4f5e8d6c3041829588", size = 66388, upload-time = "2026-03-09T13:13:24.765Z" }, - { url = "https://files.pythonhosted.org/packages/99/9f/795fedf35634f746151ca8839d05681ceb6287fbed6cc1c9bf235f7887c2/kiwisolver-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ed3a984b31da7481b103f68776f7128a89ef26ed40f4dc41a2223cda7fb24819", size = 64068, upload-time = "2026-03-09T13:13:25.878Z" }, - { url = "https://files.pythonhosted.org/packages/c4/13/680c54afe3e65767bed7ec1a15571e1a2f1257128733851ade24abcefbcc/kiwisolver-1.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb5136fb5352d3f422df33f0c879a1b0c204004324150cc3b5e3c4f310c9049f", size = 1477934, upload-time = "2026-03-09T13:13:27.166Z" }, - { url = "https://files.pythonhosted.org/packages/c8/2f/cebfcdb60fd6a9b0f6b47a9337198bcbad6fbe15e68189b7011fd914911f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2af221f268f5af85e776a73d62b0845fc8baf8ef0abfae79d29c77d0e776aaf", size = 1278537, upload-time = "2026-03-09T13:13:28.707Z" }, - { url = "https://files.pythonhosted.org/packages/f2/0d/9b782923aada3fafb1d6b84e13121954515c669b18af0c26e7d21f579855/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b0f172dc8ffaccb8522d7c5d899de00133f2f1ca7b0a49b7da98e901de87bf2d", size = 1296685, upload-time = "2026-03-09T13:13:30.528Z" }, - { url = "https://files.pythonhosted.org/packages/27/70/83241b6634b04fe44e892688d5208332bde130f38e610c0418f9ede47ded/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6ab8ba9152203feec73758dad83af9a0bbe05001eb4639e547207c40cfb52083", size = 1346024, upload-time = "2026-03-09T13:13:32.818Z" }, - { url = "https://files.pythonhosted.org/packages/e4/db/30ed226fb271ae1a6431fc0fe0edffb2efe23cadb01e798caeb9f2ceae8f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:cdee07c4d7f6d72008d3f73b9bf027f4e11550224c7c50d8df1ae4a37c1402a6", size = 987241, upload-time = "2026-03-09T13:13:34.435Z" }, - { url = "https://files.pythonhosted.org/packages/ec/bd/c314595208e4c9587652d50959ead9e461995389664e490f4dce7ff0f782/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7c60d3c9b06fb23bd9c6139281ccbdc384297579ae037f08ae90c69f6845c0b1", size = 2227742, upload-time = "2026-03-09T13:13:36.4Z" }, - { url = "https://files.pythonhosted.org/packages/c1/43/0499cec932d935229b5543d073c2b87c9c22846aab48881e9d8d6e742a2d/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e315e5ec90d88e140f57696ff85b484ff68bb311e36f2c414aa4286293e6dee0", size = 2323966, upload-time = "2026-03-09T13:13:38.204Z" }, - { url = "https://files.pythonhosted.org/packages/3d/6f/79b0d760907965acfd9d61826a3d41f8f093c538f55cd2633d3f0db269f6/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:1465387ac63576c3e125e5337a6892b9e99e0627d52317f3ca79e6930d889d15", size = 1977417, upload-time = "2026-03-09T13:13:39.966Z" }, - { url = "https://files.pythonhosted.org/packages/ab/31/01d0537c41cb75a551a438c3c7a80d0c60d60b81f694dac83dd436aec0d0/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:530a3fd64c87cffa844d4b6b9768774763d9caa299e9b75d8eca6a4423b31314", size = 2491238, upload-time = "2026-03-09T13:13:41.698Z" }, - { url = "https://files.pythonhosted.org/packages/e4/34/8aefdd0be9cfd00a44509251ba864f5caf2991e36772e61c408007e7f417/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1d9daea4ea6b9be74fe2f01f7fbade8d6ffab263e781274cffca0dba9be9eec9", size = 2294947, upload-time = "2026-03-09T13:13:43.343Z" }, - { url = "https://files.pythonhosted.org/packages/ad/cf/0348374369ca588f8fe9c338fae49fa4e16eeb10ffb3d012f23a54578a9e/kiwisolver-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:f18c2d9782259a6dc132fdc7a63c168cbc74b35284b6d75c673958982a378384", size = 73569, upload-time = "2026-03-09T13:13:45.792Z" }, - { url = "https://files.pythonhosted.org/packages/28/26/192b26196e2316e2bd29deef67e37cdf9870d9af8e085e521afff0fed526/kiwisolver-1.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:f7c7553b13f69c1b29a5bde08ddc6d9d0c8bfb84f9ed01c30db25944aeb852a7", size = 64997, upload-time = "2026-03-09T13:13:46.878Z" }, - { url = "https://files.pythonhosted.org/packages/9d/69/024d6711d5ba575aa65d5538042e99964104e97fa153a9f10bc369182bc2/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:fd40bb9cd0891c4c3cb1ddf83f8bbfa15731a248fdc8162669405451e2724b09", size = 123166, upload-time = "2026-03-09T13:13:48.032Z" }, - { url = "https://files.pythonhosted.org/packages/ce/48/adbb40df306f587054a348831220812b9b1d787aff714cfbc8556e38fccd/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c0e1403fd7c26d77c1f03e096dc58a5c726503fa0db0456678b8668f76f521e3", size = 66395, upload-time = "2026-03-09T13:13:49.365Z" }, - { url = "https://files.pythonhosted.org/packages/a8/3a/d0a972b34e1c63e2409413104216cd1caa02c5a37cb668d1687d466c1c45/kiwisolver-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dda366d548e89a90d88a86c692377d18d8bd64b39c1fb2b92cb31370e2896bbd", size = 64065, upload-time = "2026-03-09T13:13:50.562Z" }, - { url = "https://files.pythonhosted.org/packages/2b/0a/7b98e1e119878a27ba8618ca1e18b14f992ff1eda40f47bccccf4de44121/kiwisolver-1.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:332b4f0145c30b5f5ad9374881133e5aa64320428a57c2c2b61e9d891a51c2f3", size = 1477903, upload-time = "2026-03-09T13:13:52.084Z" }, - { url = "https://files.pythonhosted.org/packages/18/d8/55638d89ffd27799d5cc3d8aa28e12f4ce7a64d67b285114dbedc8ea4136/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c50b89ffd3e1a911c69a1dd3de7173c0cd10b130f56222e57898683841e4f96", size = 1278751, upload-time = "2026-03-09T13:13:54.673Z" }, - { url = "https://files.pythonhosted.org/packages/b8/97/b4c8d0d18421ecceba20ad8701358453b88e32414e6f6950b5a4bad54e65/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4db576bb8c3ef9365f8b40fe0f671644de6736ae2c27a2c62d7d8a1b4329f099", size = 1296793, upload-time = "2026-03-09T13:13:56.287Z" }, - { url = "https://files.pythonhosted.org/packages/c4/10/f862f94b6389d8957448ec9df59450b81bec4abb318805375c401a1e6892/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0b85aad90cea8ac6797a53b5d5f2e967334fa4d1149f031c4537569972596cb8", size = 1346041, upload-time = "2026-03-09T13:13:58.269Z" }, - { url = "https://files.pythonhosted.org/packages/a3/6a/f1650af35821eaf09de398ec0bc2aefc8f211f0cda50204c9f1673741ba9/kiwisolver-1.5.0-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:d36ca54cb4c6c4686f7cbb7b817f66f5911c12ddb519450bbe86707155028f87", size = 987292, upload-time = "2026-03-09T13:13:59.871Z" }, - { url = "https://files.pythonhosted.org/packages/de/19/d7fb82984b9238115fe629c915007be608ebd23dc8629703d917dbfaffd4/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:38f4a703656f493b0ad185211ccfca7f0386120f022066b018eb5296d8613e23", size = 2227865, upload-time = "2026-03-09T13:14:01.401Z" }, - { url = "https://files.pythonhosted.org/packages/7f/b9/46b7f386589fd222dac9e9de9c956ce5bcefe2ee73b4e79891381dda8654/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3ac2360e93cb41be81121755c6462cff3beaa9967188c866e5fce5cf13170859", size = 2324369, upload-time = "2026-03-09T13:14:02.972Z" }, - { url = "https://files.pythonhosted.org/packages/92/8b/95e237cf3d9c642960153c769ddcbe278f182c8affb20cecc1cc983e7cc5/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c95cab08d1965db3d84a121f1c7ce7479bdd4072c9b3dafd8fecce48a2e6b902", size = 1977989, upload-time = "2026-03-09T13:14:04.503Z" }, - { url = "https://files.pythonhosted.org/packages/1b/95/980c9df53501892784997820136c01f62bc1865e31b82b9560f980c0e649/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fc20894c3d21194d8041a28b65622d5b86db786da6e3cfe73f0c762951a61167", size = 2491645, upload-time = "2026-03-09T13:14:06.106Z" }, - { url = "https://files.pythonhosted.org/packages/cb/32/900647fd0840abebe1561792c6b31e6a7c0e278fc3973d30572a965ca14c/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7a32f72973f0f950c1920475d5c5ea3d971b81b6f0ec53b8d0a956cc965f22e0", size = 2295237, upload-time = "2026-03-09T13:14:08.891Z" }, - { url = "https://files.pythonhosted.org/packages/be/8a/be60e3bbcf513cc5a50f4a3e88e1dcecebb79c1ad607a7222877becaa101/kiwisolver-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bf3acf1419fa93064a4c2189ac0b58e3be7872bf6ee6177b0d4c63dc4cea276", size = 73573, upload-time = "2026-03-09T13:14:12.327Z" }, - { url = "https://files.pythonhosted.org/packages/4d/d2/64be2e429eb4fca7f7e1c52a91b12663aeaf25de3895e5cca0f47ef2a8d0/kiwisolver-1.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:fa8eb9ecdb7efb0b226acec134e0d709e87a909fa4971a54c0c4f6e88635484c", size = 64998, upload-time = "2026-03-09T13:14:13.469Z" }, - { url = "https://files.pythonhosted.org/packages/b0/69/ce68dd0c85755ae2de490bf015b62f2cea5f6b14ff00a463f9d0774449ff/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:db485b3847d182b908b483b2ed133c66d88d49cacf98fd278fadafe11b4478d1", size = 125700, upload-time = "2026-03-09T13:14:14.636Z" }, - { url = "https://files.pythonhosted.org/packages/74/aa/937aac021cf9d4349990d47eb319309a51355ed1dbdc9c077cdc9224cb11/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:be12f931839a3bdfe28b584db0e640a65a8bcbc24560ae3fdb025a449b3d754e", size = 67537, upload-time = "2026-03-09T13:14:15.808Z" }, - { url = "https://files.pythonhosted.org/packages/ee/20/3a87fbece2c40ad0f6f0aefa93542559159c5f99831d596050e8afae7a9f/kiwisolver-1.5.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:16b85d37c2cbb3253226d26e64663f755d88a03439a9c47df6246b35defbdfb7", size = 65514, upload-time = "2026-03-09T13:14:18.035Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7f/f943879cda9007c45e1f7dba216d705c3a18d6b35830e488b6c6a4e7cdf0/kiwisolver-1.5.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4432b835675f0ea7414aab3d37d119f7226d24869b7a829caeab49ebda407b0c", size = 1584848, upload-time = "2026-03-09T13:14:19.745Z" }, - { url = "https://files.pythonhosted.org/packages/37/f8/4d4f85cc1870c127c88d950913370dd76138482161cd07eabbc450deff01/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b0feb50971481a2cc44d94e88bdb02cdd497618252ae226b8eb1201b957e368", size = 1391542, upload-time = "2026-03-09T13:14:21.54Z" }, - { url = "https://files.pythonhosted.org/packages/04/0b/65dd2916c84d252b244bd405303220f729e7c17c9d7d33dca6feeff9ffc4/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56fa888f10d0f367155e76ce849fa1166fc9730d13bd2d65a2aa13b6f5424489", size = 1404447, upload-time = "2026-03-09T13:14:23.205Z" }, - { url = "https://files.pythonhosted.org/packages/39/5c/2606a373247babce9b1d056c03a04b65f3cf5290a8eac5d7bdead0a17e21/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:940dda65d5e764406b9fb92761cbf462e4e63f712ab60ed98f70552e496f3bf1", size = 1455918, upload-time = "2026-03-09T13:14:24.74Z" }, - { url = "https://files.pythonhosted.org/packages/d5/d1/c6078b5756670658e9192a2ef11e939c92918833d2745f85cd14a6004bdf/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_39_riscv64.whl", hash = "sha256:89fc958c702ee9a745e4700378f5d23fddbc46ff89e8fdbf5395c24d5c1452a3", size = 1072856, upload-time = "2026-03-09T13:14:26.597Z" }, - { url = "https://files.pythonhosted.org/packages/cb/c8/7def6ddf16eb2b3741d8b172bdaa9af882b03c78e9b0772975408801fa63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9027d773c4ff81487181a925945743413f6069634d0b122d0b37684ccf4f1e18", size = 2333580, upload-time = "2026-03-09T13:14:28.237Z" }, - { url = "https://files.pythonhosted.org/packages/9e/87/2ac1fce0eb1e616fcd3c35caa23e665e9b1948bb984f4764790924594128/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:5b233ea3e165e43e35dba1d2b8ecc21cf070b45b65ae17dd2747d2713d942021", size = 2423018, upload-time = "2026-03-09T13:14:30.018Z" }, - { url = "https://files.pythonhosted.org/packages/67/13/c6700ccc6cc218716bfcda4935e4b2997039869b4ad8a94f364c5a3b8e63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ce9bf03dad3b46408c08649c6fbd6ca28a9fce0eb32fdfffa6775a13103b5310", size = 2062804, upload-time = "2026-03-09T13:14:32.888Z" }, - { url = "https://files.pythonhosted.org/packages/1b/bd/877056304626943ff0f1f44c08f584300c199b887cb3176cd7e34f1515f1/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:fc4d3f1fb9ca0ae9f97b095963bc6326f1dbfd3779d6679a1e016b9baaa153d3", size = 2597482, upload-time = "2026-03-09T13:14:34.971Z" }, - { url = "https://files.pythonhosted.org/packages/75/19/c60626c47bf0f8ac5dcf72c6c98e266d714f2fbbfd50cf6dab5ede3aaa50/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f443b4825c50a51ee68585522ab4a1d1257fac65896f282b4c6763337ac9f5d2", size = 2394328, upload-time = "2026-03-09T13:14:36.816Z" }, - { url = "https://files.pythonhosted.org/packages/47/84/6a6d5e5bb8273756c27b7d810d47f7ef2f1f9b9fd23c9ee9a3f8c75c9cef/kiwisolver-1.5.0-cp313-cp313t-win_arm64.whl", hash = "sha256:893ff3a711d1b515ba9da14ee090519bad4610ed1962fbe298a434e8c5f8db53", size = 68410, upload-time = "2026-03-09T13:14:38.695Z" }, - { url = "https://files.pythonhosted.org/packages/e4/d7/060f45052f2a01ad5762c8fdecd6d7a752b43400dc29ff75cd47225a40fd/kiwisolver-1.5.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8df31fe574b8b3993cc61764f40941111b25c2d9fea13d3ce24a49907cd2d615", size = 123231, upload-time = "2026-03-09T13:14:41.323Z" }, - { url = "https://files.pythonhosted.org/packages/c2/a7/78da680eadd06ff35edef6ef68a1ad273bad3e2a0936c9a885103230aece/kiwisolver-1.5.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1d49a49ac4cbfb7c1375301cd1ec90169dfeae55ff84710d782260ce77a75a02", size = 66489, upload-time = "2026-03-09T13:14:42.534Z" }, - { url = "https://files.pythonhosted.org/packages/49/b2/97980f3ad4fae37dd7fe31626e2bf75fbf8bdf5d303950ec1fab39a12da8/kiwisolver-1.5.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0cbe94b69b819209a62cb27bdfa5dc2a8977d8de2f89dfd97ba4f53ed3af754e", size = 64063, upload-time = "2026-03-09T13:14:44.759Z" }, - { url = "https://files.pythonhosted.org/packages/e7/f9/b06c934a6aa8bc91f566bd2a214fd04c30506c2d9e2b6b171953216a65b6/kiwisolver-1.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:80aa065ffd378ff784822a6d7c3212f2d5f5e9c3589614b5c228b311fd3063ac", size = 1475913, upload-time = "2026-03-09T13:14:46.247Z" }, - { url = "https://files.pythonhosted.org/packages/6b/f0/f768ae564a710135630672981231320bc403cf9152b5596ec5289de0f106/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e7f886f47ab881692f278ae901039a234e4025a68e6dfab514263a0b1c4ae05", size = 1282782, upload-time = "2026-03-09T13:14:48.458Z" }, - { url = "https://files.pythonhosted.org/packages/e2/9f/1de7aad00697325f05238a5f2eafbd487fb637cc27a558b5367a5f37fb7f/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5060731cc3ed12ca3a8b57acd4aeca5bbc2f49216dd0bec1650a1acd89486bcd", size = 1300815, upload-time = "2026-03-09T13:14:50.721Z" }, - { url = "https://files.pythonhosted.org/packages/5a/c2/297f25141d2e468e0ce7f7a7b92e0cf8918143a0cbd3422c1ad627e85a06/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7a4aa69609f40fce3cbc3f87b2061f042eee32f94b8f11db707b66a26461591a", size = 1347925, upload-time = "2026-03-09T13:14:52.304Z" }, - { url = "https://files.pythonhosted.org/packages/b9/d3/f4c73a02eb41520c47610207b21afa8cdd18fdbf64ffd94674ae21c4812d/kiwisolver-1.5.0-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:d168fda2dbff7b9b5f38e693182d792a938c31db4dac3a80a4888de603c99554", size = 991322, upload-time = "2026-03-09T13:14:54.637Z" }, - { url = "https://files.pythonhosted.org/packages/7b/46/d3f2efef7732fcda98d22bf4ad5d3d71d545167a852ca710a494f4c15343/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:413b820229730d358efd838ecbab79902fe97094565fdc80ddb6b0a18c18a581", size = 2232857, upload-time = "2026-03-09T13:14:56.471Z" }, - { url = "https://files.pythonhosted.org/packages/3f/ec/2d9756bf2b6d26ae4349b8d3662fb3993f16d80c1f971c179ce862b9dbae/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5124d1ea754509b09e53738ec185584cc609aae4a3b510aaf4ed6aa047ef9303", size = 2329376, upload-time = "2026-03-09T13:14:58.072Z" }, - { url = "https://files.pythonhosted.org/packages/8f/9f/876a0a0f2260f1bde92e002b3019a5fabc35e0939c7d945e0fa66185eb20/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e4415a8db000bf49a6dd1c478bf70062eaacff0f462b92b0ba68791a905861f9", size = 1982549, upload-time = "2026-03-09T13:14:59.668Z" }, - { url = "https://files.pythonhosted.org/packages/6c/4f/ba3624dfac23a64d54ac4179832860cb537c1b0af06024936e82ca4154a0/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d618fd27420381a4f6044faa71f46d8bfd911bd077c555f7138ed88729bfbe79", size = 2494680, upload-time = "2026-03-09T13:15:01.364Z" }, - { url = "https://files.pythonhosted.org/packages/39/b7/97716b190ab98911b20d10bf92eca469121ec483b8ce0edd314f51bc85af/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5092eb5b1172947f57d6ea7d89b2f29650414e4293c47707eb499ec07a0ac796", size = 2297905, upload-time = "2026-03-09T13:15:03.925Z" }, - { url = "https://files.pythonhosted.org/packages/a3/36/4e551e8aa55c9188bca9abb5096805edbf7431072b76e2298e34fd3a3008/kiwisolver-1.5.0-cp314-cp314-win_amd64.whl", hash = "sha256:d76e2d8c75051d58177e762164d2e9ab92886534e3a12e795f103524f221dd8e", size = 75086, upload-time = "2026-03-09T13:15:07.775Z" }, - { url = "https://files.pythonhosted.org/packages/70/15/9b90f7df0e31a003c71649cf66ef61c3c1b862f48c81007fa2383c8bd8d7/kiwisolver-1.5.0-cp314-cp314-win_arm64.whl", hash = "sha256:fa6248cd194edff41d7ea9425ced8ca3a6f838bfb295f6f1d6e6bb694a8518df", size = 66577, upload-time = "2026-03-09T13:15:09.139Z" }, - { url = "https://files.pythonhosted.org/packages/17/01/7dc8c5443ff42b38e72731643ed7cf1ed9bf01691ae5cdca98501999ed83/kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:d1ffeb80b5676463d7a7d56acbe8e37a20ce725570e09549fe738e02ca6b7e1e", size = 125794, upload-time = "2026-03-09T13:15:10.525Z" }, - { url = "https://files.pythonhosted.org/packages/46/8a/b4ebe46ebaac6a303417fab10c2e165c557ddaff558f9699d302b256bc53/kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:bc4d8e252f532ab46a1de9349e2d27b91fce46736a9eedaa37beaca66f574ed4", size = 67646, upload-time = "2026-03-09T13:15:12.016Z" }, - { url = "https://files.pythonhosted.org/packages/60/35/10a844afc5f19d6f567359bf4789e26661755a2f36200d5d1ed8ad0126e5/kiwisolver-1.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6783e069732715ad0c3ce96dbf21dbc2235ab0593f2baf6338101f70371f4028", size = 65511, upload-time = "2026-03-09T13:15:13.311Z" }, - { url = "https://files.pythonhosted.org/packages/f8/8a/685b297052dd041dcebce8e8787b58923b6e78acc6115a0dc9189011c44b/kiwisolver-1.5.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e7c4c09a490dc4d4a7f8cbee56c606a320f9dc28cf92a7157a39d1ce7676a657", size = 1584858, upload-time = "2026-03-09T13:15:15.103Z" }, - { url = "https://files.pythonhosted.org/packages/9e/80/04865e3d4638ac5bddec28908916df4a3075b8c6cc101786a96803188b96/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2a075bd7bd19c70cf67c8badfa36cf7c5d8de3c9ddb8420c51e10d9c50e94920", size = 1392539, upload-time = "2026-03-09T13:15:16.661Z" }, - { url = "https://files.pythonhosted.org/packages/ba/01/77a19cacc0893fa13fafa46d1bba06fb4dc2360b3292baf4b56d8e067b24/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bdd3e53429ff02aa319ba59dfe4ceeec345bf46cf180ec2cf6fd5b942e7975e9", size = 1405310, upload-time = "2026-03-09T13:15:18.229Z" }, - { url = "https://files.pythonhosted.org/packages/53/39/bcaf5d0cca50e604cfa9b4e3ae1d64b50ca1ae5b754122396084599ef903/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cdcb35dc9d807259c981a85531048ede628eabcffb3239adf3d17463518992d", size = 1456244, upload-time = "2026-03-09T13:15:20.444Z" }, - { url = "https://files.pythonhosted.org/packages/d0/7a/72c187abc6975f6978c3e39b7cf67aeb8b3c0a8f9790aa7fd412855e9e1f/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:70d593af6a6ca332d1df73d519fddb5148edb15cd90d5f0155e3746a6d4fcc65", size = 1073154, upload-time = "2026-03-09T13:15:22.039Z" }, - { url = "https://files.pythonhosted.org/packages/c7/ca/cf5b25783ebbd59143b4371ed0c8428a278abe68d6d0104b01865b1bbd0f/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:377815a8616074cabbf3f53354e1d040c35815a134e01d7614b7692e4bf8acfa", size = 2334377, upload-time = "2026-03-09T13:15:23.741Z" }, - { url = "https://files.pythonhosted.org/packages/4a/e5/b1f492adc516796e88751282276745340e2a72dcd0d36cf7173e0daf3210/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0255a027391d52944eae1dbb5d4cc5903f57092f3674e8e544cdd2622826b3f0", size = 2425288, upload-time = "2026-03-09T13:15:25.789Z" }, - { url = "https://files.pythonhosted.org/packages/e6/e5/9b21fbe91a61b8f409d74a26498706e97a48008bfcd1864373d32a6ba31c/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:012b1eb16e28718fa782b5e61dc6f2da1f0792ca73bd05d54de6cb9561665fc9", size = 2063158, upload-time = "2026-03-09T13:15:27.63Z" }, - { url = "https://files.pythonhosted.org/packages/b1/02/83f47986138310f95ea95531f851b2a62227c11cbc3e690ae1374fe49f0f/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0e3aafb33aed7479377e5e9a82e9d4bf87063741fc99fc7ae48b0f16e32bdd6f", size = 2597260, upload-time = "2026-03-09T13:15:29.421Z" }, - { url = "https://files.pythonhosted.org/packages/07/18/43a5f24608d8c313dd189cf838c8e68d75b115567c6279de7796197cfb6a/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e7a116ae737f0000343218c4edf5bd45893bfeaff0993c0b215d7124c9f77646", size = 2394403, upload-time = "2026-03-09T13:15:31.517Z" }, - { url = "https://files.pythonhosted.org/packages/3b/b5/98222136d839b8afabcaa943b09bd05888c2d36355b7e448550211d1fca4/kiwisolver-1.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1dd9b0b119a350976a6d781e7278ec7aca0b201e1a9e2d23d9804afecb6ca681", size = 79687, upload-time = "2026-03-09T13:15:33.204Z" }, - { url = "https://files.pythonhosted.org/packages/99/a2/ca7dc962848040befed12732dff6acae7fb3c4f6fc4272b3f6c9a30b8713/kiwisolver-1.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:58f812017cd2985c21fbffb4864d59174d4903dd66fa23815e74bbc7a0e2dd57", size = 70032, upload-time = "2026-03-09T13:15:34.411Z" }, - { url = "https://files.pythonhosted.org/packages/1c/fa/2910df836372d8761bb6eff7d8bdcb1613b5c2e03f260efe7abe34d388a7/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_10_13_x86_64.whl", hash = "sha256:5ae8e62c147495b01a0f4765c878e9bfdf843412446a247e28df59936e99e797", size = 130262, upload-time = "2026-03-09T13:15:35.629Z" }, - { url = "https://files.pythonhosted.org/packages/0f/41/c5f71f9f00aabcc71fee8b7475e3f64747282580c2fe748961ba29b18385/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:f6764a4ccab3078db14a632420930f6186058750df066b8ea2a7106df91d3203", size = 138036, upload-time = "2026-03-09T13:15:36.894Z" }, - { url = "https://files.pythonhosted.org/packages/fa/06/7399a607f434119c6e1fdc8ec89a8d51ccccadf3341dee4ead6bd14caaf5/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c31c13da98624f957b0fb1b5bae5383b2333c2c3f6793d9825dd5ce79b525cb7", size = 194295, upload-time = "2026-03-09T13:15:38.22Z" }, - { url = "https://files.pythonhosted.org/packages/b5/91/53255615acd2a1eaca307ede3c90eb550bae9c94581f8c00081b6b1c8f44/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-win_amd64.whl", hash = "sha256:1f1489f769582498610e015a8ef2d36f28f505ab3096d0e16b4858a9ec214f57", size = 75987, upload-time = "2026-03-09T13:15:39.65Z" }, - { url = "https://files.pythonhosted.org/packages/17/6f/6fd4f690a40c2582fa34b97d2678f718acf3706b91d270c65ecb455d0a06/kiwisolver-1.5.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:295d9ffe712caa9f8a3081de8d32fc60191b4b51c76f02f951fd8407253528f4", size = 59606, upload-time = "2026-03-09T13:15:40.81Z" }, - { url = "https://files.pythonhosted.org/packages/82/a0/2355d5e3b338f13ce63f361abb181e3b6ea5fffdb73f739b3e80efa76159/kiwisolver-1.5.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:51e8c4084897de9f05898c2c2a39af6318044ae969d46ff7a34ed3f96274adca", size = 57537, upload-time = "2026-03-09T13:15:42.071Z" }, - { url = "https://files.pythonhosted.org/packages/c8/b9/1d50e610ecadebe205b71d6728fd224ce0e0ca6aba7b9cbe1da049203ac5/kiwisolver-1.5.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b83af57bdddef03c01a9138034c6ff03181a3028d9a1003b301eb1a55e161a3f", size = 79888, upload-time = "2026-03-09T13:15:43.317Z" }, - { url = "https://files.pythonhosted.org/packages/cd/ee/b85ffcd75afed0357d74f0e6fc02a4507da441165de1ca4760b9f496390d/kiwisolver-1.5.0-pp310-pypy310_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf4679a3d71012a7c2bf360e5cd878fbd5e4fcac0896b56393dec239d81529ed", size = 77584, upload-time = "2026-03-09T13:15:44.605Z" }, - { url = "https://files.pythonhosted.org/packages/6b/dd/644d0dde6010a8583b4cd66dd41c5f83f5325464d15c4f490b3340ab73b4/kiwisolver-1.5.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:41024ed50e44ab1a60d3fe0a9d15a4ccc9f5f2b1d814ff283c8d01134d5b81bc", size = 73390, upload-time = "2026-03-09T13:15:45.832Z" }, - { url = "https://files.pythonhosted.org/packages/e9/eb/5fcbbbf9a0e2c3a35effb88831a483345326bbc3a030a3b5b69aee647f84/kiwisolver-1.5.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ec4c85dc4b687c7f7f15f553ff26a98bfe8c58f5f7f0ac8905f0ba4c7be60232", size = 59532, upload-time = "2026-03-09T13:15:47.047Z" }, - { url = "https://files.pythonhosted.org/packages/c3/9b/e17104555bb4db148fd52327feea1e96be4b88e8e008b029002c281a21ab/kiwisolver-1.5.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:12e91c215a96e39f57989c8912ae761286ac5a9584d04030ceb3368a357f017a", size = 57420, upload-time = "2026-03-09T13:15:48.199Z" }, - { url = "https://files.pythonhosted.org/packages/48/44/2b5b95b7aa39fb2d8d9d956e0f3d5d45aef2ae1d942d4c3ffac2f9cfed1a/kiwisolver-1.5.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be4a51a55833dc29ab5d7503e7bcb3b3af3402d266018137127450005cdfe737", size = 79892, upload-time = "2026-03-09T13:15:49.694Z" }, - { url = "https://files.pythonhosted.org/packages/52/7d/7157f9bba6b455cfb4632ed411e199fc8b8977642c2b12082e1bd9e6d173/kiwisolver-1.5.0-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:daae526907e262de627d8f70058a0f64acc9e2641c164c99c8f594b34a799a16", size = 77603, upload-time = "2026-03-09T13:15:50.945Z" }, - { url = "https://files.pythonhosted.org/packages/0a/dd/8050c947d435c8d4bc94e3252f4d8bb8a76cfb424f043a8680be637a57f1/kiwisolver-1.5.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:59cd8683f575d96df5bb48f6add94afc055012c29e28124fcae2b63661b9efb1", size = 73558, upload-time = "2026-03-09T13:15:52.112Z" }, -] - -[[package]] -name = "lalsuite" -version = "7.26.11" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "astropy", version = "6.1.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "astropy", version = "7.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "igwn-ligolw" }, - { name = "igwn-segments" }, - { name = "lscsoft-glue" }, - { name = "matplotlib" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "python-dateutil" }, - { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/f1/c745b5bc297c27e15dcf5c6e4e93de34fcdfbf62ff7cdd279c72c3c537d3/lalsuite-7.26.11-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:e09d6c7bccf98d230702f53b1c75345c0f2bd8c3bca0d52f3ee3f8e4760340bc", size = 38241394, upload-time = "2026-04-27T08:25:51.558Z" }, - { url = "https://files.pythonhosted.org/packages/9d/6d/863e4477633a5f8d5cfc37a920bed776a3a499ea7437f43265be674d9ba9/lalsuite-7.26.11-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:e93b95b201337dcc50d2549ed8161edcab3eed93908b06d96e5fa755d64c7df2", size = 39559904, upload-time = "2026-04-27T08:25:55.102Z" }, - { url = "https://files.pythonhosted.org/packages/72/9c/7c0a4b303c50d769ba2317a328bf71ece25468226ea24c5d7713df349bbf/lalsuite-7.26.11-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6d8020057afd9f5a342119f89f25eae48f81644e53b942f5176c5158d2484281", size = 38077102, upload-time = "2026-04-27T08:25:58.305Z" }, - { url = "https://files.pythonhosted.org/packages/97/0e/6cb88e6ce4a0fe47fd19a878c70e32f7feff7b228a61584ab66ccee6c0d4/lalsuite-7.26.11-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:0b86e12bec3c92dba75f507242729c5d4d77f8228afb24431a663b04b504d7d3", size = 40137837, upload-time = "2026-04-27T08:26:01.459Z" }, - { url = "https://files.pythonhosted.org/packages/e3/5d/8dc7dd53cd14f58dcb6d75912a79977c9f5b3fd0ff8fc45bc5b521ceeeb8/lalsuite-7.26.11-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:53f3099bd87f4512ddd50819549a725197e250371f5b43150ceb73695573d689", size = 38710999, upload-time = "2026-04-27T08:26:04.495Z" }, - { url = "https://files.pythonhosted.org/packages/1a/50/f0ce97154c4e749a6f1d1a0763d647a157d2289895a3e87041427bc7f070/lalsuite-7.26.11-cp311-cp311-macosx_13_0_x86_64.whl", hash = "sha256:2f6a75deb4a46fea4f8be5efbdc2d5349a4c2042584328b2e97645037537a622", size = 40219823, upload-time = "2026-04-27T08:26:07.673Z" }, - { url = "https://files.pythonhosted.org/packages/e1/4e/0d27d65f94c2ac4bd7e22ac81beee48a646d81d911c9dc4145cb1078296e/lalsuite-7.26.11-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:6e52504199a95e6df747f269085c061396687398c332923a5c049f9401ab75a6", size = 38077168, upload-time = "2026-04-27T08:26:10.806Z" }, - { url = "https://files.pythonhosted.org/packages/3c/f6/9c1b0d9d4d7465aeeda5ef725dc78616674ff14f83f3f8f04c05f4d17904/lalsuite-7.26.11-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:35d53b1f38e64320368aaf9257c6aea34dfe4c727f3ddafec6e9debccbcd7686", size = 40139374, upload-time = "2026-04-27T08:26:14.147Z" }, - { url = "https://files.pythonhosted.org/packages/68/2d/7b11e16d87780e2a7d779f9db4e1b1a9838d2f38c87b4da04ba7536928de/lalsuite-7.26.11-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:637733bb5ee5987c12d290b920b3e168d62eea720f40224bad21aad0a29be8a3", size = 38888265, upload-time = "2026-04-27T08:26:17.028Z" }, - { url = "https://files.pythonhosted.org/packages/36/4d/c59f85c1675362b70ef660521b81621792f5039fffcbf205ece019a0b343/lalsuite-7.26.11-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:bc7ba72e82a15c4bf6d8af28a228a76ed4dd01b6b1f99d67827426a7161e4998", size = 40319766, upload-time = "2026-04-27T08:26:20.875Z" }, - { url = "https://files.pythonhosted.org/packages/85/fd/9313f82301340d5ee90fb9c9adab017d63ed696ea2350c579f8add88d0a3/lalsuite-7.26.11-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:035024031fe2e0f9a2dfee811c307534b859acadaba7398aaf09edb061c970f6", size = 38055937, upload-time = "2026-04-27T08:26:24.463Z" }, - { url = "https://files.pythonhosted.org/packages/3f/74/9cf796165b9f730db561524c63bb959531785eceb7ef94637c1ba00ffa47/lalsuite-7.26.11-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:cc667d2676655d9cdf53f2d0b17c0fcc048cbb55298af18b3adf088f56c0f666", size = 40115490, upload-time = "2026-04-27T08:26:27.574Z" }, - { url = "https://files.pythonhosted.org/packages/f6/aa/89231f955274443473b182b2474b68b34b7941218a38c5334ca9ceee771b/lalsuite-7.26.11-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:6e3eca2f13f7b953f57d5c7610f22c095ede887025c599fa718e4b6344b190c6", size = 38778514, upload-time = "2026-04-27T08:26:31.123Z" }, - { url = "https://files.pythonhosted.org/packages/ce/7b/dd86fa3caa5012131514b254e1c22f4900bc1bebe48a6c11f877cf8e9a41/lalsuite-7.26.11-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:e0eedf77f4fa99ef2a826a79b50311a7dbed3c46836ceee110c0535b0f7726ce", size = 40431172, upload-time = "2026-04-27T08:26:34.462Z" }, - { url = "https://files.pythonhosted.org/packages/6f/c1/b1352c27faeaa1a0c8394c182c78d2949277e94342ca971956762afc91ab/lalsuite-7.26.11-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:dece4ae7ee5ddb7924bf83dc0fd928c52aa981958054d7a08ce633506695883f", size = 38053524, upload-time = "2026-04-27T08:26:37.635Z" }, - { url = "https://files.pythonhosted.org/packages/e3/aa/79c6c4b44dbd3f52bb1d66b83fdb40a3530c77e44aca67a1a201cf6f14a5/lalsuite-7.26.11-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5d324806f50aba1efad7f25671f6c814116f301f7d2940bb1f69416da2970570", size = 40113540, upload-time = "2026-04-27T08:26:40.903Z" }, - { url = "https://files.pythonhosted.org/packages/1f/52/f34c4d0bf38056439c0cdc41d8f231727680d217858f542013a620424894/lalsuite-7.26.11-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:30ca69d6844fd942b536b197346488388d50db0dc12f1d097a22fbeb53172e41", size = 39222715, upload-time = "2026-04-27T08:26:44.371Z" }, - { url = "https://files.pythonhosted.org/packages/d6/d8/840d2156c7b984fc33131c1f47ce062e311b9f4c9d717bc0b4b2f62f2e30/lalsuite-7.26.11-cp314-cp314-macosx_13_0_x86_64.whl", hash = "sha256:cd3ca3a05f91250d357c63eb4e0795e914197e1a9a274c0f2d2da3d785d78566", size = 40407326, upload-time = "2026-04-27T08:26:47.879Z" }, - { url = "https://files.pythonhosted.org/packages/f2/d1/d7b3a5a7af9fd808bc0660c61696387b7ea51222133297be82287f6636b8/lalsuite-7.26.11-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:68122eff45fc3d4f6906d701ceddeb360ebb1dc0d8784aca8ea27dcd8c2379b1", size = 38088119, upload-time = "2026-04-27T08:26:51.311Z" }, - { url = "https://files.pythonhosted.org/packages/aa/1c/7875d7e53282606ec1ad14eb4982bb34a48cf884de25c218348f7be1301b/lalsuite-7.26.11-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:7b5ae63f512459586f7856a81186489742922ba647f97fa21be3bb0fc434ee52", size = 40133782, upload-time = "2026-04-27T08:26:54.543Z" }, - { url = "https://files.pythonhosted.org/packages/22/d2/42d97504dd88ee4962d39e89335106dcb1a36afd316acd1d48a1b81830f7/lalsuite-7.26.11-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:ce4aca4e9f055dbb2c4fb35bab3958588d15aa68303071e6a6ad3f4b7498d009", size = 38091760, upload-time = "2026-04-27T08:26:57.57Z" }, - { url = "https://files.pythonhosted.org/packages/0b/fe/526f5caa17888d26c0cb98ec976b3485fd299e981651b74379c4d8f328a2/lalsuite-7.26.11-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:a1f37616585f92a5fe20f16a041f25135b73e7d53c8ab59d59fec6c2c7ee43d8", size = 40120289, upload-time = "2026-04-27T08:27:00.577Z" }, -] - -[[package]] -name = "ligotimegps" -version = "2.0.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -sdist = { url = "https://files.pythonhosted.org/packages/39/2e/cef2ee4c4f3f1f04566e3e7e9343811a74f6e9a0bc6ef4711248f132e3bb/ligotimegps-2.0.1.tar.gz", hash = "sha256:88626c02ad9a464d1242a1147b40074792f424bafa2ab013eee629c7d1b6469c", size = 35191, upload-time = "2019-04-25T16:00:25.481Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/b6/6d6d0585fa2ae936a9f5d411b1f0fbe9fcb0aca0c51a775aa4f8f95fdf5e/ligotimegps-2.0.1-py2.py3-none-any.whl", hash = "sha256:da8c1289ba1310337ef5177e7936e25ce47d4e8e6a269cbdd5e9abfc5b5db490", size = 19930, upload-time = "2019-04-25T16:00:23.354Z" }, -] - -[[package]] -name = "ligotimegps" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'win32'", - "python_full_version == '3.12.*' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version == '3.12.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", -] -sdist = { url = "https://files.pythonhosted.org/packages/7e/9b/521be61daa7603603826d40fa6327a22e04df4f20e69d9ed42182370a7f8/ligotimegps-2.1.0.tar.gz", hash = "sha256:d948ffc4d58472b303478a983ec56d2e6a7f35e54cbb351ca6f1292e68398cb2", size = 28069, upload-time = "2025-10-07T10:55:28.124Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/92/38674acb59c663bcdb69522e11afd3b4e6371f74b5764aeec2e99308cd79/ligotimegps-2.1.0-py3-none-any.whl", hash = "sha256:14dbbb07b175b94b4e1a519d7baa4548f0ea07bc71ef7d7f096ad8397d359043", size = 23370, upload-time = "2025-10-07T10:55:28.855Z" }, -] - -[[package]] -name = "lineax" -version = "0.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "equinox" }, - { name = "jax", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "jax", version = "0.10.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "jaxtyping", version = "0.3.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "jaxtyping", version = "0.3.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/35/d6/4e28416a6fe58dd6bc7565b1ffa330f4d0ba7d74212642b1b734c511299e/lineax-0.1.0.tar.gz", hash = "sha256:5f1a8f060142af2cdbf7d66b99e8d3071c3aa734b677df6339df4b4c4c0554d2", size = 50209, upload-time = "2026-01-27T21:17:26.652Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/80/0c/2ed47112fc1958a0a81c9b015d4e1861953a1ec3a17b081c0180a25ce82c/lineax-0.1.0-py3-none-any.whl", hash = "sha256:f00911c6b07d427c4835db46856970c8348bc82a035b51f4386ad09382af957a", size = 74600, upload-time = "2026-01-27T21:17:25.33Z" }, -] - -[[package]] -name = "lscsoft-glue" -version = "4.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "igwn-segments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/de/0b/cf319595547297bc21d6f6228c07f6e63b6c7f47fc9d3ac47ee6998b06ee/lscsoft_glue-4.1.1.tar.gz", hash = "sha256:487df9085eaafe635dd62445e744017c1c90cc696be76ac6b654bd70841ae733", size = 56280, upload-time = "2025-07-18T14:52:45.284Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/cc/a78f98ed9c3ac814072e4db226a4e2b27a58a62fed8341de2459c70b8036/lscsoft_glue-4.1.1-py3-none-any.whl", hash = "sha256:3e34b0dee5a1e8d2085f701398b184cd4541c4c23baf7f3ae451ee3d8eab4251", size = 51071, upload-time = "2025-07-18T14:52:44.376Z" }, -] - -[[package]] -name = "markdown-it-py" -version = "4.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mdurl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z" }, -] - -[[package]] -name = "markupsafe" -version = "3.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", size = 11631, upload-time = "2025-09-27T18:36:05.558Z" }, - { url = "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", size = 12057, upload-time = "2025-09-27T18:36:07.165Z" }, - { url = "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", size = 22050, upload-time = "2025-09-27T18:36:08.005Z" }, - { url = "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", size = 20681, upload-time = "2025-09-27T18:36:08.881Z" }, - { url = "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", size = 20705, upload-time = "2025-09-27T18:36:10.131Z" }, - { url = "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", size = 21524, upload-time = "2025-09-27T18:36:11.324Z" }, - { url = "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", size = 20282, upload-time = "2025-09-27T18:36:12.573Z" }, - { url = "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", size = 20745, upload-time = "2025-09-27T18:36:13.504Z" }, - { url = "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", size = 14571, upload-time = "2025-09-27T18:36:14.779Z" }, - { url = "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", size = 15056, upload-time = "2025-09-27T18:36:16.125Z" }, - { url = "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", size = 13932, upload-time = "2025-09-27T18:36:17.311Z" }, - { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" }, - { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" }, - { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" }, - { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" }, - { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" }, - { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" }, - { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" }, - { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" }, - { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload-time = "2025-09-27T18:36:28.045Z" }, - { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload-time = "2025-09-27T18:36:29.025Z" }, - { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload-time = "2025-09-27T18:36:29.954Z" }, - { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, - { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, - { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, - { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, - { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, - { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, - { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, - { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, - { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, - { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, - { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, - { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, - { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, - { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, - { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, - { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, - { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, - { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, - { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, - { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, - { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, - { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, - { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, - { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, - { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, - { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, - { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, - { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, - { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, - { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, - { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, - { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, - { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, - { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, - { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, - { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, - { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, - { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, - { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, - { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, - { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, - { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, - { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, - { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, - { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, - { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, - { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, - { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, - { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, - { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, - { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, - { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, - { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, -] - -[[package]] -name = "matplotlib" -version = "3.10.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "contourpy", version = "1.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "contourpy", version = "1.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "cycler" }, - { name = "fonttools" }, - { name = "kiwisolver" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "packaging" }, - { name = "pillow" }, - { name = "pyparsing" }, - { name = "python-dateutil" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/63/1b/4be5be87d43d327a0cf4de1a56e86f7f84c89312452406cf122efe2839e6/matplotlib-3.10.9.tar.gz", hash = "sha256:fd66508e8c6877d98e586654b608a0456db8d7e8a546eb1e2600efd957302358", size = 34811233, upload-time = "2026-04-24T00:14:13.539Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/6f/340b04986e67aac6f66c5145ce68bf72c64bed30f92c8913499a6e6b8f99/matplotlib-3.10.9-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77210dce9cb8153dffc967efaae990543392563d5a376d4dd8539bebcb0ed217", size = 8296625, upload-time = "2026-04-24T00:11:43.376Z" }, - { url = "https://files.pythonhosted.org/packages/bb/2f/127081eb83162053ebb9678ceac64220b93a663e0167432566e9c7c82aab/matplotlib-3.10.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1e7698ac9868428e84d2c967424803b2472ff7167d9d6590d4204ed775343c3b", size = 8188790, upload-time = "2026-04-24T00:11:46.556Z" }, - { url = "https://files.pythonhosted.org/packages/fc/b7/d8bcec2626c35f96972bff656299fef4578113ea6193c8fdad324710410c/matplotlib-3.10.9-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1aa972116abb4c9d201bf245620b433726cb6856f3bef6a78f776a00f5c92d37", size = 8769389, upload-time = "2026-04-24T00:11:48.959Z" }, - { url = "https://files.pythonhosted.org/packages/12/49/b78e214a527ea732033b7f4d37f7afb504d74ba9d134bd47938230dfb8b1/matplotlib-3.10.9-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae2f11957b27ce53497dd4d7b235c4d4f1faf383dfb39d0c5beb833bff883294", size = 9589657, upload-time = "2026-04-24T00:11:51.915Z" }, - { url = "https://files.pythonhosted.org/packages/5f/15/5246f7b43beae19c74dfee651d58d6cc8112e06f77adb4e88cc04f2e3a23/matplotlib-3.10.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b049278ddce116aaa1c1377ebf58adea909132dfce0281cf7e3a1ea9fc2e2c65", size = 9651983, upload-time = "2026-04-24T00:11:54.766Z" }, - { url = "https://files.pythonhosted.org/packages/75/77/5acecfe672ba0fa1b8c0454f69ce155d1e6fc5852fa7206bf9afaf767121/matplotlib-3.10.9-cp310-cp310-win_amd64.whl", hash = "sha256:82834c3c292d24d3a8aae77cd2d20019de69d692a34a970e4fdb8d33e2ea3dda", size = 8199701, upload-time = "2026-04-24T00:11:58.389Z" }, - { url = "https://files.pythonhosted.org/packages/4c/8c/290f021104741fea63769c31494f5324c0cd249bf536a65a4350767b1f22/matplotlib-3.10.9-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:68cfdcede415f7c8f5577b03303dd94526cdb6d11036cecdc205e08733b2d2bb", size = 8306860, upload-time = "2026-04-24T00:12:01.207Z" }, - { url = "https://files.pythonhosted.org/packages/51/18/325cd32ece1120d1da51cc4e4294c6580190699490183fc2fe8cb6d61ec5/matplotlib-3.10.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfca0129678bd56379db26c52b5d77ed7de314c047492fbdc763aa7501710cfb", size = 8199254, upload-time = "2026-04-24T00:12:04.239Z" }, - { url = "https://files.pythonhosted.org/packages/79/db/e28c1b83e3680740aa78925f5fb2ae4d16207207419ad75ea9fe604f8676/matplotlib-3.10.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8e436d155fa8a3399dc62683f8f5d0e2e50d25d0144a73edd73f82eec8f4abfb", size = 8777092, upload-time = "2026-04-24T00:12:06.793Z" }, - { url = "https://files.pythonhosted.org/packages/55/fa/3ce7adfe9ba101748f465211660d9c6374c876b671bdb8c2bb6d347e8b94/matplotlib-3.10.9-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56fc0bd271b00025c6edfdc7c2dcd247372c8e1544971d62e1dc7c17367e8bf9", size = 9595691, upload-time = "2026-04-24T00:12:09.706Z" }, - { url = "https://files.pythonhosted.org/packages/36/c4/6960a76686ed668f2c60f84e9799ba4c0d56abdb36b1577b60c1d061d1ec/matplotlib-3.10.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5a6104ed666402ba5106d7f36e0e0cdca4e8d7fa4d39708ca88019e2835a2eb", size = 9659771, upload-time = "2026-04-24T00:12:12.766Z" }, - { url = "https://files.pythonhosted.org/packages/7e/0d/271aace3342157c64700c9ff4c59c7b392f3dbab393692e8db6fbe7ab96c/matplotlib-3.10.9-cp311-cp311-win_amd64.whl", hash = "sha256:d730e984eddf56974c3e72b6129c7ca462ac38dc624338f4b0b23eb23ecba00f", size = 8205112, upload-time = "2026-04-24T00:12:15.773Z" }, - { url = "https://files.pythonhosted.org/packages/e2/ee/cb57ad4754f3e7b9174ce6ce66d9205fb827067e48a9f58ac09d7e7d6b77/matplotlib-3.10.9-cp311-cp311-win_arm64.whl", hash = "sha256:51bf0ddbdc598e060d46c16b5590708f81a1624cefbaaf62f6a81bf9285b8c80", size = 8132310, upload-time = "2026-04-24T00:12:18.645Z" }, - { url = "https://files.pythonhosted.org/packages/35/c6/5581e26c72233ebb2a2a6fed2d24fb7c66b4700120b813f51b0555acf0b6/matplotlib-3.10.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f0c3c28d9fbcc1fe7a03be236d73430cf6409c41fb2383a7ac52fe932b072cb1", size = 8319908, upload-time = "2026-04-24T00:12:21.323Z" }, - { url = "https://files.pythonhosted.org/packages/b7/18/4880dd762e40cd360c1bf06e890c5a97b997e91cb324602b1a19950ad5ce/matplotlib-3.10.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41cb28c2bd769aa3e98322c6ab09854cbcc52ab69d2759d681bba3e327b2b320", size = 8216016, upload-time = "2026-04-24T00:12:23.4Z" }, - { url = "https://files.pythonhosted.org/packages/32/91/d024616abdba99e83120e07a20658976f6a343646710760c4a51df126029/matplotlib-3.10.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ae20801130378b82d647ff5047c07316295b68dc054ca6b3c13519d0ea624285", size = 8789336, upload-time = "2026-04-24T00:12:26.096Z" }, - { url = "https://files.pythonhosted.org/packages/5c/04/030a2f61ef2158f5e4c259487a92ac877732499fb33d871585d89e03c42d/matplotlib-3.10.9-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c63ebcd8b4b169eb2f5c200552ae6b8be8999a005b6b507ed76fb8d7d674fe2", size = 9604602, upload-time = "2026-04-24T00:12:29.052Z" }, - { url = "https://files.pythonhosted.org/packages/fc/c2/541e4d09d87bb6b5830fc28b4c887a9a8cf4e1c6cee698a8c05552ae2003/matplotlib-3.10.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d75d11c949914165976c621b2324f9ef162af7ebf4b057ddf95dd1dba7e5edcf", size = 9670966, upload-time = "2026-04-24T00:12:32.131Z" }, - { url = "https://files.pythonhosted.org/packages/04/a1/4571fc46e7702de8d0c2dc54ad1b2f8e29328dea3ee90831181f7353d93c/matplotlib-3.10.9-cp312-cp312-win_amd64.whl", hash = "sha256:d091f9d758b34aaaaa6331d13574bf01891d903b3dec59bfff458ef7551de5d6", size = 8217462, upload-time = "2026-04-24T00:12:35.226Z" }, - { url = "https://files.pythonhosted.org/packages/4b/d0/2269edb12aa30c13c8bcc9382892e39943ce1d28aab4ec296e0381798e81/matplotlib-3.10.9-cp312-cp312-win_arm64.whl", hash = "sha256:10cc5ce06d10231c36f40e875f3c7e8050362a4ee8f0ee5d29a6b3277d57bb42", size = 8136688, upload-time = "2026-04-24T00:12:37.442Z" }, - { url = "https://files.pythonhosted.org/packages/aa/d3/8d4f6afbecb49fc04e060a57c0fce39ea51cc163a6bd87303ccd698e4fa6/matplotlib-3.10.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b580440f1ff81a0e34122051a3dfabb7e4b7f9e380629929bde0eff9af72165f", size = 8320331, upload-time = "2026-04-24T00:12:39.688Z" }, - { url = "https://files.pythonhosted.org/packages/63/d9/9e14bc7564bf92d5ffa801ae5fac819ce74b925dfb55e3ebde61a3bbad3e/matplotlib-3.10.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b1b745c489cd1a77a0dc1120a05dc87af9798faebc913601feb8c73d89bf2d1e", size = 8216461, upload-time = "2026-04-24T00:12:42.494Z" }, - { url = "https://files.pythonhosted.org/packages/8a/17/4402d0d14ccf1dfc70932600b68097fbbf9c898a4871d2cbbe79c7801a32/matplotlib-3.10.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8f3bcac1ca5ed000a6f4337d47ba67dfddf37ed6a46c15fd7f014997f7bf865f", size = 8790091, upload-time = "2026-04-24T00:12:44.789Z" }, - { url = "https://files.pythonhosted.org/packages/3e/0b/322aeec06dd9b91411f92028b37d447342770a24392aa4813e317064dad5/matplotlib-3.10.9-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a8d66a55def891c33147ba3ba9bfcabf0b526a43764c818acbb4525e5ed0838", size = 9605027, upload-time = "2026-04-24T00:12:47.583Z" }, - { url = "https://files.pythonhosted.org/packages/74/88/5f13482f55e7b00bcfc09838b093c2456e1379978d2a146844aae05350ad/matplotlib-3.10.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d843374407c4017a6403b59c6c81606773d136f3259d5b6da3131bc814542cc2", size = 9671269, upload-time = "2026-04-24T00:12:50.878Z" }, - { url = "https://files.pythonhosted.org/packages/c5/e0/0840fd2f93da988ec660b8ad1984abe9f25d2aed22a5e394ff1c68c88307/matplotlib-3.10.9-cp313-cp313-win_amd64.whl", hash = "sha256:f4399f64b3e94cd500195490972ae1ee81170df1636fa15364d157d5bdd7b921", size = 8217588, upload-time = "2026-04-24T00:12:53.784Z" }, - { url = "https://files.pythonhosted.org/packages/47/b9/d706d06dd605c49b9f83a2aed8c13e3e5db70697d7a80b7e3d7915de6b17/matplotlib-3.10.9-cp313-cp313-win_arm64.whl", hash = "sha256:ba7b3b8ef09eab7df0e86e9ae086faa433efbfbdb46afcb3aa16aabf779469a8", size = 8136913, upload-time = "2026-04-24T00:12:56.501Z" }, - { url = "https://files.pythonhosted.org/packages/9b/45/6e32d96978264c8ca8c4b1010adb955a1a49cfaf314e212bbc8908f04a61/matplotlib-3.10.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:09218df8a93712bd6ea133e83a153c755448cf7868316c531cffcc43f69d1cc9", size = 8368019, upload-time = "2026-04-24T00:12:58.896Z" }, - { url = "https://files.pythonhosted.org/packages/86/0a/c8e3d3bba245f0f7fc424937f8ff7ef77291a36af3edb97ccd78aa93d84f/matplotlib-3.10.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:82368699727bfb7b0182e1aa13082e3c08e092fa1a25d3e1fd92405bff96f6d4", size = 8264645, upload-time = "2026-04-24T00:13:01.406Z" }, - { url = "https://files.pythonhosted.org/packages/3d/aa/5bf5a14fe4fed73a4209a155606f8096ff797aad89c6c35179026571133e/matplotlib-3.10.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3225f4e1edcb8c86c884ddf79ebe20ecd0a67d30188f279897554ccd8fded4dc", size = 8802194, upload-time = "2026-04-24T00:13:03.702Z" }, - { url = "https://files.pythonhosted.org/packages/dd/5e/b4be852d6bba6fd15893fadf91ff26ae49cb91aac789e95dde9d342e664f/matplotlib-3.10.9-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de2445a0c6690d21b7eb6ce071cebad6d40a2e9bdf10d039074a96ba19797b99", size = 9622684, upload-time = "2026-04-24T00:13:06.647Z" }, - { url = "https://files.pythonhosted.org/packages/4c/3d/ed428c971139112ef730f62770654d609467346d09d4b62617e1afd68a5a/matplotlib-3.10.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:b2b9516251cb89ff618d757daec0e2ed1bf21248013844a853d87ef85ab3081d", size = 9680790, upload-time = "2026-04-24T00:13:10.009Z" }, - { url = "https://files.pythonhosted.org/packages/e7/09/052e884aaf2b985c63cb79f715f1d5b6a3eaa7de78f6a52b9dbc077d5b53/matplotlib-3.10.9-cp313-cp313t-win_amd64.whl", hash = "sha256:e9fae004b941b23ff2edcf1567a857ed77bafc8086ffa258190462328434faf8", size = 8287571, upload-time = "2026-04-24T00:13:13.087Z" }, - { url = "https://files.pythonhosted.org/packages/f4/38/ae27288e788c35a4250491422f3db7750366fc8c97d6f36fbdecfc1f5518/matplotlib-3.10.9-cp313-cp313t-win_arm64.whl", hash = "sha256:6b63d9c7c769b88ab81e10dc86e4e0607cf56817b9f9e6cf24b2a5f1693b8e38", size = 8188292, upload-time = "2026-04-24T00:13:15.546Z" }, - { url = "https://files.pythonhosted.org/packages/d6/e6/3bd8afd04949f02eabc1c17115ea5255e19cacd4d06fc5abdde4eeb0052c/matplotlib-3.10.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:172db52c9e683f5d12eaf57f0f54834190e12581fe1cc2a19595a8f5acb4e77d", size = 8321276, upload-time = "2026-04-24T00:13:18.318Z" }, - { url = "https://files.pythonhosted.org/packages/41/86/86231232fff41c9f8e4a1a7d7a597d349a02527109c3af7d618366122139/matplotlib-3.10.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:97e35e8d39ccc85859095e01a53847432ba9a53ddf7986f7a54a11b73d0e143f", size = 8218218, upload-time = "2026-04-24T00:13:20.974Z" }, - { url = "https://files.pythonhosted.org/packages/85/8f/becc9722cafc64f5d2eb0b7c1bf5f585271c618a45dbd8fabeb021f898b6/matplotlib-3.10.9-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aba1615dabe83188e19d4f75a253c6a08423e04c1425e64039f800050a69de6b", size = 9608145, upload-time = "2026-04-24T00:13:23.228Z" }, - { url = "https://files.pythonhosted.org/packages/32/5d/f7e914f7d9325abff4057cee62c0fa70263683189f774473cbfb534cd13b/matplotlib-3.10.9-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34cf8167e023ad956c15f36302911d5406bd99a9862c1a8499ea6f7c0e015dc2", size = 9885085, upload-time = "2026-04-24T00:13:25.849Z" }, - { url = "https://files.pythonhosted.org/packages/a5/fd/fa69f2221534e80cc5772ac2b7d222011a2acafc2ec7216d5dd174c864ae/matplotlib-3.10.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:59476c6d29d612b8e9bb6ce8c5b631be6ba8f9e3a2421f22a02b192c7dd28716", size = 9672358, upload-time = "2026-04-24T00:13:28.906Z" }, - { url = "https://files.pythonhosted.org/packages/ab/1a/5a4f747a8b271cbb024946d2dd3c913ab5032ba430626f8c3528ada96b4b/matplotlib-3.10.9-cp314-cp314-win_amd64.whl", hash = "sha256:336b9acc64d309063126edcdaca00db9373af3c476bb94388fe9c5a53ad13e6f", size = 8349970, upload-time = "2026-04-24T00:13:31.904Z" }, - { url = "https://files.pythonhosted.org/packages/64/dc/95d60ecaefe30680a154b52ea96ab4b0dab547f1fd6aa12f5fb655e89cae/matplotlib-3.10.9-cp314-cp314-win_arm64.whl", hash = "sha256:2dc9477819ffd78ad12a20df1d9d6a6bd4fec6aaa9072681465fddca052f1456", size = 8272785, upload-time = "2026-04-24T00:13:34.511Z" }, - { url = "https://files.pythonhosted.org/packages/70/a0/005d68bc8b8418300ce6591f18586910a8526806e2ab663933d9f20a41e9/matplotlib-3.10.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:da4e09638420548f31c354032a6250e473c68e5a4e96899b4844cf39ddea23fe", size = 8367999, upload-time = "2026-04-24T00:13:36.962Z" }, - { url = "https://files.pythonhosted.org/packages/22/05/1236cc9290be70b2498af20ca348add76e3fffe7f67b477db5133a84f3ea/matplotlib-3.10.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:345f6f68ecc8da0ca56fad2ea08fde1a115eda530079eca185d50a7bc3e146c6", size = 8264543, upload-time = "2026-04-24T00:13:39.851Z" }, - { url = "https://files.pythonhosted.org/packages/cd/c2/071f5a5ff6c5bd63aaaf2f45c811d9bf2ced94bde188d9e1a519e21d0cba/matplotlib-3.10.9-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4edcfbd8565339aa62f1cd4012f7180926fdbe71850f7b0d3c379c175cd6b66c", size = 9622800, upload-time = "2026-04-24T00:13:42.296Z" }, - { url = "https://files.pythonhosted.org/packages/95/57/da7d1f10a85624b9e7db68e069dd94e58dc41dbf9463c5921632ecbe3661/matplotlib-3.10.9-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6be157fe17fc37cb95ac1d7374cf717ce9259616edec911a78d9d26dae8522d4", size = 9888561, upload-time = "2026-04-24T00:13:45.026Z" }, - { url = "https://files.pythonhosted.org/packages/67/b2/ef8d6bb59b0edb6c16c968b70f548aa13b54348972def5aa6ac85df67145/matplotlib-3.10.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4e42042d54db34fda4e95a7bd3e5789c2a995d2dad3eb8850232ee534092fbbf", size = 9680884, upload-time = "2026-04-24T00:13:48.066Z" }, - { url = "https://files.pythonhosted.org/packages/61/1c/d21bfeb9931881ebe96bcfcff27c7ae4b160ae0ec291a714c42641a56d75/matplotlib-3.10.9-cp314-cp314t-win_amd64.whl", hash = "sha256:c27df8b3848f32a83d1767566595e43cfaa4460380974da06f4279a7ec143c39", size = 8432333, upload-time = "2026-04-24T00:13:51.008Z" }, - { url = "https://files.pythonhosted.org/packages/78/23/92493c3e6e1b635ccfff146f7b99e674808787915420373ac399283764c2/matplotlib-3.10.9-cp314-cp314t-win_arm64.whl", hash = "sha256:a49f1eadc84ca85fd72fa4e89e70e61bf86452df6f971af04b12c60761a0772c", size = 8324785, upload-time = "2026-04-24T00:13:53.633Z" }, - { url = "https://files.pythonhosted.org/packages/2c/2b/0e92ad0ac446633f928a1563db4aa8add407e1924faf0ded5b95b35afb27/matplotlib-3.10.9-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1872fb212a05b729e649754a72d5da61d03e0554d76e80303b6f83d1d2c0552b", size = 8293058, upload-time = "2026-04-24T00:13:56.339Z" }, - { url = "https://files.pythonhosted.org/packages/4b/23/74682fd369f5299ceda438fea2a0662e6383b85c9383fb9cdfcf04713e07/matplotlib-3.10.9-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:985f2238880e2e69093f588f5fe2e46771747febf0649f3cf7f7b7480875317f", size = 8186627, upload-time = "2026-04-24T00:13:58.623Z" }, - { url = "https://files.pythonhosted.org/packages/ca/e8/368aab88f3c4cd8992800f31abfe0670c3e47540ba20a97e9fdbcde594b3/matplotlib-3.10.9-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6640f75af2c6148293caa0a2b39dd806a492dd66c8a8b04035813e33d0fd2585", size = 8764117, upload-time = "2026-04-24T00:14:01.684Z" }, - { url = "https://files.pythonhosted.org/packages/63/e2/9f66ca6a651a52abfe0d4964ce01439ed34f3f1e119de10ff3a07f403043/matplotlib-3.10.9-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:42fb814efabe95c06c1994d8ab5a8385f43a249e23badd3ba931d4308e5bca20", size = 8304420, upload-time = "2026-04-24T00:14:04.57Z" }, - { url = "https://files.pythonhosted.org/packages/e8/e8/467c03568218792906aa87b5e7bb379b605e056ed0c74fe00c051786d925/matplotlib-3.10.9-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f76e640a5268850bfda54b5131b1b1941cc685e42c5fa98ed9f2d64038308cba", size = 8197981, upload-time = "2026-04-24T00:14:07.233Z" }, - { url = "https://files.pythonhosted.org/packages/6f/87/afead29192170917537934c6aff4b008c805fff7b1ccea0c79120d96beda/matplotlib-3.10.9-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3fc0364dfbe1d07f6d15c5ebd0c5bf89e126916e5a8667dd4a7a6e84c36653d4", size = 8774002, upload-time = "2026-04-24T00:14:09.816Z" }, -] - -[[package]] -name = "mdurl" -version = "0.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, -] - -[[package]] -name = "ml-dtypes" -version = "0.5.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0e/4a/c27b42ed9b1c7d13d9ba8b6905dece787d6259152f2309338aed29b2447b/ml_dtypes-0.5.4.tar.gz", hash = "sha256:8ab06a50fb9bf9666dd0fe5dfb4676fa2b0ac0f31ecff72a6c3af8e22c063453", size = 692314, upload-time = "2025-11-17T22:32:31.031Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/3a/c5b855752a70267ff729c349e650263adb3c206c29d28cc8ea7ace30a1d5/ml_dtypes-0.5.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b95e97e470fe60ed493fd9ae3911d8da4ebac16bd21f87ffa2b7c588bf22ea2c", size = 679735, upload-time = "2025-11-17T22:31:31.367Z" }, - { url = "https://files.pythonhosted.org/packages/41/79/7433f30ee04bd4faa303844048f55e1eb939131c8e5195a00a96a0939b64/ml_dtypes-0.5.4-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4b801ebe0b477be666696bda493a9be8356f1f0057a57f1e35cd26928823e5a", size = 5051883, upload-time = "2025-11-17T22:31:33.658Z" }, - { url = "https://files.pythonhosted.org/packages/10/b1/8938e8830b0ee2e167fc75a094dea766a1152bde46752cd9bfc57ee78a82/ml_dtypes-0.5.4-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:388d399a2152dd79a3f0456a952284a99ee5c93d3e2f8dfe25977511e0515270", size = 5030369, upload-time = "2025-11-17T22:31:35.595Z" }, - { url = "https://files.pythonhosted.org/packages/c7/a3/51886727bd16e2f47587997b802dd56398692ce8c6c03c2e5bb32ecafe26/ml_dtypes-0.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:4ff7f3e7ca2972e7de850e7b8fcbb355304271e2933dd90814c1cb847414d6e2", size = 210738, upload-time = "2025-11-17T22:31:37.43Z" }, - { url = "https://files.pythonhosted.org/packages/c6/5e/712092cfe7e5eb667b8ad9ca7c54442f21ed7ca8979745f1000e24cf8737/ml_dtypes-0.5.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6c7ecb74c4bd71db68a6bea1edf8da8c34f3d9fe218f038814fd1d310ac76c90", size = 679734, upload-time = "2025-11-17T22:31:39.223Z" }, - { url = "https://files.pythonhosted.org/packages/4f/cf/912146dfd4b5c0eea956836c01dcd2fce6c9c844b2691f5152aca196ce4f/ml_dtypes-0.5.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc11d7e8c44a65115d05e2ab9989d1e045125d7be8e05a071a48bc76eb6d6040", size = 5056165, upload-time = "2025-11-17T22:31:41.071Z" }, - { url = "https://files.pythonhosted.org/packages/a9/80/19189ea605017473660e43762dc853d2797984b3c7bf30ce656099add30c/ml_dtypes-0.5.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:19b9a53598f21e453ea2fbda8aa783c20faff8e1eeb0d7ab899309a0053f1483", size = 5034975, upload-time = "2025-11-17T22:31:42.758Z" }, - { url = "https://files.pythonhosted.org/packages/b4/24/70bd59276883fdd91600ca20040b41efd4902a923283c4d6edcb1de128d2/ml_dtypes-0.5.4-cp311-cp311-win_amd64.whl", hash = "sha256:7c23c54a00ae43edf48d44066a7ec31e05fdc2eee0be2b8b50dd1903a1db94bb", size = 210742, upload-time = "2025-11-17T22:31:44.068Z" }, - { url = "https://files.pythonhosted.org/packages/a0/c9/64230ef14e40aa3f1cb254ef623bf812735e6bec7772848d19131111ac0d/ml_dtypes-0.5.4-cp311-cp311-win_arm64.whl", hash = "sha256:557a31a390b7e9439056644cb80ed0735a6e3e3bb09d67fd5687e4b04238d1de", size = 160709, upload-time = "2025-11-17T22:31:46.557Z" }, - { url = "https://files.pythonhosted.org/packages/a8/b8/3c70881695e056f8a32f8b941126cf78775d9a4d7feba8abcb52cb7b04f2/ml_dtypes-0.5.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a174837a64f5b16cab6f368171a1a03a27936b31699d167684073ff1c4237dac", size = 676927, upload-time = "2025-11-17T22:31:48.182Z" }, - { url = "https://files.pythonhosted.org/packages/54/0f/428ef6881782e5ebb7eca459689448c0394fa0a80bea3aa9262cba5445ea/ml_dtypes-0.5.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a7f7c643e8b1320fd958bf098aa7ecf70623a42ec5154e3be3be673f4c34d900", size = 5028464, upload-time = "2025-11-17T22:31:50.135Z" }, - { url = "https://files.pythonhosted.org/packages/3a/cb/28ce52eb94390dda42599c98ea0204d74799e4d8047a0eb559b6fd648056/ml_dtypes-0.5.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ad459e99793fa6e13bd5b7e6792c8f9190b4e5a1b45c63aba14a4d0a7f1d5ff", size = 5009002, upload-time = "2025-11-17T22:31:52.001Z" }, - { url = "https://files.pythonhosted.org/packages/f5/f0/0cfadd537c5470378b1b32bd859cf2824972174b51b873c9d95cfd7475a5/ml_dtypes-0.5.4-cp312-cp312-win_amd64.whl", hash = "sha256:c1a953995cccb9e25a4ae19e34316671e4e2edaebe4cf538229b1fc7109087b7", size = 212222, upload-time = "2025-11-17T22:31:53.742Z" }, - { url = "https://files.pythonhosted.org/packages/16/2e/9acc86985bfad8f2c2d30291b27cd2bb4c74cea08695bd540906ed744249/ml_dtypes-0.5.4-cp312-cp312-win_arm64.whl", hash = "sha256:9bad06436568442575beb2d03389aa7456c690a5b05892c471215bfd8cf39460", size = 160793, upload-time = "2025-11-17T22:31:55.358Z" }, - { url = "https://files.pythonhosted.org/packages/d9/a1/4008f14bbc616cfb1ac5b39ea485f9c63031c4634ab3f4cf72e7541f816a/ml_dtypes-0.5.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c760d85a2f82e2bed75867079188c9d18dae2ee77c25a54d60e9cc79be1bc48", size = 676888, upload-time = "2025-11-17T22:31:56.907Z" }, - { url = "https://files.pythonhosted.org/packages/d3/b7/dff378afc2b0d5a7d6cd9d3209b60474d9819d1189d347521e1688a60a53/ml_dtypes-0.5.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce756d3a10d0c4067172804c9cc276ba9cc0ff47af9078ad439b075d1abdc29b", size = 5036993, upload-time = "2025-11-17T22:31:58.497Z" }, - { url = "https://files.pythonhosted.org/packages/eb/33/40cd74219417e78b97c47802037cf2d87b91973e18bb968a7da48a96ea44/ml_dtypes-0.5.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:533ce891ba774eabf607172254f2e7260ba5f57bdd64030c9a4fcfbd99815d0d", size = 5010956, upload-time = "2025-11-17T22:31:59.931Z" }, - { url = "https://files.pythonhosted.org/packages/e1/8b/200088c6859d8221454825959df35b5244fa9bdf263fd0249ac5fb75e281/ml_dtypes-0.5.4-cp313-cp313-win_amd64.whl", hash = "sha256:f21c9219ef48ca5ee78402d5cc831bd58ea27ce89beda894428bc67a52da5328", size = 212224, upload-time = "2025-11-17T22:32:01.349Z" }, - { url = "https://files.pythonhosted.org/packages/8f/75/dfc3775cb36367816e678f69a7843f6f03bd4e2bcd79941e01ea960a068e/ml_dtypes-0.5.4-cp313-cp313-win_arm64.whl", hash = "sha256:35f29491a3e478407f7047b8a4834e4640a77d2737e0b294d049746507af5175", size = 160798, upload-time = "2025-11-17T22:32:02.864Z" }, - { url = "https://files.pythonhosted.org/packages/4f/74/e9ddb35fd1dd43b1106c20ced3f53c2e8e7fc7598c15638e9f80677f81d4/ml_dtypes-0.5.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:304ad47faa395415b9ccbcc06a0350800bc50eda70f0e45326796e27c62f18b6", size = 702083, upload-time = "2025-11-17T22:32:04.08Z" }, - { url = "https://files.pythonhosted.org/packages/74/f5/667060b0aed1aa63166b22897fdf16dca9eb704e6b4bbf86848d5a181aa7/ml_dtypes-0.5.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6a0df4223b514d799b8a1629c65ddc351b3efa833ccf7f8ea0cf654a61d1e35d", size = 5354111, upload-time = "2025-11-17T22:32:05.546Z" }, - { url = "https://files.pythonhosted.org/packages/40/49/0f8c498a28c0efa5f5c95a9e374c83ec1385ca41d0e85e7cf40e5d519a21/ml_dtypes-0.5.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:531eff30e4d368cb6255bc2328d070e35836aa4f282a0fb5f3a0cd7260257298", size = 5366453, upload-time = "2025-11-17T22:32:07.115Z" }, - { url = "https://files.pythonhosted.org/packages/8c/27/12607423d0a9c6bbbcc780ad19f1f6baa2b68b18ce4bddcdc122c4c68dc9/ml_dtypes-0.5.4-cp313-cp313t-win_amd64.whl", hash = "sha256:cb73dccfc991691c444acc8c0012bee8f2470da826a92e3a20bb333b1a7894e6", size = 225612, upload-time = "2025-11-17T22:32:08.615Z" }, - { url = "https://files.pythonhosted.org/packages/e5/80/5a5929e92c72936d5b19872c5fb8fc09327c1da67b3b68c6a13139e77e20/ml_dtypes-0.5.4-cp313-cp313t-win_arm64.whl", hash = "sha256:3bbbe120b915090d9dd1375e4684dd17a20a2491ef25d640a908281da85e73f1", size = 164145, upload-time = "2025-11-17T22:32:09.782Z" }, - { url = "https://files.pythonhosted.org/packages/72/4e/1339dc6e2557a344f5ba5590872e80346f76f6cb2ac3dd16e4666e88818c/ml_dtypes-0.5.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:2b857d3af6ac0d39db1de7c706e69c7f9791627209c3d6dedbfca8c7e5faec22", size = 673781, upload-time = "2025-11-17T22:32:11.364Z" }, - { url = "https://files.pythonhosted.org/packages/04/f9/067b84365c7e83bda15bba2b06c6ca250ce27b20630b1128c435fb7a09aa/ml_dtypes-0.5.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:805cef3a38f4eafae3a5bf9ebdcdb741d0bcfd9e1bd90eb54abd24f928cd2465", size = 5036145, upload-time = "2025-11-17T22:32:12.783Z" }, - { url = "https://files.pythonhosted.org/packages/c6/bb/82c7dcf38070b46172a517e2334e665c5bf374a262f99a283ea454bece7c/ml_dtypes-0.5.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14a4fd3228af936461db66faccef6e4f41c1d82fcc30e9f8d58a08916b1d811f", size = 5010230, upload-time = "2025-11-17T22:32:14.38Z" }, - { url = "https://files.pythonhosted.org/packages/e9/93/2bfed22d2498c468f6bcd0d9f56b033eaa19f33320389314c19ef6766413/ml_dtypes-0.5.4-cp314-cp314-win_amd64.whl", hash = "sha256:8c6a2dcebd6f3903e05d51960a8058d6e131fe69f952a5397e5dbabc841b6d56", size = 221032, upload-time = "2025-11-17T22:32:15.763Z" }, - { url = "https://files.pythonhosted.org/packages/76/a3/9c912fe6ea747bb10fe2f8f54d027eb265db05dfb0c6335e3e063e74e6e8/ml_dtypes-0.5.4-cp314-cp314-win_arm64.whl", hash = "sha256:5a0f68ca8fd8d16583dfa7793973feb86f2fbb56ce3966daf9c9f748f52a2049", size = 163353, upload-time = "2025-11-17T22:32:16.932Z" }, - { url = "https://files.pythonhosted.org/packages/cd/02/48aa7d84cc30ab4ee37624a2fd98c56c02326785750cd212bc0826c2f15b/ml_dtypes-0.5.4-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:bfc534409c5d4b0bf945af29e5d0ab075eae9eecbb549ff8a29280db822f34f9", size = 702085, upload-time = "2025-11-17T22:32:18.175Z" }, - { url = "https://files.pythonhosted.org/packages/5a/e7/85cb99fe80a7a5513253ec7faa88a65306be071163485e9a626fce1b6e84/ml_dtypes-0.5.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2314892cdc3fcf05e373d76d72aaa15fda9fb98625effa73c1d646f331fcecb7", size = 5355358, upload-time = "2025-11-17T22:32:19.7Z" }, - { url = "https://files.pythonhosted.org/packages/79/2b/a826ba18d2179a56e144aef69e57fb2ab7c464ef0b2111940ee8a3a223a2/ml_dtypes-0.5.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d2ffd05a2575b1519dc928c0b93c06339eb67173ff53acb00724502cda231cf", size = 5366332, upload-time = "2025-11-17T22:32:21.193Z" }, - { url = "https://files.pythonhosted.org/packages/84/44/f4d18446eacb20ea11e82f133ea8f86e2bf2891785b67d9da8d0ab0ef525/ml_dtypes-0.5.4-cp314-cp314t-win_amd64.whl", hash = "sha256:4381fe2f2452a2d7589689693d3162e876b3ddb0a832cde7a414f8e1adf7eab1", size = 236612, upload-time = "2025-11-17T22:32:22.579Z" }, - { url = "https://files.pythonhosted.org/packages/ad/3f/3d42e9a78fe5edf792a83c074b13b9b770092a4fbf3462872f4303135f09/ml_dtypes-0.5.4-cp314-cp314t-win_arm64.whl", hash = "sha256:11942cbf2cf92157db91e5022633c0d9474d4dfd813a909383bd23ce828a4b7d", size = 168825, upload-time = "2025-11-17T22:32:23.766Z" }, -] - -[[package]] -name = "mpmath" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, -] - -[[package]] -name = "narwhals" -version = "2.21.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2d/0e/3ad61eb87088cc4932e0d851531fa82f845a6230b68b091a0e298cc7e537/narwhals-2.21.0.tar.gz", hash = "sha256:7c6e7f50528e62b7a967dd864d7e117d2955d38d4f730653ce46a9861358e2dc", size = 633083, upload-time = "2026-05-08T12:29:02.587Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/e1/68c2256b69a314eba133673377ba9118c356f6342a0c02b61de449cf2bf2/narwhals-2.21.0-py3-none-any.whl", hash = "sha256:1e6617d0fca68ae1fda29e5397c4eaacd3ffc9fffe6bcd6ded0c690475e853be", size = 451943, upload-time = "2026-05-08T12:29:01.058Z" }, -] - -[[package]] -name = "networkx" -version = "3.4.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368, upload-time = "2024-10-21T12:39:38.695Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263, upload-time = "2024-10-21T12:39:36.247Z" }, -] - -[[package]] -name = "networkx" -version = "3.6.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'win32'", - "python_full_version == '3.12.*' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version == '3.12.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", -] -sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025, upload-time = "2025-12-08T17:02:39.908Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504, upload-time = "2025-12-08T17:02:38.159Z" }, -] - -[[package]] -name = "numpy" -version = "2.2.6" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" }, - { url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" }, - { url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" }, - { url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" }, - { url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" }, - { url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" }, - { url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" }, - { url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" }, - { url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" }, - { url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" }, - { url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" }, - { url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" }, - { url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" }, - { url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" }, - { url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" }, - { url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" }, - { url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" }, - { url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" }, - { url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866, upload-time = "2025-05-17T21:33:50.273Z" }, - { url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455, upload-time = "2025-05-17T21:34:09.135Z" }, - { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" }, - { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" }, - { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" }, - { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" }, - { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" }, - { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" }, - { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" }, - { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" }, - { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" }, - { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" }, - { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" }, - { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" }, - { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" }, - { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" }, - { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" }, - { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" }, - { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" }, - { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" }, - { url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" }, - { url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" }, - { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" }, - { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" }, - { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" }, - { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" }, - { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" }, - { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" }, - { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" }, - { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" }, - { url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" }, - { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" }, - { url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" }, - { url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" }, - { url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" }, - { url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" }, -] - -[[package]] -name = "numpy" -version = "2.4.4" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'win32'", - "python_full_version == '3.12.*' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version == '3.12.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", -] -sdist = { url = "https://files.pythonhosted.org/packages/d7/9f/b8cef5bffa569759033adda9481211426f12f53299629b410340795c2514/numpy-2.4.4.tar.gz", hash = "sha256:2d390634c5182175533585cc89f3608a4682ccb173cc9bb940b2881c8d6f8fa0", size = 20731587, upload-time = "2026-03-29T13:22:01.298Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/c6/4218570d8c8ecc9704b5157a3348e486e84ef4be0ed3e38218ab473c83d2/numpy-2.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f983334aea213c99992053ede6168500e5f086ce74fbc4acc3f2b00f5762e9db", size = 16976799, upload-time = "2026-03-29T13:18:15.438Z" }, - { url = "https://files.pythonhosted.org/packages/dd/92/b4d922c4a5f5dab9ed44e6153908a5c665b71acf183a83b93b690996e39b/numpy-2.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:72944b19f2324114e9dc86a159787333b77874143efcf89a5167ef83cfee8af0", size = 14971552, upload-time = "2026-03-29T13:18:18.606Z" }, - { url = "https://files.pythonhosted.org/packages/8a/dc/df98c095978fa6ee7b9a9387d1d58cbb3d232d0e69ad169a4ce784bde4fd/numpy-2.4.4-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:86b6f55f5a352b48d7fbfd2dbc3d5b780b2d79f4d3c121f33eb6efb22e9a2015", size = 5476566, upload-time = "2026-03-29T13:18:21.532Z" }, - { url = "https://files.pythonhosted.org/packages/28/34/b3fdcec6e725409223dd27356bdf5a3c2cc2282e428218ecc9cb7acc9763/numpy-2.4.4-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:ba1f4fc670ed79f876f70082eff4f9583c15fb9a4b89d6188412de4d18ae2f40", size = 6806482, upload-time = "2026-03-29T13:18:23.634Z" }, - { url = "https://files.pythonhosted.org/packages/68/62/63417c13aa35d57bee1337c67446761dc25ea6543130cf868eace6e8157b/numpy-2.4.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a87ec22c87be071b6bdbd27920b129b94f2fc964358ce38f3822635a3e2e03d", size = 15973376, upload-time = "2026-03-29T13:18:26.677Z" }, - { url = "https://files.pythonhosted.org/packages/cf/c5/9fcb7e0e69cef59cf10c746b84f7d58b08bc66a6b7d459783c5a4f6101a6/numpy-2.4.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df3775294accfdd75f32c74ae39fcba920c9a378a2fc18a12b6820aa8c1fb502", size = 16925137, upload-time = "2026-03-29T13:18:30.14Z" }, - { url = "https://files.pythonhosted.org/packages/7e/43/80020edacb3f84b9efdd1591120a4296462c23fd8db0dde1666f6ef66f13/numpy-2.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0d4e437e295f18ec29bc79daf55e8a47a9113df44d66f702f02a293d93a2d6dd", size = 17329414, upload-time = "2026-03-29T13:18:33.733Z" }, - { url = "https://files.pythonhosted.org/packages/fd/06/af0658593b18a5f73532d377188b964f239eb0894e664a6c12f484472f97/numpy-2.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6aa3236c78803afbcb255045fbef97a9e25a1f6c9888357d205ddc42f4d6eba5", size = 18658397, upload-time = "2026-03-29T13:18:37.511Z" }, - { url = "https://files.pythonhosted.org/packages/e6/ce/13a09ed65f5d0ce5c7dd0669250374c6e379910f97af2c08c57b0608eee4/numpy-2.4.4-cp311-cp311-win32.whl", hash = "sha256:30caa73029a225b2d40d9fae193e008e24b2026b7ee1a867b7ee8d96ca1a448e", size = 6239499, upload-time = "2026-03-29T13:18:40.372Z" }, - { url = "https://files.pythonhosted.org/packages/bd/63/05d193dbb4b5eec1eca73822d80da98b511f8328ad4ae3ca4caf0f4db91d/numpy-2.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:6bbe4eb67390b0a0265a2c25458f6b90a409d5d069f1041e6aff1e27e3d9a79e", size = 12614257, upload-time = "2026-03-29T13:18:42.95Z" }, - { url = "https://files.pythonhosted.org/packages/87/c5/8168052f080c26fa984c413305012be54741c9d0d74abd7fbeeccae3889f/numpy-2.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:fcfe2045fd2e8f3cb0ce9d4ba6dba6333b8fa05bb8a4939c908cd43322d14c7e", size = 10486775, upload-time = "2026-03-29T13:18:45.835Z" }, - { url = "https://files.pythonhosted.org/packages/28/05/32396bec30fb2263770ee910142f49c1476d08e8ad41abf8403806b520ce/numpy-2.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15716cfef24d3a9762e3acdf87e27f58dc823d1348f765bbea6bef8c639bfa1b", size = 16689272, upload-time = "2026-03-29T13:18:49.223Z" }, - { url = "https://files.pythonhosted.org/packages/c5/f3/a983d28637bfcd763a9c7aafdb6d5c0ebf3d487d1e1459ffdb57e2f01117/numpy-2.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23cbfd4c17357c81021f21540da84ee282b9c8fba38a03b7b9d09ba6b951421e", size = 14699573, upload-time = "2026-03-29T13:18:52.629Z" }, - { url = "https://files.pythonhosted.org/packages/9b/fd/e5ecca1e78c05106d98028114f5c00d3eddb41207686b2b7de3e477b0e22/numpy-2.4.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8b3b60bb7cba2c8c81837661c488637eee696f59a877788a396d33150c35d842", size = 5204782, upload-time = "2026-03-29T13:18:55.579Z" }, - { url = "https://files.pythonhosted.org/packages/de/2f/702a4594413c1a8632092beae8aba00f1d67947389369b3777aed783fdca/numpy-2.4.4-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e4a010c27ff6f210ff4c6ef34394cd61470d01014439b192ec22552ee867f2a8", size = 6552038, upload-time = "2026-03-29T13:18:57.769Z" }, - { url = "https://files.pythonhosted.org/packages/7f/37/eed308a8f56cba4d1fdf467a4fc67ef4ff4bf1c888f5fc980481890104b1/numpy-2.4.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f9e75681b59ddaa5e659898085ae0eaea229d054f2ac0c7e563a62205a700121", size = 15670666, upload-time = "2026-03-29T13:19:00.341Z" }, - { url = "https://files.pythonhosted.org/packages/0a/0d/0e3ecece05b7a7e87ab9fb587855548da437a061326fff64a223b6dcb78a/numpy-2.4.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:81f4a14bee47aec54f883e0cad2d73986640c1590eb9bfaaba7ad17394481e6e", size = 16645480, upload-time = "2026-03-29T13:19:03.63Z" }, - { url = "https://files.pythonhosted.org/packages/34/49/f2312c154b82a286758ee2f1743336d50651f8b5195db18cdb63675ff649/numpy-2.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:62d6b0f03b694173f9fcb1fb317f7222fd0b0b103e784c6549f5e53a27718c44", size = 17020036, upload-time = "2026-03-29T13:19:07.428Z" }, - { url = "https://files.pythonhosted.org/packages/7b/e9/736d17bd77f1b0ec4f9901aaec129c00d59f5d84d5e79bba540ef12c2330/numpy-2.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fbc356aae7adf9e6336d336b9c8111d390a05df88f1805573ebb0807bd06fd1d", size = 18368643, upload-time = "2026-03-29T13:19:10.775Z" }, - { url = "https://files.pythonhosted.org/packages/63/f6/d417977c5f519b17c8a5c3bc9e8304b0908b0e21136fe43bf628a1343914/numpy-2.4.4-cp312-cp312-win32.whl", hash = "sha256:0d35aea54ad1d420c812bfa0385c71cd7cc5bcf7c65fed95fc2cd02fe8c79827", size = 5961117, upload-time = "2026-03-29T13:19:13.464Z" }, - { url = "https://files.pythonhosted.org/packages/2d/5b/e1deebf88ff431b01b7406ca3583ab2bbb90972bbe1c568732e49c844f7e/numpy-2.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:b5f0362dc928a6ecd9db58868fca5e48485205e3855957bdedea308f8672ea4a", size = 12320584, upload-time = "2026-03-29T13:19:16.155Z" }, - { url = "https://files.pythonhosted.org/packages/58/89/e4e856ac82a68c3ed64486a544977d0e7bdd18b8da75b78a577ca31c4395/numpy-2.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:846300f379b5b12cc769334464656bc882e0735d27d9726568bc932fdc49d5ec", size = 10221450, upload-time = "2026-03-29T13:19:18.994Z" }, - { url = "https://files.pythonhosted.org/packages/14/1d/d0a583ce4fefcc3308806a749a536c201ed6b5ad6e1322e227ee4848979d/numpy-2.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:08f2e31ed5e6f04b118e49821397f12767934cfdd12a1ce86a058f91e004ee50", size = 16684933, upload-time = "2026-03-29T13:19:22.47Z" }, - { url = "https://files.pythonhosted.org/packages/c1/62/2b7a48fbb745d344742c0277f01286dead15f3f68e4f359fbfcf7b48f70f/numpy-2.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e823b8b6edc81e747526f70f71a9c0a07ac4e7ad13020aa736bb7c9d67196115", size = 14694532, upload-time = "2026-03-29T13:19:25.581Z" }, - { url = "https://files.pythonhosted.org/packages/e5/87/499737bfba066b4a3bebff24a8f1c5b2dee410b209bc6668c9be692580f0/numpy-2.4.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4a19d9dba1a76618dd86b164d608566f393f8ec6ac7c44f0cc879011c45e65af", size = 5199661, upload-time = "2026-03-29T13:19:28.31Z" }, - { url = "https://files.pythonhosted.org/packages/cd/da/464d551604320d1491bc345efed99b4b7034143a85787aab78d5691d5a0e/numpy-2.4.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d2a8490669bfe99a233298348acc2d824d496dee0e66e31b66a6022c2ad74a5c", size = 6547539, upload-time = "2026-03-29T13:19:30.97Z" }, - { url = "https://files.pythonhosted.org/packages/7d/90/8d23e3b0dafd024bf31bdec225b3bb5c2dbfa6912f8a53b8659f21216cbf/numpy-2.4.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45dbed2ab436a9e826e302fcdcbe9133f9b0006e5af7168afb8963a6520da103", size = 15668806, upload-time = "2026-03-29T13:19:33.887Z" }, - { url = "https://files.pythonhosted.org/packages/d1/73/a9d864e42a01896bb5974475438f16086be9ba1f0d19d0bb7a07427c4a8b/numpy-2.4.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c901b15172510173f5cb310eae652908340f8dede90fff9e3bf6c0d8dfd92f83", size = 16632682, upload-time = "2026-03-29T13:19:37.336Z" }, - { url = "https://files.pythonhosted.org/packages/34/fb/14570d65c3bde4e202a031210475ae9cde9b7686a2e7dc97ee67d2833b35/numpy-2.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:99d838547ace2c4aace6c4f76e879ddfe02bb58a80c1549928477862b7a6d6ed", size = 17019810, upload-time = "2026-03-29T13:19:40.963Z" }, - { url = "https://files.pythonhosted.org/packages/8a/77/2ba9d87081fd41f6d640c83f26fb7351e536b7ce6dd9061b6af5904e8e46/numpy-2.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0aec54fd785890ecca25a6003fd9a5aed47ad607bbac5cd64f836ad8666f4959", size = 18357394, upload-time = "2026-03-29T13:19:44.859Z" }, - { url = "https://files.pythonhosted.org/packages/a2/23/52666c9a41708b0853fa3b1a12c90da38c507a3074883823126d4e9d5b30/numpy-2.4.4-cp313-cp313-win32.whl", hash = "sha256:07077278157d02f65c43b1b26a3886bce886f95d20aabd11f87932750dfb14ed", size = 5959556, upload-time = "2026-03-29T13:19:47.661Z" }, - { url = "https://files.pythonhosted.org/packages/57/fb/48649b4971cde70d817cf97a2a2fdc0b4d8308569f1dd2f2611959d2e0cf/numpy-2.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:5c70f1cc1c4efbe316a572e2d8b9b9cc44e89b95f79ca3331553fbb63716e2bf", size = 12317311, upload-time = "2026-03-29T13:19:50.67Z" }, - { url = "https://files.pythonhosted.org/packages/ba/d8/11490cddd564eb4de97b4579ef6bfe6a736cc07e94c1598590ae25415e01/numpy-2.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:ef4059d6e5152fa1a39f888e344c73fdc926e1b2dd58c771d67b0acfbf2aa67d", size = 10222060, upload-time = "2026-03-29T13:19:54.229Z" }, - { url = "https://files.pythonhosted.org/packages/99/5d/dab4339177a905aad3e2221c915b35202f1ec30d750dd2e5e9d9a72b804b/numpy-2.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4bbc7f303d125971f60ec0aaad5e12c62d0d2c925f0ab1273debd0e4ba37aba5", size = 14822302, upload-time = "2026-03-29T13:19:57.585Z" }, - { url = "https://files.pythonhosted.org/packages/eb/e4/0564a65e7d3d97562ed6f9b0fd0fb0a6f559ee444092f105938b50043876/numpy-2.4.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:4d6d57903571f86180eb98f8f0c839fa9ebbfb031356d87f1361be91e433f5b7", size = 5327407, upload-time = "2026-03-29T13:20:00.601Z" }, - { url = "https://files.pythonhosted.org/packages/29/8d/35a3a6ce5ad371afa58b4700f1c820f8f279948cca32524e0a695b0ded83/numpy-2.4.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:4636de7fd195197b7535f231b5de9e4b36d2c440b6e566d2e4e4746e6af0ca93", size = 6647631, upload-time = "2026-03-29T13:20:02.855Z" }, - { url = "https://files.pythonhosted.org/packages/f4/da/477731acbd5a58a946c736edfdabb2ac5b34c3d08d1ba1a7b437fa0884df/numpy-2.4.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad2e2ef14e0b04e544ea2fa0a36463f847f113d314aa02e5b402fdf910ef309e", size = 15727691, upload-time = "2026-03-29T13:20:06.004Z" }, - { url = "https://files.pythonhosted.org/packages/e6/db/338535d9b152beabeb511579598418ba0212ce77cf9718edd70262cc4370/numpy-2.4.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a285b3b96f951841799528cd1f4f01cd70e7e0204b4abebac9463eecfcf2a40", size = 16681241, upload-time = "2026-03-29T13:20:09.417Z" }, - { url = "https://files.pythonhosted.org/packages/e2/a9/ad248e8f58beb7a0219b413c9c7d8151c5d285f7f946c3e26695bdbbe2df/numpy-2.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f8474c4241bc18b750be2abea9d7a9ec84f46ef861dbacf86a4f6e043401f79e", size = 17085767, upload-time = "2026-03-29T13:20:13.126Z" }, - { url = "https://files.pythonhosted.org/packages/b5/1a/3b88ccd3694681356f70da841630e4725a7264d6a885c8d442a697e1146b/numpy-2.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4e874c976154687c1f71715b034739b45c7711bec81db01914770373d125e392", size = 18403169, upload-time = "2026-03-29T13:20:17.096Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c9/fcfd5d0639222c6eac7f304829b04892ef51c96a75d479214d77e3ce6e33/numpy-2.4.4-cp313-cp313t-win32.whl", hash = "sha256:9c585a1790d5436a5374bac930dad6ed244c046ed91b2b2a3634eb2971d21008", size = 6083477, upload-time = "2026-03-29T13:20:20.195Z" }, - { url = "https://files.pythonhosted.org/packages/d5/e3/3938a61d1c538aaec8ed6fd6323f57b0c2d2d2219512434c5c878db76553/numpy-2.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:93e15038125dc1e5345d9b5b68aa7f996ec33b98118d18c6ca0d0b7d6198b7e8", size = 12457487, upload-time = "2026-03-29T13:20:22.946Z" }, - { url = "https://files.pythonhosted.org/packages/97/6a/7e345032cc60501721ef94e0e30b60f6b0bd601f9174ebd36389a2b86d40/numpy-2.4.4-cp313-cp313t-win_arm64.whl", hash = "sha256:0dfd3f9d3adbe2920b68b5cd3d51444e13a10792ec7154cd0a2f6e74d4ab3233", size = 10292002, upload-time = "2026-03-29T13:20:25.909Z" }, - { url = "https://files.pythonhosted.org/packages/6e/06/c54062f85f673dd5c04cbe2f14c3acb8c8b95e3384869bb8cc9bff8cb9df/numpy-2.4.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f169b9a863d34f5d11b8698ead99febeaa17a13ca044961aa8e2662a6c7766a0", size = 16684353, upload-time = "2026-03-29T13:20:29.504Z" }, - { url = "https://files.pythonhosted.org/packages/4c/39/8a320264a84404c74cc7e79715de85d6130fa07a0898f67fb5cd5bd79908/numpy-2.4.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2483e4584a1cb3092da4470b38866634bafb223cbcd551ee047633fd2584599a", size = 14704914, upload-time = "2026-03-29T13:20:33.547Z" }, - { url = "https://files.pythonhosted.org/packages/91/fb/287076b2614e1d1044235f50f03748f31fa287e3dbe6abeb35cdfa351eca/numpy-2.4.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:2d19e6e2095506d1736b7d80595e0f252d76b89f5e715c35e06e937679ea7d7a", size = 5210005, upload-time = "2026-03-29T13:20:36.45Z" }, - { url = "https://files.pythonhosted.org/packages/63/eb/fcc338595309910de6ecabfcef2419a9ce24399680bfb149421fa2df1280/numpy-2.4.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:6a246d5914aa1c820c9443ddcee9c02bec3e203b0c080349533fae17727dfd1b", size = 6544974, upload-time = "2026-03-29T13:20:39.014Z" }, - { url = "https://files.pythonhosted.org/packages/44/5d/e7e9044032a716cdfaa3fba27a8e874bf1c5f1912a1ddd4ed071bf8a14a6/numpy-2.4.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:989824e9faf85f96ec9c7761cd8d29c531ad857bfa1daa930cba85baaecf1a9a", size = 15684591, upload-time = "2026-03-29T13:20:42.146Z" }, - { url = "https://files.pythonhosted.org/packages/98/7c/21252050676612625449b4807d6b695b9ce8a7c9e1c197ee6216c8a65c7c/numpy-2.4.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:27a8d92cd10f1382a67d7cf4db7ce18341b66438bdd9f691d7b0e48d104c2a9d", size = 16637700, upload-time = "2026-03-29T13:20:46.204Z" }, - { url = "https://files.pythonhosted.org/packages/b1/29/56d2bbef9465db24ef25393383d761a1af4f446a1df9b8cded4fe3a5a5d7/numpy-2.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e44319a2953c738205bf3354537979eaa3998ed673395b964c1176083dd46252", size = 17035781, upload-time = "2026-03-29T13:20:50.242Z" }, - { url = "https://files.pythonhosted.org/packages/e3/2b/a35a6d7589d21f44cea7d0a98de5ddcbb3d421b2622a5c96b1edf18707c3/numpy-2.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e892aff75639bbef0d2a2cfd55535510df26ff92f63c92cd84ef8d4ba5a5557f", size = 18362959, upload-time = "2026-03-29T13:20:54.019Z" }, - { url = "https://files.pythonhosted.org/packages/64/c9/d52ec581f2390e0f5f85cbfd80fb83d965fc15e9f0e1aec2195faa142cde/numpy-2.4.4-cp314-cp314-win32.whl", hash = "sha256:1378871da56ca8943c2ba674530924bb8ca40cd228358a3b5f302ad60cf875fc", size = 6008768, upload-time = "2026-03-29T13:20:56.912Z" }, - { url = "https://files.pythonhosted.org/packages/fa/22/4cc31a62a6c7b74a8730e31a4274c5dc80e005751e277a2ce38e675e4923/numpy-2.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:715d1c092715954784bc79e1174fc2a90093dc4dc84ea15eb14dad8abdcdeb74", size = 12449181, upload-time = "2026-03-29T13:20:59.548Z" }, - { url = "https://files.pythonhosted.org/packages/70/2e/14cda6f4d8e396c612d1bf97f22958e92148801d7e4f110cabebdc0eef4b/numpy-2.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:2c194dd721e54ecad9ad387c1d35e63dce5c4450c6dc7dd5611283dda239aabb", size = 10496035, upload-time = "2026-03-29T13:21:02.524Z" }, - { url = "https://files.pythonhosted.org/packages/b1/e8/8fed8c8d848d7ecea092dc3469643f9d10bc3a134a815a3b033da1d2039b/numpy-2.4.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2aa0613a5177c264ff5921051a5719d20095ea586ca88cc802c5c218d1c67d3e", size = 14824958, upload-time = "2026-03-29T13:21:05.671Z" }, - { url = "https://files.pythonhosted.org/packages/05/1a/d8007a5138c179c2bf33ef44503e83d70434d2642877ee8fbb230e7c0548/numpy-2.4.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:42c16925aa5a02362f986765f9ebabf20de75cdefdca827d14315c568dcab113", size = 5330020, upload-time = "2026-03-29T13:21:08.635Z" }, - { url = "https://files.pythonhosted.org/packages/99/64/ffb99ac6ae93faf117bcbd5c7ba48a7f45364a33e8e458545d3633615dda/numpy-2.4.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:874f200b2a981c647340f841730fc3a2b54c9d940566a3c4149099591e2c4c3d", size = 6650758, upload-time = "2026-03-29T13:21:10.949Z" }, - { url = "https://files.pythonhosted.org/packages/6e/6e/795cc078b78a384052e73b2f6281ff7a700e9bf53bcce2ee579d4f6dd879/numpy-2.4.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9b39d38a9bd2ae1becd7eac1303d031c5c110ad31f2b319c6e7d98b135c934d", size = 15729948, upload-time = "2026-03-29T13:21:14.047Z" }, - { url = "https://files.pythonhosted.org/packages/5f/86/2acbda8cc2af5f3d7bfc791192863b9e3e19674da7b5e533fded124d1299/numpy-2.4.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b268594bccac7d7cf5844c7732e3f20c50921d94e36d7ec9b79e9857694b1b2f", size = 16679325, upload-time = "2026-03-29T13:21:17.561Z" }, - { url = "https://files.pythonhosted.org/packages/bc/59/cafd83018f4aa55e0ac6fa92aa066c0a1877b77a615ceff1711c260ffae8/numpy-2.4.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ac6b31e35612a26483e20750126d30d0941f949426974cace8e6b5c58a3657b0", size = 17084883, upload-time = "2026-03-29T13:21:21.106Z" }, - { url = "https://files.pythonhosted.org/packages/f0/85/a42548db84e65ece46ab2caea3d3f78b416a47af387fcbb47ec28e660dc2/numpy-2.4.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8e3ed142f2728df44263aaf5fb1f5b0b99f4070c553a0d7f033be65338329150", size = 18403474, upload-time = "2026-03-29T13:21:24.828Z" }, - { url = "https://files.pythonhosted.org/packages/ed/ad/483d9e262f4b831000062e5d8a45e342166ec8aaa1195264982bca267e62/numpy-2.4.4-cp314-cp314t-win32.whl", hash = "sha256:dddbbd259598d7240b18c9d87c56a9d2fb3b02fe266f49a7c101532e78c1d871", size = 6155500, upload-time = "2026-03-29T13:21:28.205Z" }, - { url = "https://files.pythonhosted.org/packages/c7/03/2fc4e14c7bd4ff2964b74ba90ecb8552540b6315f201df70f137faa5c589/numpy-2.4.4-cp314-cp314t-win_amd64.whl", hash = "sha256:a7164afb23be6e37ad90b2f10426149fd75aee07ca55653d2aa41e66c4ef697e", size = 12637755, upload-time = "2026-03-29T13:21:31.107Z" }, - { url = "https://files.pythonhosted.org/packages/58/78/548fb8e07b1a341746bfbecb32f2c268470f45fa028aacdbd10d9bc73aab/numpy-2.4.4-cp314-cp314t-win_arm64.whl", hash = "sha256:ba203255017337d39f89bdd58417f03c4426f12beed0440cfd933cb15f8669c7", size = 10566643, upload-time = "2026-03-29T13:21:34.339Z" }, - { url = "https://files.pythonhosted.org/packages/6b/33/8fae8f964a4f63ed528264ddf25d2b683d0b663e3cba26961eb838a7c1bd/numpy-2.4.4-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:58c8b5929fcb8287cbd6f0a3fae19c6e03a5c48402ae792962ac465224a629a4", size = 16854491, upload-time = "2026-03-29T13:21:38.03Z" }, - { url = "https://files.pythonhosted.org/packages/bc/d0/1aabee441380b981cf8cdda3ae7a46aa827d1b5a8cce84d14598bc94d6d9/numpy-2.4.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:eea7ac5d2dce4189771cedb559c738a71512768210dc4e4753b107a2048b3d0e", size = 14895830, upload-time = "2026-03-29T13:21:41.509Z" }, - { url = "https://files.pythonhosted.org/packages/a5/b8/aafb0d1065416894fccf4df6b49ef22b8db045187949545bced89c034b8e/numpy-2.4.4-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:51fc224f7ca4d92656d5a5eb315f12eb5fe2c97a66249aa7b5f562528a3be38c", size = 5400927, upload-time = "2026-03-29T13:21:44.747Z" }, - { url = "https://files.pythonhosted.org/packages/d6/77/063baa20b08b431038c7f9ff5435540c7b7265c78cf56012a483019ca72d/numpy-2.4.4-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:28a650663f7314afc3e6ec620f44f333c386aad9f6fc472030865dc0ebb26ee3", size = 6715557, upload-time = "2026-03-29T13:21:47.406Z" }, - { url = "https://files.pythonhosted.org/packages/c7/a8/379542d45a14f149444c5c4c4e7714707239ce9cc1de8c2803958889da14/numpy-2.4.4-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:19710a9ca9992d7174e9c52f643d4272dcd1558c5f7af7f6f8190f633bd651a7", size = 15804253, upload-time = "2026-03-29T13:21:50.753Z" }, - { url = "https://files.pythonhosted.org/packages/a2/c8/f0a45426d6d21e7ea3310a15cf90c43a14d9232c31a837702dba437f3373/numpy-2.4.4-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b2aec6af35c113b05695ebb5749a787acd63cafc83086a05771d1e1cd1e555f", size = 16753552, upload-time = "2026-03-29T13:21:54.344Z" }, - { url = "https://files.pythonhosted.org/packages/04/74/f4c001f4714c3ad9ce037e18cf2b9c64871a84951eaa0baf683a9ca9301c/numpy-2.4.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f2cf083b324a467e1ab358c105f6cad5ea950f50524668a80c486ff1db24e119", size = 12509075, upload-time = "2026-03-29T13:21:57.644Z" }, -] - -[[package]] -name = "nvidia-cublas" -version = "13.1.1.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-cuda-nvrtc", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/a1/0bd24ee8c8d03adac032fd2909426a00c88f8c57961b1277ded97f91119f/nvidia_cublas-13.1.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b7a210458267ac818974c53038fbec2e969d5c99f305ab15c72522fa9f001dd5", size = 542848918, upload-time = "2026-04-08T18:46:22.985Z" }, - { url = "https://files.pythonhosted.org/packages/3b/cd/154ca20c38269e05eff77c1464e6c1da89f50a6390b565e9d82e06bc11e1/nvidia_cublas-13.1.1.3-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:37936a16db8fe4ac1f065c2139360608a543a09275cb1a1af612e08cfa065436", size = 423138758, upload-time = "2026-04-08T18:46:58.655Z" }, -] - -[[package]] -name = "nvidia-cuda-cupti" -version = "13.0.85" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/2a/80353b103fc20ce05ef51e928daed4b6015db4aaa9162ed0997090fe2250/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_aarch64.whl", hash = "sha256:796bd679890ee55fb14a94629b698b6db54bcfd833d391d5e94017dd9d7d3151", size = 10310827, upload-time = "2025-09-04T08:26:42.012Z" }, - { url = "https://files.pythonhosted.org/packages/33/6d/737d164b4837a9bbd202f5ae3078975f0525a55730fe871d8ed4e3b952b0/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_x86_64.whl", hash = "sha256:4eb01c08e859bf924d222250d2e8f8b8ff6d3db4721288cf35d14252a4d933c8", size = 10715597, upload-time = "2025-09-04T08:26:51.312Z" }, -] - -[[package]] -name = "nvidia-cuda-nvrtc" -version = "13.0.88" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/68/483a78f5e8f31b08fb1bb671559968c0ca3a065ac7acabfc7cee55214fd6/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:ad9b6d2ead2435f11cbb6868809d2adeeee302e9bb94bcf0539c7a40d80e8575", size = 90215200, upload-time = "2025-09-04T08:28:44.204Z" }, - { url = "https://files.pythonhosted.org/packages/b7/dc/6bb80850e0b7edd6588d560758f17e0550893a1feaf436807d64d2da040f/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d27f20a0ca67a4bb34268a5e951033496c5b74870b868bacd046b1b8e0c3267b", size = 43015449, upload-time = "2025-09-04T08:28:20.239Z" }, -] - -[[package]] -name = "nvidia-cuda-runtime" -version = "13.0.96" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/87/4f/17d7b9b8e285199c58ce28e31b5c5bbaa4d8271af06a89b6405258245de2/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ef9bcbe90493a2b9d810e43d249adb3d02e98dd30200d86607d8d02687c43f55", size = 2261060, upload-time = "2025-10-09T08:55:15.78Z" }, - { url = "https://files.pythonhosted.org/packages/2e/24/d1558f3b68b1d26e706813b1d10aa1d785e4698c425af8db8edc3dced472/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f82250d7782aa23b6cfe765ecc7db554bd3c2870c43f3d1821f1d18aebf0548", size = 2243632, upload-time = "2025-10-09T08:55:36.117Z" }, -] - -[[package]] -name = "nvidia-cudnn-cu13" -version = "9.20.0.48" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-cublas", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/56/c5/83384d846b2fd17c44bd499b36c75a45ed4f095fbbb2252294e89cea5c5c/nvidia_cudnn_cu13-9.20.0.48-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:e31454ae00094b0c55319d9d15b6fa2fc50a9e1c0f5c8c80fb75258234e731e1", size = 444574296, upload-time = "2026-03-09T19:28:27.751Z" }, - { url = "https://files.pythonhosted.org/packages/6e/5e/edb9c0ae051602c3ccaffe424256463636d639e27d7f302dde9975ef9e7a/nvidia_cudnn_cu13-9.20.0.48-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0c45dd8eeb50b603f07995b1b300c62ffe6a1980482b82b3bcf94a4ca9d49304", size = 366173588, upload-time = "2026-03-09T19:29:34.474Z" }, -] - -[[package]] -name = "nvidia-cufft" -version = "12.0.0.61" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-nvjitlink", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554, upload-time = "2025-09-04T08:31:38.196Z" }, - { url = "https://files.pythonhosted.org/packages/a8/2f/7b57e29836ea8714f81e9898409196f47d772d5ddedddf1592eadb8ab743/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6c44f692dce8fd5ffd3e3df134b6cdb9c2f72d99cf40b62c32dde45eea9ddad3", size = 214085489, upload-time = "2025-09-04T08:31:56.044Z" }, -] - -[[package]] -name = "nvidia-cufile" -version = "1.15.1.6" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/70/4f193de89a48b71714e74602ee14d04e4019ad36a5a9f20c425776e72cd6/nvidia_cufile-1.15.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08a3ecefae5a01c7f5117351c64f17c7c62efa5fffdbe24fc7d298da19cd0b44", size = 1223672, upload-time = "2025-09-04T08:32:22.779Z" }, - { url = "https://files.pythonhosted.org/packages/ab/73/cc4a14c9813a8a0d509417cf5f4bdaba76e924d58beb9864f5a7baceefbf/nvidia_cufile-1.15.1.6-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:bdc0deedc61f548bddf7733bdc216456c2fdb101d020e1ab4b88d232d5e2f6d1", size = 1136992, upload-time = "2025-09-04T08:32:14.119Z" }, -] - -[[package]] -name = "nvidia-curand" -version = "10.4.0.35" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/72/7c2ae24fb6b63a32e6ae5d241cc65263ea18d08802aaae087d9f013335a2/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:133df5a7509c3e292aaa2b477afd0194f06ce4ea24d714d616ff36439cee349a", size = 61962106, upload-time = "2025-08-04T10:21:41.128Z" }, - { url = "https://files.pythonhosted.org/packages/a5/9f/be0a41ca4a4917abf5cb9ae0daff1a6060cc5de950aec0396de9f3b52bc5/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:1aee33a5da6e1db083fe2b90082def8915f30f3248d5896bcec36a579d941bfc", size = 59544258, upload-time = "2025-08-04T10:22:03.992Z" }, -] - -[[package]] -name = "nvidia-cusolver" -version = "12.0.4.66" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-cublas", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, - { name = "nvidia-cusparse", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, - { name = "nvidia-nvjitlink", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760, upload-time = "2025-09-04T08:33:04.222Z" }, - { url = "https://files.pythonhosted.org/packages/5f/67/cba3777620cdacb99102da4042883709c41c709f4b6323c10781a9c3aa34/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0a759da5dea5c0ea10fd307de75cdeb59e7ea4fcb8add0924859b944babf1112", size = 200941980, upload-time = "2025-09-04T08:33:22.767Z" }, -] - -[[package]] -name = "nvidia-cusparse" -version = "12.6.3.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-nvjitlink", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568, upload-time = "2025-09-04T08:33:42.864Z" }, - { url = "https://files.pythonhosted.org/packages/fa/18/623c77619c31d62efd55302939756966f3ecc8d724a14dab2b75f1508850/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b3c89c88d01ee0e477cb7f82ef60a11a4bcd57b6b87c33f789350b59759360b", size = 145942937, upload-time = "2025-09-04T08:33:58.029Z" }, -] - -[[package]] -name = "nvidia-cusparselt-cu13" -version = "0.8.1" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/46/e1/cdc1797eadf82d3a9a575a19b33fdc871a97edbec42c00b5b5e914f4aff4/nvidia_cusparselt_cu13-0.8.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:4dca476c50bf4780d46cd0bfbd82e2bc10a08e4fef7950917ce8d7578d22a23f", size = 221051344, upload-time = "2025-09-05T18:49:51.289Z" }, - { url = "https://files.pythonhosted.org/packages/34/7d/2661f2fb3ac4302f3a246f5fc030213ac60c1fe0bce84f9783dbd831dbb7/nvidia_cusparselt_cu13-0.8.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:786ce87568c303fadb5afcc7102d454cd3040d75f6f8626f5db460d1871f4dd0", size = 170148586, upload-time = "2025-09-05T18:50:50.248Z" }, -] - -[[package]] -name = "nvidia-nccl-cu13" -version = "2.29.7" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/72/0d/daf50d44177ee0cbc7ff0a0c91eb5ff676c82be42f9a970bc7597f440c3a/nvidia_nccl_cu13-2.29.7-py3-none-manylinux_2_18_aarch64.whl", hash = "sha256:674a12383e3c38a1bcccae7d4f3633b37852230b6047883cb2f4c2d1b36d9bf5", size = 206014712, upload-time = "2026-03-03T05:34:20.843Z" }, - { url = "https://files.pythonhosted.org/packages/67/f4/58e4e91b6919367c7aafb8e36fce9aad1a3047e536bf7e2fd560927d3a4c/nvidia_nccl_cu13-2.29.7-py3-none-manylinux_2_18_x86_64.whl", hash = "sha256:edd81538446786ec3b73972543e53bb43bcaf0bfc8ef76cb679fcc390ffe136d", size = 205976000, upload-time = "2026-03-03T05:36:24.472Z" }, -] - -[[package]] -name = "nvidia-nvjitlink" -version = "13.0.88" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/56/7a/123e033aaff487c77107195fa5a2b8686795ca537935a24efae476c41f05/nvidia_nvjitlink-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:13a74f429e23b921c1109976abefacc69835f2f433ebd323d3946e11d804e47b", size = 40713933, upload-time = "2025-09-04T08:35:43.553Z" }, - { url = "https://files.pythonhosted.org/packages/ab/2c/93c5250e64df4f894f1cbb397c6fd71f79813f9fd79d7cd61de3f97b3c2d/nvidia_nvjitlink-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e931536ccc7d467a98ba1d8b89ff7fa7f1fa3b13f2b0069118cd7f47bff07d0c", size = 38768748, upload-time = "2025-09-04T08:35:20.008Z" }, -] - -[[package]] -name = "nvidia-nvshmem-cu13" -version = "3.4.5" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/0f/05cc9c720236dcd2db9c1ab97fff629e96821be2e63103569da0c9b72f19/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dc2a197f38e5d0376ad52cd1a2a3617d3cdc150fd5966f4aee9bcebb1d68fe9", size = 60215947, upload-time = "2025-09-06T00:32:20.022Z" }, - { url = "https://files.pythonhosted.org/packages/3c/35/a9bf80a609e74e3b000fef598933235c908fcefcef9026042b8e6dfde2a9/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:290f0a2ee94c9f3687a02502f3b9299a9f9fe826e6d0287ee18482e78d495b80", size = 60412546, upload-time = "2025-09-06T00:32:41.564Z" }, -] - -[[package]] -name = "nvidia-nvtx" -version = "13.0.85" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/f3/d86c845465a2723ad7e1e5c36dcd75ddb82898b3f53be47ebd429fb2fa5d/nvidia_nvtx-13.0.85-py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4936d1d6780fbe68db454f5e72a42ff64d1fd6397df9f363ae786930fd5c1cd4", size = 148047, upload-time = "2025-09-04T08:29:01.761Z" }, - { url = "https://files.pythonhosted.org/packages/a8/64/3708a90d1ebe202ffdeb7185f878a3c84d15c2b2c31858da2ce0583e2def/nvidia_nvtx-13.0.85-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb7780edb6b14107373c835bf8b72e7a178bac7367e23da7acb108f973f157a6", size = 148878, upload-time = "2025-09-04T08:28:53.627Z" }, -] - -[[package]] -name = "opt-einsum" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/b9/2ac072041e899a52f20cf9510850ff58295003aa75525e58343591b0cbfb/opt_einsum-3.4.0.tar.gz", hash = "sha256:96ca72f1b886d148241348783498194c577fa30a8faac108586b14f1ba4473ac", size = 63004, upload-time = "2024-09-26T14:33:24.483Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/23/cd/066e86230ae37ed0be70aae89aabf03ca8d9f39c8aea0dec8029455b5540/opt_einsum-3.4.0-py3-none-any.whl", hash = "sha256:69bb92469f86a1565195ece4ac0323943e83477171b91d24c35afe028a90d7cd", size = 71932, upload-time = "2024-09-26T14:33:23.039Z" }, -] - -[[package]] -name = "packaging" -version = "26.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, -] - -[[package]] -name = "pandas" -version = "2.3.3" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "python-dateutil", marker = "python_full_version < '3.11'" }, - { name = "pytz", marker = "python_full_version < '3.11'" }, - { name = "tzdata", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/f7/f425a00df4fcc22b292c6895c6831c0c8ae1d9fac1e024d16f98a9ce8749/pandas-2.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:376c6446ae31770764215a6c937f72d917f214b43560603cd60da6408f183b6c", size = 11555763, upload-time = "2025-09-29T23:16:53.287Z" }, - { url = "https://files.pythonhosted.org/packages/13/4f/66d99628ff8ce7857aca52fed8f0066ce209f96be2fede6cef9f84e8d04f/pandas-2.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e19d192383eab2f4ceb30b412b22ea30690c9e618f78870357ae1d682912015a", size = 10801217, upload-time = "2025-09-29T23:17:04.522Z" }, - { url = "https://files.pythonhosted.org/packages/1d/03/3fc4a529a7710f890a239cc496fc6d50ad4a0995657dccc1d64695adb9f4/pandas-2.3.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5caf26f64126b6c7aec964f74266f435afef1c1b13da3b0636c7518a1fa3e2b1", size = 12148791, upload-time = "2025-09-29T23:17:18.444Z" }, - { url = "https://files.pythonhosted.org/packages/40/a8/4dac1f8f8235e5d25b9955d02ff6f29396191d4e665d71122c3722ca83c5/pandas-2.3.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd7478f1463441ae4ca7308a70e90b33470fa593429f9d4c578dd00d1fa78838", size = 12769373, upload-time = "2025-09-29T23:17:35.846Z" }, - { url = "https://files.pythonhosted.org/packages/df/91/82cc5169b6b25440a7fc0ef3a694582418d875c8e3ebf796a6d6470aa578/pandas-2.3.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4793891684806ae50d1288c9bae9330293ab4e083ccd1c5e383c34549c6e4250", size = 13200444, upload-time = "2025-09-29T23:17:49.341Z" }, - { url = "https://files.pythonhosted.org/packages/10/ae/89b3283800ab58f7af2952704078555fa60c807fff764395bb57ea0b0dbd/pandas-2.3.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28083c648d9a99a5dd035ec125d42439c6c1c525098c58af0fc38dd1a7a1b3d4", size = 13858459, upload-time = "2025-09-29T23:18:03.722Z" }, - { url = "https://files.pythonhosted.org/packages/85/72/530900610650f54a35a19476eca5104f38555afccda1aa11a92ee14cb21d/pandas-2.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:503cf027cf9940d2ceaa1a93cfb5f8c8c7e6e90720a2850378f0b3f3b1e06826", size = 11346086, upload-time = "2025-09-29T23:18:18.505Z" }, - { url = "https://files.pythonhosted.org/packages/c1/fa/7ac648108144a095b4fb6aa3de1954689f7af60a14cf25583f4960ecb878/pandas-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:602b8615ebcc4a0c1751e71840428ddebeb142ec02c786e8ad6b1ce3c8dec523", size = 11578790, upload-time = "2025-09-29T23:18:30.065Z" }, - { url = "https://files.pythonhosted.org/packages/9b/35/74442388c6cf008882d4d4bdfc4109be87e9b8b7ccd097ad1e7f006e2e95/pandas-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8fe25fc7b623b0ef6b5009149627e34d2a4657e880948ec3c840e9402e5c1b45", size = 10833831, upload-time = "2025-09-29T23:38:56.071Z" }, - { url = "https://files.pythonhosted.org/packages/fe/e4/de154cbfeee13383ad58d23017da99390b91d73f8c11856f2095e813201b/pandas-2.3.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b468d3dad6ff947df92dcb32ede5b7bd41a9b3cceef0a30ed925f6d01fb8fa66", size = 12199267, upload-time = "2025-09-29T23:18:41.627Z" }, - { url = "https://files.pythonhosted.org/packages/bf/c9/63f8d545568d9ab91476b1818b4741f521646cbdd151c6efebf40d6de6f7/pandas-2.3.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b98560e98cb334799c0b07ca7967ac361a47326e9b4e5a7dfb5ab2b1c9d35a1b", size = 12789281, upload-time = "2025-09-29T23:18:56.834Z" }, - { url = "https://files.pythonhosted.org/packages/f2/00/a5ac8c7a0e67fd1a6059e40aa08fa1c52cc00709077d2300e210c3ce0322/pandas-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37b5848ba49824e5c30bedb9c830ab9b7751fd049bc7914533e01c65f79791", size = 13240453, upload-time = "2025-09-29T23:19:09.247Z" }, - { url = "https://files.pythonhosted.org/packages/27/4d/5c23a5bc7bd209231618dd9e606ce076272c9bc4f12023a70e03a86b4067/pandas-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db4301b2d1f926ae677a751eb2bd0e8c5f5319c9cb3f88b0becbbb0b07b34151", size = 13890361, upload-time = "2025-09-29T23:19:25.342Z" }, - { url = "https://files.pythonhosted.org/packages/8e/59/712db1d7040520de7a4965df15b774348980e6df45c129b8c64d0dbe74ef/pandas-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:f086f6fe114e19d92014a1966f43a3e62285109afe874f067f5abbdcbb10e59c", size = 11348702, upload-time = "2025-09-29T23:19:38.296Z" }, - { url = "https://files.pythonhosted.org/packages/9c/fb/231d89e8637c808b997d172b18e9d4a4bc7bf31296196c260526055d1ea0/pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53", size = 11597846, upload-time = "2025-09-29T23:19:48.856Z" }, - { url = "https://files.pythonhosted.org/packages/5c/bd/bf8064d9cfa214294356c2d6702b716d3cf3bb24be59287a6a21e24cae6b/pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35", size = 10729618, upload-time = "2025-09-29T23:39:08.659Z" }, - { url = "https://files.pythonhosted.org/packages/57/56/cf2dbe1a3f5271370669475ead12ce77c61726ffd19a35546e31aa8edf4e/pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908", size = 11737212, upload-time = "2025-09-29T23:19:59.765Z" }, - { url = "https://files.pythonhosted.org/packages/e5/63/cd7d615331b328e287d8233ba9fdf191a9c2d11b6af0c7a59cfcec23de68/pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89", size = 12362693, upload-time = "2025-09-29T23:20:14.098Z" }, - { url = "https://files.pythonhosted.org/packages/a6/de/8b1895b107277d52f2b42d3a6806e69cfef0d5cf1d0ba343470b9d8e0a04/pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98", size = 12771002, upload-time = "2025-09-29T23:20:26.76Z" }, - { url = "https://files.pythonhosted.org/packages/87/21/84072af3187a677c5893b170ba2c8fbe450a6ff911234916da889b698220/pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084", size = 13450971, upload-time = "2025-09-29T23:20:41.344Z" }, - { url = "https://files.pythonhosted.org/packages/86/41/585a168330ff063014880a80d744219dbf1dd7a1c706e75ab3425a987384/pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b", size = 10992722, upload-time = "2025-09-29T23:20:54.139Z" }, - { url = "https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713", size = 11544671, upload-time = "2025-09-29T23:21:05.024Z" }, - { url = "https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8", size = 10680807, upload-time = "2025-09-29T23:21:15.979Z" }, - { url = "https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d", size = 11709872, upload-time = "2025-09-29T23:21:27.165Z" }, - { url = "https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac", size = 12306371, upload-time = "2025-09-29T23:21:40.532Z" }, - { url = "https://files.pythonhosted.org/packages/33/81/a3afc88fca4aa925804a27d2676d22dcd2031c2ebe08aabd0ae55b9ff282/pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c", size = 12765333, upload-time = "2025-09-29T23:21:55.77Z" }, - { url = "https://files.pythonhosted.org/packages/8d/0f/b4d4ae743a83742f1153464cf1a8ecfafc3ac59722a0b5c8602310cb7158/pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493", size = 13418120, upload-time = "2025-09-29T23:22:10.109Z" }, - { url = "https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee", size = 10993991, upload-time = "2025-09-29T23:25:04.889Z" }, - { url = "https://files.pythonhosted.org/packages/f9/ca/3f8d4f49740799189e1395812f3bf23b5e8fc7c190827d55a610da72ce55/pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5", size = 12048227, upload-time = "2025-09-29T23:22:24.343Z" }, - { url = "https://files.pythonhosted.org/packages/0e/5a/f43efec3e8c0cc92c4663ccad372dbdff72b60bdb56b2749f04aa1d07d7e/pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21", size = 11411056, upload-time = "2025-09-29T23:22:37.762Z" }, - { url = "https://files.pythonhosted.org/packages/46/b1/85331edfc591208c9d1a63a06baa67b21d332e63b7a591a5ba42a10bb507/pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78", size = 11645189, upload-time = "2025-09-29T23:22:51.688Z" }, - { url = "https://files.pythonhosted.org/packages/44/23/78d645adc35d94d1ac4f2a3c4112ab6f5b8999f4898b8cdf01252f8df4a9/pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110", size = 12121912, upload-time = "2025-09-29T23:23:05.042Z" }, - { url = "https://files.pythonhosted.org/packages/53/da/d10013df5e6aaef6b425aa0c32e1fc1f3e431e4bcabd420517dceadce354/pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86", size = 12712160, upload-time = "2025-09-29T23:23:28.57Z" }, - { url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233, upload-time = "2025-09-29T23:24:24.876Z" }, - { url = "https://files.pythonhosted.org/packages/04/fd/74903979833db8390b73b3a8a7d30d146d710bd32703724dd9083950386f/pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0", size = 11540635, upload-time = "2025-09-29T23:25:52.486Z" }, - { url = "https://files.pythonhosted.org/packages/21/00/266d6b357ad5e6d3ad55093a7e8efc7dd245f5a842b584db9f30b0f0a287/pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593", size = 10759079, upload-time = "2025-09-29T23:26:33.204Z" }, - { url = "https://files.pythonhosted.org/packages/ca/05/d01ef80a7a3a12b2f8bbf16daba1e17c98a2f039cbc8e2f77a2c5a63d382/pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c", size = 11814049, upload-time = "2025-09-29T23:27:15.384Z" }, - { url = "https://files.pythonhosted.org/packages/15/b2/0e62f78c0c5ba7e3d2c5945a82456f4fac76c480940f805e0b97fcbc2f65/pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b", size = 12332638, upload-time = "2025-09-29T23:27:51.625Z" }, - { url = "https://files.pythonhosted.org/packages/c5/33/dd70400631b62b9b29c3c93d2feee1d0964dc2bae2e5ad7a6c73a7f25325/pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6", size = 12886834, upload-time = "2025-09-29T23:28:21.289Z" }, - { url = "https://files.pythonhosted.org/packages/d3/18/b5d48f55821228d0d2692b34fd5034bb185e854bdb592e9c640f6290e012/pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3", size = 13409925, upload-time = "2025-09-29T23:28:58.261Z" }, - { url = "https://files.pythonhosted.org/packages/a6/3d/124ac75fcd0ecc09b8fdccb0246ef65e35b012030defb0e0eba2cbbbe948/pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5", size = 11109071, upload-time = "2025-09-29T23:32:27.484Z" }, - { url = "https://files.pythonhosted.org/packages/89/9c/0e21c895c38a157e0faa1fb64587a9226d6dd46452cac4532d80c3c4a244/pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec", size = 12048504, upload-time = "2025-09-29T23:29:31.47Z" }, - { url = "https://files.pythonhosted.org/packages/d7/82/b69a1c95df796858777b68fbe6a81d37443a33319761d7c652ce77797475/pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7", size = 11410702, upload-time = "2025-09-29T23:29:54.591Z" }, - { url = "https://files.pythonhosted.org/packages/f9/88/702bde3ba0a94b8c73a0181e05144b10f13f29ebfc2150c3a79062a8195d/pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450", size = 11634535, upload-time = "2025-09-29T23:30:21.003Z" }, - { url = "https://files.pythonhosted.org/packages/a4/1e/1bac1a839d12e6a82ec6cb40cda2edde64a2013a66963293696bbf31fbbb/pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5", size = 12121582, upload-time = "2025-09-29T23:30:43.391Z" }, - { url = "https://files.pythonhosted.org/packages/44/91/483de934193e12a3b1d6ae7c8645d083ff88dec75f46e827562f1e4b4da6/pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788", size = 12699963, upload-time = "2025-09-29T23:31:10.009Z" }, - { url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175, upload-time = "2025-09-29T23:31:59.173Z" }, -] - -[[package]] -name = "pandas" -version = "3.0.3" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'win32'", - "python_full_version == '3.12.*' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version == '3.12.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", -] -dependencies = [ - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "python-dateutil", marker = "python_full_version >= '3.11'" }, - { name = "tzdata", marker = "(python_full_version >= '3.11' and sys_platform == 'emscripten') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f8/87/4341c6252d1c47b08768c3d25ac487362bf403f0313ddae4a2a26c9b1b4c/pandas-3.0.3.tar.gz", hash = "sha256:696a4a00a2a2a35d4e5deb3fc946641b96c944f02230e4f76137fe35d806c4fc", size = 4651414, upload-time = "2026-05-11T18:54:29.21Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/42/16/b5c76b838fd9bf6ce84d3a53346b8874ec05c5f0040d75ef2c320100cd2a/pandas-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:455f6f8139d4282188f526868dbc3c828470e88a3d9d59a891bd46a455f21b98", size = 10338495, upload-time = "2026-05-11T18:52:11.558Z" }, - { url = "https://files.pythonhosted.org/packages/5a/b0/a4ffc4ae74d2d822200dcc46898987d8eb6032d1e2b219cae39da6f5cbcc/pandas-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4e15135e2ee5df1063313e2425ceef8ac0f4ae775893815b0923651b806a5639", size = 9938250, upload-time = "2026-05-11T18:52:17.005Z" }, - { url = "https://files.pythonhosted.org/packages/2e/b2/3323601a52caee42c019e370090ca4544b241437240ca04f786cce82b0cf/pandas-3.0.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:05f1f1752b8533ea03f7f39a9c15b1a058d067bb48f4748948e7a8691e0510f2", size = 10770558, upload-time = "2026-05-11T18:52:19.865Z" }, - { url = "https://files.pythonhosted.org/packages/32/f1/bbecd2f867b97abebe0f9b53d750f862251b40337e061b36676ded3d920f/pandas-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a1e45c80cceb3b4a21bc5939d52e8cbd8d9b7305309219d59e9754d9ce09e27", size = 11274611, upload-time = "2026-05-11T18:52:22.622Z" }, - { url = "https://files.pythonhosted.org/packages/7f/4f/eafabf2d5fae5adf143b4d18d3706c5efdc368a7c4eb1ee8a3eddabbd0f6/pandas-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:14da8316da4d0c5a77618425996bfb1248ca87fc2c1486e6fde4652bd18b5824", size = 11784670, upload-time = "2026-05-11T18:52:25.4Z" }, - { url = "https://files.pythonhosted.org/packages/49/44/1eb20389301b57b19cc099a1c2f662501f72f08a65f912d05822613c1532/pandas-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a55066a0505dae0ba2b50a46637db34b46f9094c65c5d4800794ef6335010938", size = 12353708, upload-time = "2026-05-11T18:52:28.139Z" }, - { url = "https://files.pythonhosted.org/packages/eb/62/c321f13b5ba1819fc8dca456c7fce578da2dcfecff1abbf0eaddf8406c0f/pandas-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:6674ab18ad8c57802867264b00e15e7bb904700cdd9046e3b2fa1fce237439ea", size = 9907609, upload-time = "2026-05-11T18:52:30.982Z" }, - { url = "https://files.pythonhosted.org/packages/53/85/1b7f563ebc6357c27233a02a96b589bcce1fa9c6eb89fb4f0e56421d277e/pandas-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:5cc09a68b3120e0f54870dede8287a7bb1fa463907e4fcec1ea77cab6179bf7a", size = 9165596, upload-time = "2026-05-11T18:52:33.334Z" }, - { url = "https://files.pythonhosted.org/packages/24/f1/392f8c5bfc16f66a0d2d41561c01627c228fe7ed2a0d056ef11315042570/pandas-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fed2ff7fd9779120e388e285fc029bd5cf9490cdd2e4166a9ee22c0e49a9ab09", size = 10357846, upload-time = "2026-05-11T18:52:36.143Z" }, - { url = "https://files.pythonhosted.org/packages/cf/3d/b16412745651e855f357e5e66930248688378853a6e2698a214e331fba1f/pandas-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b168fc218fd80a6cbdbdbc1a97ddc7889ed057d7eb45f50d866ceab5f39904c4", size = 9899550, upload-time = "2026-05-11T18:52:38.976Z" }, - { url = "https://files.pythonhosted.org/packages/31/a8/fa2535168fffcedf67f4f6de28d2dd903a747ca7c8ea6989451aaeb3a92f/pandas-3.0.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0383c72c75cdcca61a9e116e611143902dbfd08bff356829c2f6d1cf40a9ca8c", size = 10412965, upload-time = "2026-05-11T18:52:41.915Z" }, - { url = "https://files.pythonhosted.org/packages/65/b6/09b01cdbc15224e2850365192d17b7bdebb8bdbd8780ed221fcdf0d9a515/pandas-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6dc0b3fd2169c9157deed50b4d519553a3655c8c6a96027136d654592be973a9", size = 10894600, upload-time = "2026-05-11T18:52:45.02Z" }, - { url = "https://files.pythonhosted.org/packages/c9/a4/2eb28f2fccb4ced4a2c79ab2a5dee9ade1ebf44922ebad6fea158c9f95d4/pandas-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7e65d5407dc0b394f509699650e4a2ec01c0514f21850f453fa60f3be79a5dbf", size = 11422824, upload-time = "2026-05-11T18:52:48.058Z" }, - { url = "https://files.pythonhosted.org/packages/f8/45/830bb57f533a4604b355e07edcb8ea18cf88b5f94e5fca92f27052d7c597/pandas-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f8894dc474d648fe7b6ff0ca9b0bd73950d19952bc1a6534540762c5d79d305c", size = 11950889, upload-time = "2026-05-11T18:52:50.905Z" }, - { url = "https://files.pythonhosted.org/packages/b9/c5/fc1b368f303087d20e8c9bf3d6ceb186263cfac0ade735cd938538bea839/pandas-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:c7be265b62cef88e253a941e4698604973736dcfe242fdb5198f0f7bc473cdcc", size = 9755463, upload-time = "2026-05-11T18:52:53.386Z" }, - { url = "https://files.pythonhosted.org/packages/86/bd/fda8f9705b1b09c6ebe14bfc0fa0e4ec8584d54ea673628f157ff55131af/pandas-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:557409bc4178e70ee8d9ddb494798e51ebf6ea59330f6be22c51bab2a7db6c49", size = 9066158, upload-time = "2026-05-11T18:52:56.038Z" }, - { url = "https://files.pythonhosted.org/packages/c5/90/62d8302883c44308c477e222c3daf7c813a34c8e96985882fbd53d964352/pandas-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:67b3b64c11910cfa29f4e94a14d3bff9ee693b6fc76055e7cad549cee0aec5fa", size = 10331071, upload-time = "2026-05-11T18:52:58.838Z" }, - { url = "https://files.pythonhosted.org/packages/7f/ae/6a6493c783a101f165e4356953ba3c74d6f77f0042fa7d753da9dfbb640c/pandas-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:39436b377d56d2a2e52d0395bdbee171f01068e99af5250509aceeb929f765c7", size = 9875690, upload-time = "2026-05-11T18:53:01.431Z" }, - { url = "https://files.pythonhosted.org/packages/62/7c/5df8e9f56c69a2769fbe9382a5ef8f2658c007e376434e1e2cbb57ad895f/pandas-3.0.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4be06d68f9ddcfc645b87534911da79a8fbffc7573c80e0edcf42a5020624d8", size = 10381634, upload-time = "2026-05-11T18:53:04.393Z" }, - { url = "https://files.pythonhosted.org/packages/99/68/1237369725aa617bb358263d535803e3053fdbc593513ec5ed9c9896b5b6/pandas-3.0.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a4eeb6830daf35a71cc09649bd823e2b542dac246cdee9614c6e4bd65028cd6a", size = 10891243, upload-time = "2026-05-11T18:53:07.643Z" }, - { url = "https://files.pythonhosted.org/packages/25/93/77d108e8af7222b4a503ebde0e30215b1c2e4f8e53a526431890f22d5586/pandas-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1928e07221f82db493cd4af1e23c1bfca524a19a4699887975bff68f49a72bfb", size = 11388659, upload-time = "2026-05-11T18:53:10.634Z" }, - { url = "https://files.pythonhosted.org/packages/d0/bd/eff5b4399f332ac386c853f6cd2bd3fa2ca0061b9f36ecd9c4d7c4265649/pandas-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51b1fe551acb77dac643c6fda86084d8d446c10fe64b06a9cc29c4cc8540e7f2", size = 11942880, upload-time = "2026-05-11T18:53:13.536Z" }, - { url = "https://files.pythonhosted.org/packages/2c/20/559ace4200982c3887d0b86bfd0d856a2143ef8ddab63cc07934951a964c/pandas-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:a82d532a3351d435432cd913edbccaf8b8e01d4dd0e5ced5a8d2e8ecd94c7e44", size = 9757091, upload-time = "2026-05-11T18:53:16.306Z" }, - { url = "https://files.pythonhosted.org/packages/3a/66/69055a09fe200f29f922a3eeec4804611900b95f52d932ece3393c3c0c19/pandas-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:275c14e0fce14a2ec20eee474aecd305478ea3c1e6f6a9d8fe219a165542717e", size = 9057282, upload-time = "2026-05-11T18:53:18.768Z" }, - { url = "https://files.pythonhosted.org/packages/57/0e/efe801b0e6811e8e650cd21b7f2608e30f08a7067e2bf6e8752b0d56ee3c/pandas-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:46997386d528eb40376ecd6b033cf4a8a1e5282580f68f43de875b78cba2199d", size = 10767016, upload-time = "2026-05-11T18:53:21.227Z" }, - { url = "https://files.pythonhosted.org/packages/ea/dc/eb55135a1d5f0f0519f28da1f609a206d2cad1f9c35c32d51e38dd7261ae/pandas-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:261e308dfb22448384b7580cf719d2f998fe2966c92893c3e77d14008af1f066", size = 10420210, upload-time = "2026-05-11T18:53:23.982Z" }, - { url = "https://files.pythonhosted.org/packages/c6/3e/b1d5d955ce33ffecb407465a60bc32769d74fcf68224b7ae67ae11d4dea4/pandas-3.0.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dd1a5d1def6a46002e964510bdc67c368aa0951df5d1d9f8365336f5a1f490cd", size = 10336126, upload-time = "2026-05-11T18:53:26.731Z" }, - { url = "https://files.pythonhosted.org/packages/f5/76/a01261711ab60a22d71b862f0de20e4c504bf80457270ad8cb42110f6abc/pandas-3.0.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d72828c20c6d6e83e1e22a6a3b47b326b71664112fa9705dcbccfd7a39b62085", size = 10728051, upload-time = "2026-05-11T18:53:29.125Z" }, - { url = "https://files.pythonhosted.org/packages/e9/21/ea191195e587b18cf682e97f433f81b2d0fbe341380e80a3e0d6e4403c8e/pandas-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d26cbe1fcfc12e8fd900e2454163e466b2d3af84f7c75481df7683ffc073d870", size = 11350796, upload-time = "2026-05-11T18:53:32.056Z" }, - { url = "https://files.pythonhosted.org/packages/64/69/f0eaaf54939f0e8c6768fd06be9af2cef9b36048b96dfb9e1b2c685a807e/pandas-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3e91cec1879ada0624fc3dc9953c5cbd60208e59c0db28f540c5d6d47502422f", size = 11799741, upload-time = "2026-05-11T18:53:34.985Z" }, - { url = "https://files.pythonhosted.org/packages/45/a4/865e0e510cae5fc2194de4db28be638952de942571ba9125934fd9c01d47/pandas-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:08d789b41f87e0905880e293cedf6197ce71fe67cc081358b1e148a491b9bd13", size = 10499958, upload-time = "2026-05-11T18:53:37.857Z" }, - { url = "https://files.pythonhosted.org/packages/86/54/effdcc3c0ff7a08037889200e148ebe94c16c4f653be078c7b3675955df1/pandas-3.0.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3650109c0f22879df8bd6179ab9ee3d7f1d1d4e7e0094a3f0032d9f51e2e64ac", size = 10336065, upload-time = "2026-05-11T18:53:41.099Z" }, - { url = "https://files.pythonhosted.org/packages/68/10/bf2d6738d72748b961a3751ab89522d58c54efc36a8e1a12161216cd45cf/pandas-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:bab900348131a7db1f69a7309ef141fd5680f1487094193bcbbb61791573bf8f", size = 9926101, upload-time = "2026-05-11T18:53:43.515Z" }, - { url = "https://files.pythonhosted.org/packages/ae/e9/e35cf11c8a136e757b956f5f0efdcaa50aecde85ea055f1898dfc68262f3/pandas-3.0.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba7e08b9ac1d54569cd1e256e3668975ed624d6826f7b68df0342b012007bddb", size = 10457553, upload-time = "2026-05-11T18:53:46.394Z" }, - { url = "https://files.pythonhosted.org/packages/58/3b/1cdec6772bdbaf7b25dab360c59f03cadf05492dd724c6540af905389b07/pandas-3.0.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d71c63ae4ebdbf70209742096f1fc46a83a0613c99d4b23766cced9ff8cd62a", size = 10914065, upload-time = "2026-05-11T18:53:49.134Z" }, - { url = "https://files.pythonhosted.org/packages/c4/c2/1ef644445fcd72e3627bceec77e3560636f87ddce4ed841afe76b83b5bf9/pandas-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e3a2ec42c98ffa2565a67e08e218d06d72576d758d90facb7c00805194d8f360", size = 11459188, upload-time = "2026-05-11T18:53:52.527Z" }, - { url = "https://files.pythonhosted.org/packages/7e/49/4d8d4f42cbc9c4adc7a1870f269c02cbd6cd40d059622c06fb298addcbad/pandas-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:335f62418ed562cfc3c49e9e196375c28b729dcef8543abf4f9438e381bf3c76", size = 11982966, upload-time = "2026-05-11T18:53:55.043Z" }, - { url = "https://files.pythonhosted.org/packages/38/55/792619469bab9882d8bbd5865d45a72f6478762d04a9af4bf0d08c503e95/pandas-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:3c20a521bbb85902f79f7270c80a59e1b5452d96d170c034f207181870f97ac5", size = 9876755, upload-time = "2026-05-11T18:53:58.067Z" }, - { url = "https://files.pythonhosted.org/packages/2a/af/33c469653b0ba03b50c3a98192d4c07f0c75c66b263ceb097fce0ee97d31/pandas-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:a2d2dff8a04f3917b55ab3910c32990f8ddf7eceba114947838cefa976a68977", size = 9198658, upload-time = "2026-05-11T18:54:00.733Z" }, - { url = "https://files.pythonhosted.org/packages/a2/fa/b8c257bd76b8bd060c3a9151c1fca05e9b9c5e3af5d0f549c0356f6d143d/pandas-3.0.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:0d589105b3c14645af1738ff279b2995102d8f7a03b0a66dc8d95550eb513e04", size = 10787242, upload-time = "2026-05-11T18:54:03.564Z" }, - { url = "https://files.pythonhosted.org/packages/54/eb/f19206ffb0bf1919002969aa448b4702c6594845156a6f8050674855aac3/pandas-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:13fc1e853d9e04743d11ba75a985ccbc2a317fe07d8af61e445a6fd24dacd6a6", size = 10436369, upload-time = "2026-05-11T18:54:06.311Z" }, - { url = "https://files.pythonhosted.org/packages/fd/24/c7c39fb4fe22b71a0c2d78bf0c585c600092d85f94f086d2b3b2f6ca27e2/pandas-3.0.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:819959dab7bbd0049c15623fbac4e29a191b9528160a61fb1032242d8ced2d9c", size = 10358306, upload-time = "2026-05-11T18:54:09.085Z" }, - { url = "https://files.pythonhosted.org/packages/16/ec/dd2a9eb7fa1204df88c0864164e35b228ac581062ac612ba0a67fd812e4c/pandas-3.0.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:60ae316d3fd75d1858d450d0db0103ea2be3e7d4a95ec2f064f7e2ae63f7b028", size = 10758394, upload-time = "2026-05-11T18:54:11.956Z" }, - { url = "https://files.pythonhosted.org/packages/95/6e/00c61ea8e85b4f6d8d35e11852a1a4998fc7fafc91c6a602d1cc9c972d64/pandas-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bd3a518890b400d32f9023722dc9a9a5c969f00b415419a3c06c043f09bb5d7d", size = 11375717, upload-time = "2026-05-11T18:54:14.539Z" }, - { url = "https://files.pythonhosted.org/packages/31/89/8fc1c268969fac43688d65fd92e67df24bd128d53cb4d2eee534cd307399/pandas-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9c39be2d709d01fa972a0cabc522389fceca4f3969332ba25a7d6c5802cf976a", size = 11828897, upload-time = "2026-05-11T18:54:17.146Z" }, - { url = "https://files.pythonhosted.org/packages/56/3b/e7d20dea247a3e6dc0bd8a6953854afbedc03951def4e7371e05e7263e25/pandas-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4db8c527972a821cf5286b40ccc57642a39bc62e62022b42f99f8a67fca8c3a1", size = 10900855, upload-time = "2026-05-11T18:54:19.72Z" }, - { url = "https://files.pythonhosted.org/packages/0f/54/68a0978d1ef8502b8492099beaa6e7a0c1b32e3b5d4f677f5810cb08711c/pandas-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b2c95f8bfc1ee412bf482605d7bfd30c12d1d26bd59fdd91efeef1d4718decb1", size = 9466464, upload-time = "2026-05-11T18:54:22.754Z" }, -] - -[[package]] -name = "parameterized" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ea/49/00c0c0cc24ff4266025a53e41336b79adaa5a4ebfad214f433d623f9865e/parameterized-0.9.0.tar.gz", hash = "sha256:7fc905272cefa4f364c1a3429cbbe9c0f98b793988efb5bf90aac80f08db09b1", size = 24351, upload-time = "2023-03-27T02:01:11.592Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/00/2f/804f58f0b856ab3bf21617cccf5b39206e6c4c94c2cd227bde125ea6105f/parameterized-0.9.0-py2.py3-none-any.whl", hash = "sha256:4e0758e3d41bea3bbd05ec14fc2c24736723f243b28d702081aef438c9372b1b", size = 20475, upload-time = "2023-03-27T02:01:09.31Z" }, -] - -[[package]] -name = "pillow" -version = "12.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819, upload-time = "2026-04-01T14:46:17.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/aa/d0b28e1c811cd4d5f5c2bfe2e022292bd255ae5744a3b9ac7d6c8f72dd75/pillow-12.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:a4e8f36e677d3336f35089648c8955c51c6d386a13cf6ee9c189c5f5bd713a9f", size = 5354355, upload-time = "2026-04-01T14:42:15.402Z" }, - { url = "https://files.pythonhosted.org/packages/27/8e/1d5b39b8ae2bd7650d0c7b6abb9602d16043ead9ebbfef4bc4047454da2a/pillow-12.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e589959f10d9824d39b350472b92f0ce3b443c0a3442ebf41c40cb8361c5b97", size = 4695871, upload-time = "2026-04-01T14:42:18.234Z" }, - { url = "https://files.pythonhosted.org/packages/f0/c5/dcb7a6ca6b7d3be41a76958e90018d56c8462166b3ef223150360850c8da/pillow-12.2.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a52edc8bfff4429aaabdf4d9ee0daadbbf8562364f940937b941f87a4290f5ff", size = 6269734, upload-time = "2026-04-01T14:42:20.608Z" }, - { url = "https://files.pythonhosted.org/packages/ea/f1/aa1bb13b2f4eba914e9637893c73f2af8e48d7d4023b9d3750d4c5eb2d0c/pillow-12.2.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:975385f4776fafde056abb318f612ef6285b10a1f12b8570f3647ad0d74b48ec", size = 8076080, upload-time = "2026-04-01T14:42:23.095Z" }, - { url = "https://files.pythonhosted.org/packages/a1/2a/8c79d6a53169937784604a8ae8d77e45888c41537f7f6f65ed1f407fe66d/pillow-12.2.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd9c0c7a0c681a347b3194c500cb1e6ca9cab053ea4d82a5cf45b6b754560136", size = 6382236, upload-time = "2026-04-01T14:42:25.82Z" }, - { url = "https://files.pythonhosted.org/packages/b5/42/bbcb6051030e1e421d103ce7a8ecadf837aa2f39b8f82ef1a8d37c3d4ebc/pillow-12.2.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:88d387ff40b3ff7c274947ed3125dedf5262ec6919d83946753b5f3d7c67ea4c", size = 7070220, upload-time = "2026-04-01T14:42:28.68Z" }, - { url = "https://files.pythonhosted.org/packages/3f/e1/c2a7d6dd8cfa6b231227da096fd2d58754bab3603b9d73bf609d3c18b64f/pillow-12.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:51c4167c34b0d8ba05b547a3bb23578d0ba17b80a5593f93bd8ecb123dd336a3", size = 6493124, upload-time = "2026-04-01T14:42:31.579Z" }, - { url = "https://files.pythonhosted.org/packages/5f/41/7c8617da5d32e1d2f026e509484fdb6f3ad7efaef1749a0c1928adbb099e/pillow-12.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:34c0d99ecccea270c04882cb3b86e7b57296079c9a4aff88cb3b33563d95afaa", size = 7194324, upload-time = "2026-04-01T14:42:34.615Z" }, - { url = "https://files.pythonhosted.org/packages/2d/de/a777627e19fd6d62f84070ee1521adde5eeda4855b5cf60fe0b149118bca/pillow-12.2.0-cp310-cp310-win32.whl", hash = "sha256:b85f66ae9eb53e860a873b858b789217ba505e5e405a24b85c0464822fe88032", size = 6376363, upload-time = "2026-04-01T14:42:37.19Z" }, - { url = "https://files.pythonhosted.org/packages/e7/34/fc4cb5204896465842767b96d250c08410f01f2f28afc43b257de842eed5/pillow-12.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:673aa32138f3e7531ccdbca7b3901dba9b70940a19ccecc6a37c77d5fdeb05b5", size = 7083523, upload-time = "2026-04-01T14:42:39.62Z" }, - { url = "https://files.pythonhosted.org/packages/2d/a0/32852d36bc7709f14dc3f64f929a275e958ad8c19a6deba9610d458e28b3/pillow-12.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:3e080565d8d7c671db5802eedfb438e5565ffa40115216eabb8cd52d0ecce024", size = 2463318, upload-time = "2026-04-01T14:42:42.063Z" }, - { url = "https://files.pythonhosted.org/packages/68/e1/748f5663efe6edcfc4e74b2b93edfb9b8b99b67f21a854c3ae416500a2d9/pillow-12.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:8be29e59487a79f173507c30ddf57e733a357f67881430449bb32614075a40ab", size = 5354347, upload-time = "2026-04-01T14:42:44.255Z" }, - { url = "https://files.pythonhosted.org/packages/47/a1/d5ff69e747374c33a3b53b9f98cca7889fce1fd03d79cdc4e1bccc6c5a87/pillow-12.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:71cde9a1e1551df7d34a25462fc60325e8a11a82cc2e2f54578e5e9a1e153d65", size = 4695873, upload-time = "2026-04-01T14:42:46.452Z" }, - { url = "https://files.pythonhosted.org/packages/df/21/e3fbdf54408a973c7f7f89a23b2cb97a7ef30c61ab4142af31eee6aebc88/pillow-12.2.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f490f9368b6fc026f021db16d7ec2fbf7d89e2edb42e8ec09d2c60505f5729c7", size = 6280168, upload-time = "2026-04-01T14:42:49.228Z" }, - { url = "https://files.pythonhosted.org/packages/d3/f1/00b7278c7dd52b17ad4329153748f87b6756ec195ff786c2bdf12518337d/pillow-12.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8bd7903a5f2a4545f6fd5935c90058b89d30045568985a71c79f5fd6edf9b91e", size = 8088188, upload-time = "2026-04-01T14:42:51.735Z" }, - { url = "https://files.pythonhosted.org/packages/ad/cf/220a5994ef1b10e70e85748b75649d77d506499352be135a4989c957b701/pillow-12.2.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3997232e10d2920a68d25191392e3a4487d8183039e1c74c2297f00ed1c50705", size = 6394401, upload-time = "2026-04-01T14:42:54.343Z" }, - { url = "https://files.pythonhosted.org/packages/e9/bd/e51a61b1054f09437acfbc2ff9106c30d1eb76bc1453d428399946781253/pillow-12.2.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e74473c875d78b8e9d5da2a70f7099549f9eb37ded4e2f6a463e60125bccd176", size = 7079655, upload-time = "2026-04-01T14:42:56.954Z" }, - { url = "https://files.pythonhosted.org/packages/6b/3d/45132c57d5fb4b5744567c3817026480ac7fc3ce5d4c47902bc0e7f6f853/pillow-12.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:56a3f9c60a13133a98ecff6197af34d7824de9b7b38c3654861a725c970c197b", size = 6503105, upload-time = "2026-04-01T14:42:59.847Z" }, - { url = "https://files.pythonhosted.org/packages/7d/2e/9df2fc1e82097b1df3dce58dc43286aa01068e918c07574711fcc53e6fb4/pillow-12.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:90e6f81de50ad6b534cab6e5aef77ff6e37722b2f5d908686f4a5c9eba17a909", size = 7203402, upload-time = "2026-04-01T14:43:02.664Z" }, - { url = "https://files.pythonhosted.org/packages/bd/2e/2941e42858ebb67e50ae741473de81c2984e6eff7b397017623c676e2e8d/pillow-12.2.0-cp311-cp311-win32.whl", hash = "sha256:8c984051042858021a54926eb597d6ee3012393ce9c181814115df4c60b9a808", size = 6378149, upload-time = "2026-04-01T14:43:05.274Z" }, - { url = "https://files.pythonhosted.org/packages/69/42/836b6f3cd7f3e5fa10a1f1a5420447c17966044c8fbf589cc0452d5502db/pillow-12.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e6b2a0c538fc200b38ff9eb6628228b77908c319a005815f2dde585a0664b60", size = 7082626, upload-time = "2026-04-01T14:43:08.557Z" }, - { url = "https://files.pythonhosted.org/packages/c2/88/549194b5d6f1f494b485e493edc6693c0a16f4ada488e5bd974ed1f42fad/pillow-12.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:9a8a34cc89c67a65ea7437ce257cea81a9dad65b29805f3ecee8c8fe8ff25ffe", size = 2463531, upload-time = "2026-04-01T14:43:10.743Z" }, - { url = "https://files.pythonhosted.org/packages/58/be/7482c8a5ebebbc6470b3eb791812fff7d5e0216c2be3827b30b8bb6603ed/pillow-12.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2d192a155bbcec180f8564f693e6fd9bccff5a7af9b32e2e4bf8c9c69dbad6b5", size = 5308279, upload-time = "2026-04-01T14:43:13.246Z" }, - { url = "https://files.pythonhosted.org/packages/d8/95/0a351b9289c2b5cbde0bacd4a83ebc44023e835490a727b2a3bd60ddc0f4/pillow-12.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421", size = 4695490, upload-time = "2026-04-01T14:43:15.584Z" }, - { url = "https://files.pythonhosted.org/packages/de/af/4e8e6869cbed569d43c416fad3dc4ecb944cb5d9492defaed89ddd6fe871/pillow-12.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:03e7e372d5240cc23e9f07deca4d775c0817bffc641b01e9c3af208dbd300987", size = 6284462, upload-time = "2026-04-01T14:43:18.268Z" }, - { url = "https://files.pythonhosted.org/packages/e9/9e/c05e19657fd57841e476be1ab46c4d501bffbadbafdc31a6d665f8b737b6/pillow-12.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b86024e52a1b269467a802258c25521e6d742349d760728092e1bc2d135b4d76", size = 8094744, upload-time = "2026-04-01T14:43:20.716Z" }, - { url = "https://files.pythonhosted.org/packages/2b/54/1789c455ed10176066b6e7e6da1b01e50e36f94ba584dc68d9eebfe9156d/pillow-12.2.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7371b48c4fa448d20d2714c9a1f775a81155050d383333e0a6c15b1123dda005", size = 6398371, upload-time = "2026-04-01T14:43:23.443Z" }, - { url = "https://files.pythonhosted.org/packages/43/e3/fdc657359e919462369869f1c9f0e973f353f9a9ee295a39b1fea8ee1a77/pillow-12.2.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62f5409336adb0663b7caa0da5c7d9e7bdbaae9ce761d34669420c2a801b2780", size = 7087215, upload-time = "2026-04-01T14:43:26.758Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f8/2f6825e441d5b1959d2ca5adec984210f1ec086435b0ed5f52c19b3b8a6e/pillow-12.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:01afa7cf67f74f09523699b4e88c73fb55c13346d212a59a2db1f86b0a63e8c5", size = 6509783, upload-time = "2026-04-01T14:43:29.56Z" }, - { url = "https://files.pythonhosted.org/packages/67/f9/029a27095ad20f854f9dba026b3ea6428548316e057e6fc3545409e86651/pillow-12.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5", size = 7212112, upload-time = "2026-04-01T14:43:32.091Z" }, - { url = "https://files.pythonhosted.org/packages/be/42/025cfe05d1be22dbfdb4f264fe9de1ccda83f66e4fc3aac94748e784af04/pillow-12.2.0-cp312-cp312-win32.whl", hash = "sha256:58f62cc0f00fd29e64b29f4fd923ffdb3859c9f9e6105bfc37ba1d08994e8940", size = 6378489, upload-time = "2026-04-01T14:43:34.601Z" }, - { url = "https://files.pythonhosted.org/packages/5d/7b/25a221d2c761c6a8ae21bfa3874988ff2583e19cf8a27bf2fee358df7942/pillow-12.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:7f84204dee22a783350679a0333981df803dac21a0190d706a50475e361c93f5", size = 7084129, upload-time = "2026-04-01T14:43:37.213Z" }, - { url = "https://files.pythonhosted.org/packages/10/e1/542a474affab20fd4a0f1836cb234e8493519da6b76899e30bcc5d990b8b/pillow-12.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:af73337013e0b3b46f175e79492d96845b16126ddf79c438d7ea7ff27783a414", size = 2463612, upload-time = "2026-04-01T14:43:39.421Z" }, - { url = "https://files.pythonhosted.org/packages/4a/01/53d10cf0dbad820a8db274d259a37ba50b88b24768ddccec07355382d5ad/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c", size = 4100837, upload-time = "2026-04-01T14:43:41.506Z" }, - { url = "https://files.pythonhosted.org/packages/0f/98/f3a6657ecb698c937f6c76ee564882945f29b79bad496abcba0e84659ec5/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2", size = 4176528, upload-time = "2026-04-01T14:43:43.773Z" }, - { url = "https://files.pythonhosted.org/packages/69/bc/8986948f05e3ea490b8442ea1c1d4d990b24a7e43d8a51b2c7d8b1dced36/pillow-12.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c", size = 3640401, upload-time = "2026-04-01T14:43:45.87Z" }, - { url = "https://files.pythonhosted.org/packages/34/46/6c717baadcd62bc8ed51d238d521ab651eaa74838291bda1f86fe1f864c9/pillow-12.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795", size = 5308094, upload-time = "2026-04-01T14:43:48.438Z" }, - { url = "https://files.pythonhosted.org/packages/71/43/905a14a8b17fdb1ccb58d282454490662d2cb89a6bfec26af6d3520da5ec/pillow-12.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f", size = 4695402, upload-time = "2026-04-01T14:43:51.292Z" }, - { url = "https://files.pythonhosted.org/packages/73/dd/42107efcb777b16fa0393317eac58f5b5cf30e8392e266e76e51cff28c3d/pillow-12.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed", size = 6280005, upload-time = "2026-04-01T14:43:54.242Z" }, - { url = "https://files.pythonhosted.org/packages/a8/68/b93e09e5e8549019e61acf49f65b1a8530765a7f812c77a7461bca7e4494/pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9", size = 8090669, upload-time = "2026-04-01T14:43:57.335Z" }, - { url = "https://files.pythonhosted.org/packages/4b/6e/3ccb54ce8ec4ddd1accd2d89004308b7b0b21c4ac3d20fa70af4760a4330/pillow-12.2.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed", size = 6395194, upload-time = "2026-04-01T14:43:59.864Z" }, - { url = "https://files.pythonhosted.org/packages/67/ee/21d4e8536afd1a328f01b359b4d3997b291ffd35a237c877b331c1c3b71c/pillow-12.2.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3", size = 7082423, upload-time = "2026-04-01T14:44:02.74Z" }, - { url = "https://files.pythonhosted.org/packages/78/5f/e9f86ab0146464e8c133fe85df987ed9e77e08b29d8d35f9f9f4d6f917ba/pillow-12.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9", size = 6505667, upload-time = "2026-04-01T14:44:05.381Z" }, - { url = "https://files.pythonhosted.org/packages/ed/1e/409007f56a2fdce61584fd3acbc2bbc259857d555196cedcadc68c015c82/pillow-12.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795", size = 7208580, upload-time = "2026-04-01T14:44:08.39Z" }, - { url = "https://files.pythonhosted.org/packages/23/c4/7349421080b12fb35414607b8871e9534546c128a11965fd4a7002ccfbee/pillow-12.2.0-cp313-cp313-win32.whl", hash = "sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e", size = 6375896, upload-time = "2026-04-01T14:44:11.197Z" }, - { url = "https://files.pythonhosted.org/packages/3f/82/8a3739a5e470b3c6cbb1d21d315800d8e16bff503d1f16b03a4ec3212786/pillow-12.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b", size = 7081266, upload-time = "2026-04-01T14:44:13.947Z" }, - { url = "https://files.pythonhosted.org/packages/c3/25/f968f618a062574294592f668218f8af564830ccebdd1fa6200f598e65c5/pillow-12.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06", size = 2463508, upload-time = "2026-04-01T14:44:16.312Z" }, - { url = "https://files.pythonhosted.org/packages/4d/a4/b342930964e3cb4dce5038ae34b0eab4653334995336cd486c5a8c25a00c/pillow-12.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b", size = 5309927, upload-time = "2026-04-01T14:44:18.89Z" }, - { url = "https://files.pythonhosted.org/packages/9f/de/23198e0a65a9cf06123f5435a5d95cea62a635697f8f03d134d3f3a96151/pillow-12.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f", size = 4698624, upload-time = "2026-04-01T14:44:21.115Z" }, - { url = "https://files.pythonhosted.org/packages/01/a6/1265e977f17d93ea37aa28aa81bad4fa597933879fac2520d24e021c8da3/pillow-12.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612", size = 6321252, upload-time = "2026-04-01T14:44:23.663Z" }, - { url = "https://files.pythonhosted.org/packages/3c/83/5982eb4a285967baa70340320be9f88e57665a387e3a53a7f0db8231a0cd/pillow-12.2.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c", size = 8126550, upload-time = "2026-04-01T14:44:26.772Z" }, - { url = "https://files.pythonhosted.org/packages/4e/48/6ffc514adce69f6050d0753b1a18fd920fce8cac87620d5a31231b04bfc5/pillow-12.2.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea", size = 6433114, upload-time = "2026-04-01T14:44:29.615Z" }, - { url = "https://files.pythonhosted.org/packages/36/a3/f9a77144231fb8d40ee27107b4463e205fa4677e2ca2548e14da5cf18dce/pillow-12.2.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4", size = 7115667, upload-time = "2026-04-01T14:44:32.773Z" }, - { url = "https://files.pythonhosted.org/packages/c1/fc/ac4ee3041e7d5a565e1c4fd72a113f03b6394cc72ab7089d27608f8aaccb/pillow-12.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4", size = 6538966, upload-time = "2026-04-01T14:44:35.252Z" }, - { url = "https://files.pythonhosted.org/packages/c0/a8/27fb307055087f3668f6d0a8ccb636e7431d56ed0750e07a60547b1e083e/pillow-12.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea", size = 7238241, upload-time = "2026-04-01T14:44:37.875Z" }, - { url = "https://files.pythonhosted.org/packages/ad/4b/926ab182c07fccae9fcb120043464e1ff1564775ec8864f21a0ebce6ac25/pillow-12.2.0-cp313-cp313t-win32.whl", hash = "sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24", size = 6379592, upload-time = "2026-04-01T14:44:40.336Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c4/f9e476451a098181b30050cc4c9a3556b64c02cf6497ea421ac047e89e4b/pillow-12.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98", size = 7085542, upload-time = "2026-04-01T14:44:43.251Z" }, - { url = "https://files.pythonhosted.org/packages/00/a4/285f12aeacbe2d6dc36c407dfbbe9e96d4a80b0fb710a337f6d2ad978c75/pillow-12.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453", size = 2465765, upload-time = "2026-04-01T14:44:45.996Z" }, - { url = "https://files.pythonhosted.org/packages/bf/98/4595daa2365416a86cb0d495248a393dfc84e96d62ad080c8546256cb9c0/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8", size = 4100848, upload-time = "2026-04-01T14:44:48.48Z" }, - { url = "https://files.pythonhosted.org/packages/0b/79/40184d464cf89f6663e18dfcf7ca21aae2491fff1a16127681bf1fa9b8cf/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b", size = 4176515, upload-time = "2026-04-01T14:44:51.353Z" }, - { url = "https://files.pythonhosted.org/packages/b0/63/703f86fd4c422a9cf722833670f4f71418fb116b2853ff7da722ea43f184/pillow-12.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295", size = 3640159, upload-time = "2026-04-01T14:44:53.588Z" }, - { url = "https://files.pythonhosted.org/packages/71/e0/fb22f797187d0be2270f83500aab851536101b254bfa1eae10795709d283/pillow-12.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed", size = 5312185, upload-time = "2026-04-01T14:44:56.039Z" }, - { url = "https://files.pythonhosted.org/packages/ba/8c/1a9e46228571de18f8e28f16fabdfc20212a5d019f3e3303452b3f0a580d/pillow-12.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae", size = 4695386, upload-time = "2026-04-01T14:44:58.663Z" }, - { url = "https://files.pythonhosted.org/packages/70/62/98f6b7f0c88b9addd0e87c217ded307b36be024d4ff8869a812b241d1345/pillow-12.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601", size = 6280384, upload-time = "2026-04-01T14:45:01.5Z" }, - { url = "https://files.pythonhosted.org/packages/5e/03/688747d2e91cfbe0e64f316cd2e8005698f76ada3130d0194664174fa5de/pillow-12.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be", size = 8091599, upload-time = "2026-04-01T14:45:04.5Z" }, - { url = "https://files.pythonhosted.org/packages/f6/35/577e22b936fcdd66537329b33af0b4ccfefaeabd8aec04b266528cddb33c/pillow-12.2.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f", size = 6396021, upload-time = "2026-04-01T14:45:07.117Z" }, - { url = "https://files.pythonhosted.org/packages/11/8d/d2532ad2a603ca2b93ad9f5135732124e57811d0168155852f37fbce2458/pillow-12.2.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286", size = 7083360, upload-time = "2026-04-01T14:45:09.763Z" }, - { url = "https://files.pythonhosted.org/packages/5e/26/d325f9f56c7e039034897e7380e9cc202b1e368bfd04d4cbe6a441f02885/pillow-12.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50", size = 6507628, upload-time = "2026-04-01T14:45:12.378Z" }, - { url = "https://files.pythonhosted.org/packages/5f/f7/769d5632ffb0988f1c5e7660b3e731e30f7f8ec4318e94d0a5d674eb65a4/pillow-12.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104", size = 7209321, upload-time = "2026-04-01T14:45:15.122Z" }, - { url = "https://files.pythonhosted.org/packages/6a/7a/c253e3c645cd47f1aceea6a8bacdba9991bf45bb7dfe927f7c893e89c93c/pillow-12.2.0-cp314-cp314-win32.whl", hash = "sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7", size = 6479723, upload-time = "2026-04-01T14:45:17.797Z" }, - { url = "https://files.pythonhosted.org/packages/cd/8b/601e6566b957ca50e28725cb6c355c59c2c8609751efbecd980db44e0349/pillow-12.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150", size = 7217400, upload-time = "2026-04-01T14:45:20.529Z" }, - { url = "https://files.pythonhosted.org/packages/d6/94/220e46c73065c3e2951bb91c11a1fb636c8c9ad427ac3ce7d7f3359b9b2f/pillow-12.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1", size = 2554835, upload-time = "2026-04-01T14:45:23.162Z" }, - { url = "https://files.pythonhosted.org/packages/b6/ab/1b426a3974cb0e7da5c29ccff4807871d48110933a57207b5a676cccc155/pillow-12.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463", size = 5314225, upload-time = "2026-04-01T14:45:25.637Z" }, - { url = "https://files.pythonhosted.org/packages/19/1e/dce46f371be2438eecfee2a1960ee2a243bbe5e961890146d2dee1ff0f12/pillow-12.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3", size = 4698541, upload-time = "2026-04-01T14:45:28.355Z" }, - { url = "https://files.pythonhosted.org/packages/55/c3/7fbecf70adb3a0c33b77a300dc52e424dc22ad8cdc06557a2e49523b703d/pillow-12.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166", size = 6322251, upload-time = "2026-04-01T14:45:30.924Z" }, - { url = "https://files.pythonhosted.org/packages/1c/3c/7fbc17cfb7e4fe0ef1642e0abc17fc6c94c9f7a16be41498e12e2ba60408/pillow-12.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe", size = 8127807, upload-time = "2026-04-01T14:45:33.908Z" }, - { url = "https://files.pythonhosted.org/packages/ff/c3/a8ae14d6defd2e448493ff512fae903b1e9bd40b72efb6ec55ce0048c8ce/pillow-12.2.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd", size = 6433935, upload-time = "2026-04-01T14:45:36.623Z" }, - { url = "https://files.pythonhosted.org/packages/6e/32/2880fb3a074847ac159d8f902cb43278a61e85f681661e7419e6596803ed/pillow-12.2.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e", size = 7116720, upload-time = "2026-04-01T14:45:39.258Z" }, - { url = "https://files.pythonhosted.org/packages/46/87/495cc9c30e0129501643f24d320076f4cc54f718341df18cc70ec94c44e1/pillow-12.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06", size = 6540498, upload-time = "2026-04-01T14:45:41.879Z" }, - { url = "https://files.pythonhosted.org/packages/18/53/773f5edca692009d883a72211b60fdaf8871cbef075eaa9d577f0a2f989e/pillow-12.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43", size = 7239413, upload-time = "2026-04-01T14:45:44.705Z" }, - { url = "https://files.pythonhosted.org/packages/c9/e4/4b64a97d71b2a83158134abbb2f5bd3f8a2ea691361282f010998f339ec7/pillow-12.2.0-cp314-cp314t-win32.whl", hash = "sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354", size = 6482084, upload-time = "2026-04-01T14:45:47.568Z" }, - { url = "https://files.pythonhosted.org/packages/ba/13/306d275efd3a3453f72114b7431c877d10b1154014c1ebbedd067770d629/pillow-12.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1", size = 7225152, upload-time = "2026-04-01T14:45:50.032Z" }, - { url = "https://files.pythonhosted.org/packages/ff/6e/cf826fae916b8658848d7b9f38d88da6396895c676e8086fc0988073aaf8/pillow-12.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb", size = 2556579, upload-time = "2026-04-01T14:45:52.529Z" }, - { url = "https://files.pythonhosted.org/packages/4e/b7/2437044fb910f499610356d1352e3423753c98e34f915252aafecc64889f/pillow-12.2.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0538bd5e05efec03ae613fd89c4ce0368ecd2ba239cc25b9f9be7ed426b0af1f", size = 5273969, upload-time = "2026-04-01T14:45:55.538Z" }, - { url = "https://files.pythonhosted.org/packages/f6/f4/8316e31de11b780f4ac08ef3654a75555e624a98db1056ecb2122d008d5a/pillow-12.2.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:394167b21da716608eac917c60aa9b969421b5dcbbe02ae7f013e7b85811c69d", size = 4659674, upload-time = "2026-04-01T14:45:58.093Z" }, - { url = "https://files.pythonhosted.org/packages/d4/37/664fca7201f8bb2aa1d20e2c3d5564a62e6ae5111741966c8319ca802361/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5d04bfa02cc2d23b497d1e90a0f927070043f6cbf303e738300532379a4b4e0f", size = 5288479, upload-time = "2026-04-01T14:46:01.141Z" }, - { url = "https://files.pythonhosted.org/packages/49/62/5b0ed78fce87346be7a5cfcfaaad91f6a1f98c26f86bdbafa2066c647ef6/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0c838a5125cee37e68edec915651521191cef1e6aa336b855f495766e77a366e", size = 7032230, upload-time = "2026-04-01T14:46:03.874Z" }, - { url = "https://files.pythonhosted.org/packages/c3/28/ec0fc38107fc32536908034e990c47914c57cd7c5a3ece4d8d8f7ffd7e27/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a6c9fa44005fa37a91ebfc95d081e8079757d2e904b27103f4f5fa6f0bf78c0", size = 5355404, upload-time = "2026-04-01T14:46:06.33Z" }, - { url = "https://files.pythonhosted.org/packages/5e/8b/51b0eddcfa2180d60e41f06bd6d0a62202b20b59c68f5a132e615b75aecf/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:25373b66e0dd5905ed63fa3cae13c82fbddf3079f2c8bf15c6fb6a35586324c1", size = 6002215, upload-time = "2026-04-01T14:46:08.83Z" }, - { url = "https://files.pythonhosted.org/packages/bc/60/5382c03e1970de634027cee8e1b7d39776b778b81812aaf45b694dfe9e28/pillow-12.2.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:bfa9c230d2fe991bed5318a5f119bd6780cda2915cca595393649fc118ab895e", size = 7080946, upload-time = "2026-04-01T14:46:11.734Z" }, -] - -[[package]] -name = "plotly" -version = "6.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "narwhals" }, - { name = "packaging" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3a/7f/0f100df1172aadf88a929a9dbb902656b0880ba4b960fe5224867159d8f4/plotly-6.7.0.tar.gz", hash = "sha256:45eea0ff27e2a23ccd62776f77eb43aa1ca03df4192b76036e380bb479b892c6", size = 6911286, upload-time = "2026-04-09T20:36:45.738Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/90/ad/cba91b3bcf04073e4d1655a5c1710ef3f457f56f7d1b79dcc3d72f4dd912/plotly-6.7.0-py3-none-any.whl", hash = "sha256:ac8aca1c25c663a59b5b9140a549264a5badde2e057d79b8c772ae2920e32ff0", size = 9898444, upload-time = "2026-04-09T20:36:39.812Z" }, -] - -[[package]] -name = "pluggy" -version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, -] - -[[package]] -name = "plum-dispatch" -version = "2.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beartype" }, - { name = "rich" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1b/b7/84146ae5ff6c40d11357acdb36aafe3db7e104de01c1026d8e1b0ce3e7f1/plum_dispatch-2.9.0.tar.gz", hash = "sha256:fb45c5b2dd4dadd57def51bcf321dfa3a258df5c725f43adea7e7f6db3b79b52", size = 244502, upload-time = "2026-04-28T08:38:34.442Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/80/e1/8fa7fffff5699fa9d7aae2c57de27f2b21a242189f8c102c80a0961dcc89/plum_dispatch-2.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6c5f5dfd1afd42dfd738c1b7701b9e03279c52b4952d6bb56e169564b1d48eb9", size = 174089, upload-time = "2026-04-28T08:38:15.801Z" }, - { url = "https://files.pythonhosted.org/packages/33/91/a2879973fd40c985aeb55ffe522d7b91c10062f82b251b9ad202f063a3e8/plum_dispatch-2.9.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7cec06a2f2545a2b09e8dde81be6f1512e1df850b3ab2fbf582ef79a10956b23", size = 206109, upload-time = "2026-04-28T08:38:17.219Z" }, - { url = "https://files.pythonhosted.org/packages/e2/3a/578642ace42b78507a98e8b27ad7d8554d197b7ec67d28eab6806b0a0fc0/plum_dispatch-2.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:7e767a7e52f7d4cb3f10010024047d043c29d7ed0ab7df63bceef5371c44a2fa", size = 152556, upload-time = "2026-04-28T08:38:18.51Z" }, - { url = "https://files.pythonhosted.org/packages/e0/20/7aa36a01b1689d427a9fc20c80b1c0bc8340d1413c8071f08fb1e9a01939/plum_dispatch-2.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:25f62c2209b7bff00c6cdc25fe153ac09df304d7d833fab2852fc46fffbb7e87", size = 172039, upload-time = "2026-04-28T08:38:19.985Z" }, - { url = "https://files.pythonhosted.org/packages/63/01/eee83f936500e3e0100ac421483fa78768fec70214e4ce0241187ec3a2f1/plum_dispatch-2.9.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4d34cab84908abbdff501e356296eba960ff5d5fc04668b71ebc6f9ff84fa2f", size = 203364, upload-time = "2026-04-28T08:38:21.431Z" }, - { url = "https://files.pythonhosted.org/packages/d8/05/78c229ccce36fbd6dbc1cde698baae57313515ff1bff6ebad28be46c9386/plum_dispatch-2.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:1050150b6ae3600ac19a406b23cb7d0707825b227f608a54b8be9994d1f4b710", size = 152110, upload-time = "2026-04-28T08:38:22.975Z" }, - { url = "https://files.pythonhosted.org/packages/82/d6/fc6591336731b5e291319ec3c43d81cd6cc7940c9079183b333b85ed4d77/plum_dispatch-2.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:39a325b0b041fad687271d71abc11b5271d5a3b62af2c6b1271f4ececc3b59de", size = 175166, upload-time = "2026-04-28T08:38:24.467Z" }, - { url = "https://files.pythonhosted.org/packages/4c/4d/5418649397b477ae08839564c97596ede5ef36523147899bb913bbe959d1/plum_dispatch-2.9.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d39b20a1af776a39e4a0833e97c6258d938e5d874c7f3537bb7d772c39718c1e", size = 205988, upload-time = "2026-04-28T08:38:25.838Z" }, - { url = "https://files.pythonhosted.org/packages/e1/a5/28a6c4e45c4465a6e1a30d65206e913c217690752dc3c9261d78a9187aed/plum_dispatch-2.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:beb9b92c6994404a0d2dc83c857263f5da49519575e5cbb8a13060652746c44c", size = 152563, upload-time = "2026-04-28T08:38:27.37Z" }, - { url = "https://files.pythonhosted.org/packages/cb/18/19e01dbbaececb75815abb37bc71e10d87a7561829b4eae47ec3b342c94f/plum_dispatch-2.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:78987caf558fbdd675a7a4a5c144069f1f9524610195123fb7d0f0234d0af01a", size = 174490, upload-time = "2026-04-28T08:38:28.881Z" }, - { url = "https://files.pythonhosted.org/packages/fc/29/7b3de57cd86430fd00d71bc5a8a078bf2c63d4ada7fdfb30853fe976a19f/plum_dispatch-2.9.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:36662d76ae6d87aaa041fade036d2242f0b88b68c9fb4c96b7ba0026320065d3", size = 204734, upload-time = "2026-04-28T08:38:30.398Z" }, - { url = "https://files.pythonhosted.org/packages/ba/98/23d19326af0da251bb98a39f517ab6da983e5aad5eb9cc398b60933df2cc/plum_dispatch-2.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:a0d16daf09482a6588025e9f133a338d99474e8dd3c3dbbc4c5282a4fffe5a0e", size = 152624, upload-time = "2026-04-28T08:38:31.886Z" }, - { url = "https://files.pythonhosted.org/packages/64/a7/ee4d01d26032b060d379f44a29124180f756d907e3840d3bc450f8a0d2a7/plum_dispatch-2.9.0-py3-none-any.whl", hash = "sha256:5a516cdac5460343937a2b813562ac00b0eeac973ac023916e538610bdf34397", size = 45328, upload-time = "2026-04-28T08:38:33.022Z" }, -] - -[[package]] -name = "pycparser" -version = "3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, -] - -[[package]] -name = "pyerfa" -version = "2.0.1.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/71/39/63cc8291b0cf324ae710df41527faf7d331bce573899199d926b3e492260/pyerfa-2.0.1.5.tar.gz", hash = "sha256:17d6b24fe4846c65d5e7d8c362dcb08199dc63b30a236aedd73875cc83e1f6c0", size = 818430, upload-time = "2024-11-11T15:22:30.852Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7d/d9/3448a57cb5bd19950de6d6ab08bd8fbb3df60baa71726de91d73d76c481b/pyerfa-2.0.1.5-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:b282d7c60c4c47cf629c484c17ac504fcb04abd7b3f4dfcf53ee042afc3a5944", size = 341818, upload-time = "2024-11-11T15:22:16.467Z" }, - { url = "https://files.pythonhosted.org/packages/11/4a/31a363370478b63c6289a34743f2ba2d3ae1bd8223e004d18ab28fb92385/pyerfa-2.0.1.5-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:be1aeb70390dd03a34faf96749d5cabc58437410b4aab7213c512323932427df", size = 329370, upload-time = "2024-11-11T15:22:17.829Z" }, - { url = "https://files.pythonhosted.org/packages/cb/96/b6210fc624123c8ae13e1eecb68fb75e3f3adff216d95eee1c7b05843e3e/pyerfa-2.0.1.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0603e8e1b839327d586c8a627cdc634b795e18b007d84f0cda5500a0908254e", size = 692794, upload-time = "2024-11-11T15:22:19.429Z" }, - { url = "https://files.pythonhosted.org/packages/e5/e0/050018d855d26d3c0b4a7d1b2ed692be758ce276d8289e2a2b44ba1014a5/pyerfa-2.0.1.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e43c7194e3242083f2350b46c09fd4bf8ba1bcc0ebd1460b98fc47fe2389906", size = 738711, upload-time = "2024-11-11T15:22:20.661Z" }, - { url = "https://files.pythonhosted.org/packages/b9/f5/ff91ee77308793ae32fa1e1de95e9edd4551456dd888b4e87c5938657ca5/pyerfa-2.0.1.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:07b80cd70701f5d066b1ac8cce406682cfcd667a1186ec7d7ade597239a6021d", size = 722966, upload-time = "2024-11-11T15:22:21.905Z" }, - { url = "https://files.pythonhosted.org/packages/2c/56/b22b35c8551d2228ff8d445e63787112927ca13f6dc9e2c04f69d742c95b/pyerfa-2.0.1.5-cp39-abi3-win32.whl", hash = "sha256:d30b9b0df588ed5467e529d851ea324a67239096dd44703125072fd11b351ea2", size = 339955, upload-time = "2024-11-11T15:22:23.087Z" }, - { url = "https://files.pythonhosted.org/packages/b4/11/97233cf23ad5411ac6f13b1d6ee3888f90ace4f974d9bf9db887aa428912/pyerfa-2.0.1.5-cp39-abi3-win_amd64.whl", hash = "sha256:66292d437dcf75925b694977aa06eb697126e7b86553e620371ed3e48b5e0ad0", size = 349410, upload-time = "2024-11-11T15:22:24.817Z" }, -] - -[[package]] -name = "pyfftw" -version = "0.15.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "setuptools", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/4b/3f/ee1bc44b080fc1e81d293cd07bed563d254bc1997d63a3b8053804a87dfd/pyfftw-0.15.0.tar.gz", hash = "sha256:2f16b9854a40c8fdd10aa5803b24ddc6ab49f9cd559dbd7f07e7d61aa205c1ca", size = 164003, upload-time = "2024-11-06T16:01:19.293Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/66/99e0497a2dc8ff67aca6c23734895662b1885fce150a836de60ba9644ec6/pyFFTW-0.15.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:2c35205209dde186669b90eeda92ca6837dfff4814edc5aebb01536b32760eb0", size = 2835727, upload-time = "2024-11-06T16:00:34.678Z" }, - { url = "https://files.pythonhosted.org/packages/b3/2a/9eb5c7aa582bdb0ba25d0ef522281ffa5ff0e34a1cc9d0fb564ee019239a/pyFFTW-0.15.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:28bb5888342114950b2d7df268264863c5644f34130a7fdc0acabd05c885d486", size = 1272455, upload-time = "2024-11-06T16:00:36.636Z" }, - { url = "https://files.pythonhosted.org/packages/af/13/e1c59f3a659ce6712d999b5315c22e547a75f1cc5f171f3cdb9ceae13474/pyFFTW-0.15.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:5748f2d85d790684efd895ce8f3611cceedb75fde944c4788753e0ba1a88637a", size = 2449453, upload-time = "2024-11-06T16:00:38.491Z" }, - { url = "https://files.pythonhosted.org/packages/b6/b6/55a5033fd41d7b69d3ff9991d7f170f1f90a4463953a0c85011c7c87d739/pyFFTW-0.15.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:f8fe9c361bb69f58c9488d4d267047f94cfcea89ba7cd1ce6a148f807be88c6c", size = 3054268, upload-time = "2024-11-06T16:00:39.891Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f9/32cc147de14e30fd674a53097fc11e12a161bf88d120fbc4b5a3013e3ef1/pyFFTW-0.15.0-cp310-cp310-win32.whl", hash = "sha256:79a90799d4a4fc8d2c06b545e29a5e0575df81d0b9d131b20476a287f18817ee", size = 2234523, upload-time = "2024-11-06T16:00:41.222Z" }, - { url = "https://files.pythonhosted.org/packages/e0/dc/c469ccb4a06fa530cb34d7c61885cede184c3c0f39c873fb14bbc6c0450c/pyFFTW-0.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:80ed625c8e8b6c1ba3ba6741ca0c7584c62ac0d99d9d33d7b4617c5087977aa0", size = 2642503, upload-time = "2024-11-06T16:00:42.924Z" }, - { url = "https://files.pythonhosted.org/packages/17/6e/325833415acbb267b1533ea8c5dcd243df8b5bb3ecbc6e42778514859dfe/pyFFTW-0.15.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:facabac06cb2300fdfcb1cfab9af70b24380d24a725a9b4dbf8cdadf04c1a6c0", size = 2836851, upload-time = "2024-11-06T16:00:45.177Z" }, - { url = "https://files.pythonhosted.org/packages/11/cf/3e6e1fbb4dda9718682b38109018240a631f9078f659eea4add2189a6e6f/pyFFTW-0.15.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:730e86d23ea52a511fd3b0a0b8206dbe9f9f017522dabb838a7ca876e442ea8a", size = 1273108, upload-time = "2024-11-06T16:00:46.607Z" }, - { url = "https://files.pythonhosted.org/packages/41/b8/69fa1a05a9c1830fefa3805c0b67f43a5a6fa3d83950f2ab088b80826378/pyFFTW-0.15.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:b8ab3ad5b6fadf64e34be2fdc34c9cc2b767b33c7a1b0f3e778f4b3fac0b2566", size = 2506868, upload-time = "2024-11-06T16:00:48.197Z" }, - { url = "https://files.pythonhosted.org/packages/29/ca/f9035218cbf7986f214bbe57bfb1f2951d344a0baf601d8ee065d0422e07/pyFFTW-0.15.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:076c42a2b8841fc06fc592d812bfa41a85355dc7f79b6f51016ce981a35efc97", size = 3111543, upload-time = "2024-11-06T16:00:50.044Z" }, - { url = "https://files.pythonhosted.org/packages/1e/68/8c56e66ab9903440a41c3859409c2ec06b218c187c859c432d954002e778/pyFFTW-0.15.0-cp311-cp311-win32.whl", hash = "sha256:5c50580ac16173fb547d18ad089148f030ef48a0f1f3b2faf3bb522b7f9fd8b6", size = 2234192, upload-time = "2024-11-06T16:00:52.23Z" }, - { url = "https://files.pythonhosted.org/packages/91/cf/1b8ba1f0c8fb91f2caaf0ff30011a8289d7732b21cfc812b826222cafbaa/pyFFTW-0.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:553d62715e8e9ad20c6a6aa556846d785488f16fa2a518abcac58ae3c7087654", size = 2643265, upload-time = "2024-11-06T16:00:53.542Z" }, - { url = "https://files.pythonhosted.org/packages/08/5d/5d30a3a39bbe1e1d083d8efe23a62f1deacc445a82e3a55289b820f0ad9d/pyFFTW-0.15.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:27410a9ac0e4ad7821a564549be031d5422d9a96bb35ea63d2d324774f709b65", size = 2833540, upload-time = "2024-11-06T16:00:54.985Z" }, - { url = "https://files.pythonhosted.org/packages/4b/dc/ff9969669fcbff0972c1a96a116fa7efad763d136fc9a58f6112bf770688/pyFFTW-0.15.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:cd8bfb4a9e844693f8dd92ba97bc91d27404716b70580f846e80d74f6b092ffe", size = 1270715, upload-time = "2024-11-06T16:00:56.743Z" }, - { url = "https://files.pythonhosted.org/packages/c7/1c/30bc73495966285fdd3e79a4a7482dbdd863e505bf3021a455bf7b923423/pyFFTW-0.15.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:7d08f75ed35c0af097333b2de368a0b30460a3e7e7bed47780aac3c27f9ec77b", size = 2499664, upload-time = "2024-11-06T16:00:58.166Z" }, - { url = "https://files.pythonhosted.org/packages/6d/81/e5f5769f08aa3efa9527b328a9ebac7a2817f81a2b0f2266e79276686f31/pyFFTW-0.15.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:7ccae636ad163133bc5335d05f56afdb0c756ee013692c667b822a11e188a446", size = 3100310, upload-time = "2024-11-06T16:00:59.548Z" }, - { url = "https://files.pythonhosted.org/packages/60/5b/18c12dfb07b82a93b9b459380e2f118afd139cc564e1575624794b15e68d/pyFFTW-0.15.0-cp312-cp312-win32.whl", hash = "sha256:b4963a55b442c859ede2d247d84daa71c6c01b64e0d9b2730f5635b962777fb2", size = 2229927, upload-time = "2024-11-06T16:01:01.315Z" }, - { url = "https://files.pythonhosted.org/packages/dd/d2/cb3fc49bffb2f7cb49da6dbc67888c9c55df1aca6d11c6d1d000e37fc64f/pyFFTW-0.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:6ace61f34f0dd7973207c31ce16fd7bedf9a178bb956f157559f13384f219713", size = 2641096, upload-time = "2024-11-06T16:01:02.928Z" }, - { url = "https://files.pythonhosted.org/packages/a1/0d/90821a720638cbef79319463e3ce5d98f24153cee53316d4ecc1d8c0400d/pyFFTW-0.15.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:cfbeb106877db1b6bf527735647b861c86ac846ff47671d0d855a9be2de83368", size = 2832851, upload-time = "2024-11-06T16:01:04.301Z" }, - { url = "https://files.pythonhosted.org/packages/12/b7/e625f0cdb2bd65aa43ee35ce8cac1b0abc6c12871cd87fa83e404d82fd1d/pyFFTW-0.15.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:6d8913036a48ebcc9e3e1a315a6607e5cc31af4aee395aae180ff644c4658bbb", size = 1269651, upload-time = "2024-11-06T16:01:06.419Z" }, - { url = "https://files.pythonhosted.org/packages/0f/66/d12a9629908008ed97446c44ca2177c8f59e570a445da4136fdbb9e8db1c/pyFFTW-0.15.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:05324c8c092aa43868554daf6ddd7fd23eee18cd284ab68e749c62427c662737", size = 2496935, upload-time = "2024-11-06T16:01:07.82Z" }, - { url = "https://files.pythonhosted.org/packages/ad/c4/adcf010b151564401e3d401602b0e0cacae1e7cb816f0207397c3ddec626/pyFFTW-0.15.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:474f4a0ccfddfdfbe3e4a610b1c8f5968b58edf44434bf9d08ea52108a72df54", size = 3096170, upload-time = "2024-11-06T16:01:09.258Z" }, - { url = "https://files.pythonhosted.org/packages/b2/96/b1eaad07d5eaf026e2ed0c6f2afdf825628706162071d2665b3824727346/pyFFTW-0.15.0-cp313-cp313-win32.whl", hash = "sha256:40f8f3341546264178a8d9e8736e91554884595a683b71f8db8399907330f47d", size = 2229713, upload-time = "2024-11-06T16:01:10.902Z" }, - { url = "https://files.pythonhosted.org/packages/e9/23/d06a3e5b549f7537ada2b1fc2e5f7c05c38df70c843d463bc469812b1ebd/pyFFTW-0.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:bf04458c5d2fbfe5da270a4b667c59c5792cbe39300379d886acd8ea97fc55cc", size = 2640821, upload-time = "2024-11-06T16:01:12.234Z" }, - { url = "https://files.pythonhosted.org/packages/f2/5b/060fb5f48773dc1be3abd4d495a7b0fd02aeb6be57cb6bac214d1fd75447/pyFFTW-0.15.0-pp310-pypy310_pp73-macosx_12_0_x86_64.whl", hash = "sha256:e94576643406ffd4dc29851e4d493c8bfb3bde8390661b7fced050c1e6ff99ac", size = 2810336, upload-time = "2024-11-06T16:01:14.05Z" }, - { url = "https://files.pythonhosted.org/packages/46/5f/ee1692303b7cc15cdaf56861de6da8e12d001f4c45883832ffd26827b777/pyFFTW-0.15.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ca1dbb5e86121f24b637413584224dda129774d661be1640f092156f4088b3bf", size = 2570190, upload-time = "2024-11-06T16:01:15.496Z" }, - { url = "https://files.pythonhosted.org/packages/e1/19/b66be24aec24a1e59c8c7a04d58b446af15b809a53d70626acba7e865853/pyFFTW-0.15.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9ff6b656058f4099db30a5b5d01c7c1c6503c02a68f07398008a094c374d53c5", size = 2627837, upload-time = "2024-11-06T16:01:16.917Z" }, -] - -[[package]] -name = "pyfftw" -version = "0.15.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'win32'", - "python_full_version == '3.12.*' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version == '3.12.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", -] -dependencies = [ - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f2/2d/e38439b7f937e8bf91a9ff2b8d9713d0d8e64e980fc00e8d1945b8a5b74b/pyfftw-0.15.1.tar.gz", hash = "sha256:bbcde6d40d165e1cbaf12dde062ebfebe9e43394cac8c166e699ba2c9a4b0461", size = 192838, upload-time = "2025-10-22T19:58:56.683Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/20/c0/4f1a586eed5eba86c27d6fab4ebed44bbfeedd5209e835c19c6406f96a41/pyfftw-0.15.1-cp311-cp311-macosx_13_0_x86_64.whl", hash = "sha256:fb5283238be3c797b7b91cbb1d549787b9d0495a7d48b828832a3c94fa557f48", size = 3297881, upload-time = "2025-10-22T19:58:20.945Z" }, - { url = "https://files.pythonhosted.org/packages/b2/c1/02eaaf7a2f7ca26d7b56b6195617979b44b9213f3dcc1a514663a3bbc264/pyfftw-0.15.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:f4b42864e95f128a676e27a94025cce074d807a81d48ed423c65aa7403a7729a", size = 1682075, upload-time = "2025-10-22T19:58:22.236Z" }, - { url = "https://files.pythonhosted.org/packages/c6/23/e3692116f6e903d1d9705d1f948f94ee133512a0b388d05ea0f8adcb0c07/pyfftw-0.15.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e728dbe262182089a6606b7feb5c67bc55b8b385b0c1b60f71d6a891b5f142a", size = 2557646, upload-time = "2025-10-22T19:58:23.345Z" }, - { url = "https://files.pythonhosted.org/packages/7a/49/affa553caa93928d2f7d814f26939487d37d4068a0ba4d8238c6ba3000b1/pyfftw-0.15.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:294eecf05577b0198e6d041376e016254c4b54040b8a7a330d9d6321b8b30472", size = 3158494, upload-time = "2025-10-22T19:58:24.63Z" }, - { url = "https://files.pythonhosted.org/packages/88/92/bde713b499fa2062f30f396603bbf09acd0c9c974249638202c931433498/pyfftw-0.15.1-cp311-cp311-win32.whl", hash = "sha256:ed42033aa729520c66500027157525981613a139c2fd4671dd7d52d58cb4b820", size = 2226331, upload-time = "2025-10-22T19:58:26.345Z" }, - { url = "https://files.pythonhosted.org/packages/25/f4/e0583a68e1114b108354207ce0680b898a4b37c48bd3a8cfc1fd0ec1a476/pyfftw-0.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:e32a30c34f27c24ee602b89b22d9cd453570bf851e7d8f98506185cb5e99ccfc", size = 2634855, upload-time = "2025-10-22T19:58:27.839Z" }, - { url = "https://files.pythonhosted.org/packages/4f/76/5673561606f45c60ab3da497deead308e2c4b59c2b705334a01b880f0421/pyfftw-0.15.1-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:074926b5fa6a2193771cb4dc5beaa52ea1d629dc40da363d7de7918df5f3e951", size = 3315083, upload-time = "2025-10-22T19:58:28.981Z" }, - { url = "https://files.pythonhosted.org/packages/2d/fb/5042ad5c4ddc2db89844fc853b6358d36497e44f9d32f3aef9c3fa393182/pyfftw-0.15.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:2156d7bc72a2e2a1c747767909b1a7ba2bcfde14a51fca3171d7c1f8de6c2f02", size = 1698218, upload-time = "2025-10-22T19:58:30.536Z" }, - { url = "https://files.pythonhosted.org/packages/df/1c/7b76808da67a3775480f64e93acc0a623aad7682ba83428311de05ebc67a/pyfftw-0.15.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:313340683d5e99b0a36e45b8c2bd92cd86dfbb18917a62e89890927a66bed9e9", size = 2562434, upload-time = "2025-10-22T19:58:31.531Z" }, - { url = "https://files.pythonhosted.org/packages/fb/43/43fbc63ec6790ce6346e6441e82b54356334641d9d6f099f1ed75d302f2a/pyfftw-0.15.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900d79c7fa1b27b58ec4240f7cb6d512492a9db9bc8cbd18076ed28c84a63b62", size = 3167162, upload-time = "2025-10-22T19:58:32.636Z" }, - { url = "https://files.pythonhosted.org/packages/e4/e1/82d4967ef4ccd8c17e58a9a34fa283436de98c32c08aac4191ef56bf9989/pyfftw-0.15.1-cp312-cp312-win32.whl", hash = "sha256:9c94a6d251f2ceb9d6bb86964c43f9eb9cbd8612a60f41b10c8a64e816f6a2ed", size = 2222298, upload-time = "2025-10-22T19:58:33.795Z" }, - { url = "https://files.pythonhosted.org/packages/ab/c0/3f4513921fd3cb40986bc6fe4a33e87c5aef671edff483db6f7c4a5f8309/pyfftw-0.15.1-cp312-cp312-win_amd64.whl", hash = "sha256:74fe153d6bec682cd85b5601df09cb84ce6e3cc901172a3ce86da7544e457e4c", size = 2633571, upload-time = "2025-10-22T19:58:35.185Z" }, - { url = "https://files.pythonhosted.org/packages/c2/f5/5dca09b863e71db438069eb1990526a4409deebc190228177dec3b7cc636/pyfftw-0.15.1-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:a16c4eb45ff5277f8bcb979d43a6a0d7f2e7405dcad984dc45a30064ed487da9", size = 3315212, upload-time = "2025-10-22T19:58:36.273Z" }, - { url = "https://files.pythonhosted.org/packages/56/c1/1ff9aa930f98c77bbff9cae122e496f1fd7201abac309f1dbcc9fbe5c7ee/pyfftw-0.15.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:2b4bad0184546e3129eeda9d07541eca71232f6e431d57c734930d25f06386e8", size = 1698270, upload-time = "2025-10-22T19:58:37.684Z" }, - { url = "https://files.pythonhosted.org/packages/9a/3f/e94d5773429dc076ec4b47d029603de1728a5037024ed3dbcc853cfa72e9/pyfftw-0.15.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1716378d1d102527917b872a5baf15e2e3de52d5200ccc22128816cd71c33148", size = 2559574, upload-time = "2025-10-22T19:58:38.662Z" }, - { url = "https://files.pythonhosted.org/packages/cb/64/5f773d61cca91a1a741e2403d7191610513d0cca3fc73a41a6941a31fe89/pyfftw-0.15.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:16209de9a244ab7b3337e8ce8d528420ffd05881e7d19e5be21d2546a7e5b2c1", size = 3166906, upload-time = "2025-10-22T19:58:39.986Z" }, - { url = "https://files.pythonhosted.org/packages/c4/8a/dda252557803fde20be41396425a85faf6680eaeed3996e4b765745e9206/pyfftw-0.15.1-cp313-cp313-win32.whl", hash = "sha256:db2a65c59d7a707c55a8f0f38be3916b907429ceed316c6875201b202e22ac99", size = 2222209, upload-time = "2025-10-22T19:58:41.237Z" }, - { url = "https://files.pythonhosted.org/packages/20/6b/9236a2fb77b01b00b957304d53694de2bf034caac89c1d35ab7fa3421fd9/pyfftw-0.15.1-cp313-cp313-win_amd64.whl", hash = "sha256:dcac51a5a4b8c6600bb2c7edb86d9739860a61bb0b076e20fbf0340919da307d", size = 2633673, upload-time = "2025-10-22T19:58:42.709Z" }, - { url = "https://files.pythonhosted.org/packages/3f/5c/ec4cef958e936ac61528644825d052c1c486192aeffadf071071a17cd86a/pyfftw-0.15.1-cp314-cp314-macosx_13_0_x86_64.whl", hash = "sha256:c4e49219794bfe99c7db94d0b392c86ff979db4da2d27d444edbc6de1519ddae", size = 3313756, upload-time = "2025-10-22T19:58:44.002Z" }, - { url = "https://files.pythonhosted.org/packages/d3/68/39e06bab39edf6473653670c6c25fe971a7b369a2059ddc971ac5f872653/pyfftw-0.15.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:489a6364a613935736d43da67791ab4dfcf4875e968a8b46e99983afde7ec960", size = 1697163, upload-time = "2025-10-22T19:58:45.592Z" }, - { url = "https://files.pythonhosted.org/packages/dc/4d/9ae0dfaf5174a13714cfc5377165de5703230c1df96715cfee9d19fb0b4c/pyfftw-0.15.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:379844360c03402f4d2a738fa19924153d5467cdc88aa3752b12bbc1403512b0", size = 2561755, upload-time = "2025-10-22T19:58:46.773Z" }, - { url = "https://files.pythonhosted.org/packages/d5/3b/bc991e3abf4f6ad9907658e3b282530d345027fa294b510a9eb1fce882c4/pyfftw-0.15.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:398a342a689061bfc4bfe7271a973e093f78176a632fb4e523b3ed5b72dba4c6", size = 3156174, upload-time = "2025-10-22T19:58:47.999Z" }, - { url = "https://files.pythonhosted.org/packages/5c/5d/c0a9782c333e8b5de334285e3adc0542f2265107d0c9b720f5730bc43471/pyfftw-0.15.1-cp314-cp314-win32.whl", hash = "sha256:375ec8b11140eb30262bf9ecfa34d043a42d036c33c66e24bfe734f5aae7ddf0", size = 2245736, upload-time = "2025-10-22T19:58:49.422Z" }, - { url = "https://files.pythonhosted.org/packages/ef/c7/ba88f0b5d81ae0732a2cb2900c52924ddfa6b1623e511533c022a558e058/pyfftw-0.15.1-cp314-cp314-win_amd64.whl", hash = "sha256:558c318a1aea81ce2083309c927fd881ccf9c285c5571ce965bb7b18dc4291fd", size = 2660474, upload-time = "2025-10-22T19:58:50.93Z" }, - { url = "https://files.pythonhosted.org/packages/88/45/a34cd015d16002e748de0e218d24ecc2f30eb9ad073f1c7941a51a796a2e/pyfftw-0.15.1-pp311-pypy311_pp73-macosx_13_0_x86_64.whl", hash = "sha256:41a25b721e79378a3ea29d21d2e6cf25e43c1f44bc0bbf554beccdcdb2b8064e", size = 3278795, upload-time = "2025-10-22T19:58:52.212Z" }, - { url = "https://files.pythonhosted.org/packages/15/27/0e6195c9c5eeef73a7499772621057aff5ffd04ebb52a71d49cc049a3dad/pyfftw-0.15.1-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e957f5caa7ccc032d17831ce336cb61b93f516ed7da25ea6c9e1086ed2e323f", size = 2570541, upload-time = "2025-10-22T19:58:53.357Z" }, - { url = "https://files.pythonhosted.org/packages/2e/a9/f1d00acce882645198576a94c4b7fff5465f0bb6c4b7bb1005314527c2d0/pyfftw-0.15.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:763cd58e858133493db371ed3a63e9e41463cd40965e2375a079947c2d96805f", size = 2625398, upload-time = "2025-10-22T19:58:54.893Z" }, -] - -[[package]] -name = "pygments" -version = "2.20.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, -] - -[[package]] -name = "pyjwt" -version = "2.12.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c2/27/a3b6e5bf6ff856d2509292e95c8f57f0df7017cf5394921fc4e4ef40308a/pyjwt-2.12.1.tar.gz", hash = "sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b", size = 102564, upload-time = "2026-03-13T19:27:37.25Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/7a/8dd906bd22e79e47397a61742927f6747fe93242ef86645ee9092e610244/pyjwt-2.12.1-py3-none-any.whl", hash = "sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c", size = 29726, upload-time = "2026-03-13T19:27:35.677Z" }, -] - -[[package]] -name = "pyparsing" -version = "3.3.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" }, -] - -[[package]] -name = "pytest" -version = "9.0.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, -] - -[[package]] -name = "pytest-requires" -version = "0.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ab/fc/2fc8d97d40b3bb74d93ba7baf06644f2e53daf31345721b5548acb2123e8/pytest-requires-0.1.0.tar.gz", hash = "sha256:1d7ee62cffcd4d6ba16d21e5f96fc6d9006ef94f923d64e278d8b5fddbec924e", size = 4718, upload-time = "2021-12-21T16:55:26.774Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/bf/551afa01bc600ba472489a2269f87a8396424d965b864415a41494c541bd/pytest_requires-0.1.0-py3-none-any.whl", hash = "sha256:f13f3a7dd2d1f0df4affd2873719d0836f27012cea1a04efe6c7c117d4ec53cf", size = 4355, upload-time = "2021-12-21T16:55:24.761Z" }, -] - -[[package]] -name = "pytest-rerunfailures" -version = "16.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "packaging" }, - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6b/27/fd0209642f3a1069da3e0be3c7e339f942d052d81ccb1fb4eb9b187d3633/pytest_rerunfailures-16.2.tar.gz", hash = "sha256:5f5a32f15674a3d54f7598388fcd3cc1bc5c37284731a4704a44485dcdda5e23", size = 32121, upload-time = "2026-05-13T08:13:26.998Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/a5/d8c1ad74529b483044b787ead2d24ecc624bca4084a509002102e4bab8cc/pytest_rerunfailures-16.2-py3-none-any.whl", hash = "sha256:c22a53d2827becc76f057d4ded123c0e726523f2f0e5f0bb4efb31fd59e1f14e", size = 14505, upload-time = "2026-05-13T08:13:25.485Z" }, -] - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, -] - -[[package]] -name = "pytz" -version = "2026.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ff/46/dd499ec9038423421951e4fad73051febaa13d2df82b4064f87af8b8c0c3/pytz-2026.2.tar.gz", hash = "sha256:0e60b47b29f21574376f218fe21abc009894a2321ea16c6754f3cad6eb7cdd6a", size = 320861, upload-time = "2026-05-04T01:35:29.667Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/dd/96da98f892250475bdf2328112d7468abdd4acc7b902b6af23f4ed958ea0/pytz-2026.2-py2.py3-none-any.whl", hash = "sha256:04156e608bee23d3792fd45c94ae47fae1036688e75032eea2e3bf0323d1f126", size = 510141, upload-time = "2026-05-04T01:35:27.408Z" }, -] - -[[package]] -name = "pyyaml" -version = "6.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" }, - { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" }, - { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" }, - { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" }, - { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" }, - { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" }, - { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" }, - { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" }, - { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" }, - { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, - { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, - { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, - { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, - { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, - { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, - { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, - { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, - { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, - { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, - { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, - { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, - { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, - { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, - { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, - { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, - { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, - { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, - { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, - { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, - { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, - { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, - { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, - { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, - { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, - { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, - { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, - { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, - { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, - { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, - { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, - { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, - { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, -] - -[[package]] -name = "regex" -version = "2026.5.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/dc/0e/49aee608ad09480e7fd276898c99ec6192985fa331abe4eb3a986094490b/regex-2026.5.9.tar.gz", hash = "sha256:a8234aa23ec39894bfe4a3f1b85616a7032481964a13ac6fc9f10de4f6fca270", size = 416074, upload-time = "2026-05-09T23:15:19.37Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/ed/0ad2c8edf634918eb4484365d3819fa7bd7f58daf807fe7fb21812c316e5/regex-2026.5.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a9e1328e17c84c1a5d22ec9f785ecef4a967fab9a42b6a8dc3bcbebd0a0c9e44", size = 489438, upload-time = "2026-05-09T23:11:29.374Z" }, - { url = "https://files.pythonhosted.org/packages/89/a9/4ed972ad263963b860b7c3e86e0e1bcc791def47b43b8c8efe57e710f139/regex-2026.5.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bfe1ce50cbfb569d74e1e4337da6468961f31dbea55fd85aa5de59c0947a805a", size = 291270, upload-time = "2026-05-09T23:11:33.254Z" }, - { url = "https://files.pythonhosted.org/packages/16/81/075930d9fa28c4ea1f53398dd015ee7c882f623539759113cda1257f4b82/regex-2026.5.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15ee42209947f4ca045412eae98416317238163618ace2a8e54f99586a466733", size = 289198, upload-time = "2026-05-09T23:11:35.769Z" }, - { url = "https://files.pythonhosted.org/packages/d4/c8/5cdfbf0b5dc6599e1b6131eff43262e5275d4ec3469ce10216061659aadb/regex-2026.5.9-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4bb445ff3f725f59df8f6014edb547ee928ec7023a774f6a39a3f953038cbb2", size = 784765, upload-time = "2026-05-09T23:11:37.689Z" }, - { url = "https://files.pythonhosted.org/packages/cd/ca/ae5fd6edc59b7f84b904b31d6ec39a860cbcecd10f64bd5a062ca83a4864/regex-2026.5.9-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:446ddd671e43ab535810c4b21cff7104945c701d4a14d1e6d1cd6f4e445a8bea", size = 852115, upload-time = "2026-05-09T23:11:39.973Z" }, - { url = "https://files.pythonhosted.org/packages/f6/ce/a91cf555afb51f3b74a182e24ba073b91ea7bb64592fc4b315c111bb19fd/regex-2026.5.9-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7b92817338591505f282cf3864c145244b1edcf5381d237038df955001091538", size = 899503, upload-time = "2026-05-09T23:11:42.48Z" }, - { url = "https://files.pythonhosted.org/packages/55/7f/725a0a2b245a4cf0c4bab29d0e97c74285d94136a65d1b55a6459a583502/regex-2026.5.9-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6b8a143aca6c39b446ea8092cde25cc8fe9304d4f5fecfbc1a9dbb0282703c2", size = 794093, upload-time = "2026-05-09T23:11:44.681Z" }, - { url = "https://files.pythonhosted.org/packages/e3/2a/996efbd59ce6b5d4a09e3af6180ceb62af171f4a9a6fb557d2f0ae0d462b/regex-2026.5.9-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0f03aa6898aaaac4592479821df16e68e8d0e29e903e65d8f2dfb2f19028a989", size = 786234, upload-time = "2026-05-09T23:11:46.882Z" }, - { url = "https://files.pythonhosted.org/packages/4b/0a/8731e8b8806174c9cdd5903f80a14990331c1f42fc4209b540952e9e010d/regex-2026.5.9-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ed457d8e98ae812ed7732bef7bf78de78e834eae0372a74e23ca90ef21d910f9", size = 769895, upload-time = "2026-05-09T23:11:49.324Z" }, - { url = "https://files.pythonhosted.org/packages/9a/0b/932473194bd563f342a412ae2ffbbd6da608306a2bc4e99249a41c2b0b92/regex-2026.5.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:71b61c5bfe1c806332defc42ad6c780b3c55f661986d7f40283a3a88274b4c00", size = 774991, upload-time = "2026-05-09T23:11:51.261Z" }, - { url = "https://files.pythonhosted.org/packages/98/80/9523d196010031df25f7177ee0a467efbee436324038e5d99def17a57515/regex-2026.5.9-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:3b1e39888c5e0c7d92cea4fc777396c4a90363b05de75d02eb459a4752200808", size = 848790, upload-time = "2026-05-09T23:11:53.232Z" }, - { url = "https://files.pythonhosted.org/packages/3c/07/56987b35e89edf47e4a38cf2845aeee476bfa688a6bdbd3e820cda461dc1/regex-2026.5.9-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:6ba42b2e7e7f46cf68cc6a5ca36fa07959f9bbd9c6bdcc47b6ee76549a590248", size = 757679, upload-time = "2026-05-09T23:11:55.82Z" }, - { url = "https://files.pythonhosted.org/packages/04/2a/ff713fff0c566507c06a4ce2dc0ae8e7eeebc88811a95fc81cf1e7d534dd/regex-2026.5.9-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:c010eb8caca74bdb40c07498d7ece26b4428fd3f04aa8a72c9ac6f79e8faaac6", size = 837116, upload-time = "2026-05-09T23:11:57.934Z" }, - { url = "https://files.pythonhosted.org/packages/77/90/df6d982b03e3614785c6937ba51b57f6733d97d2ee1c9bc7531dbfab3a54/regex-2026.5.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a6a563446a41adc451393dc6b8e6ad87979efaee3c8738690a8d1b08ebead1b4", size = 782081, upload-time = "2026-05-09T23:11:59.607Z" }, - { url = "https://files.pythonhosted.org/packages/c7/8a/4e88a5f7c3e98489aac4dd23142723d907b2a595b4a6abcbacabefeded09/regex-2026.5.9-cp310-cp310-win32.whl", hash = "sha256:954cc214c04663ee6d266fc61739cad83054683048de65c5bd1d640ad28098ac", size = 266247, upload-time = "2026-05-09T23:12:01.116Z" }, - { url = "https://files.pythonhosted.org/packages/6a/40/4b224cb0582b2dca1786726e6cdabe26abbf757d7f6718332f186da155d2/regex-2026.5.9-cp310-cp310-win_amd64.whl", hash = "sha256:b310768746dd314ea6e2ff4cc89ef215426813396ff4e94ee8e6f7096c8b6e03", size = 278416, upload-time = "2026-05-09T23:12:03.2Z" }, - { url = "https://files.pythonhosted.org/packages/12/4d/014fbe803204cab0947ee428f09f658a29632053dde1d3c6176bb4f0fd4c/regex-2026.5.9-cp310-cp310-win_arm64.whl", hash = "sha256:19c16ceb4a267a8789e25733e583983eeab9f0f8664e66b0bd1c5d21f14c2d4b", size = 270413, upload-time = "2026-05-09T23:12:04.649Z" }, - { url = "https://files.pythonhosted.org/packages/c2/dc/c1f2df4027e82fc54b5a473e4b250f5139faca49a0fbe29a48668d228f34/regex-2026.5.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ccf5249114cc3e772ecdd88a98a86eca0fd74c61ce32a94743758c083fc05d48", size = 489445, upload-time = "2026-05-09T23:12:06.111Z" }, - { url = "https://files.pythonhosted.org/packages/03/d2/59f01110660081cce9c0bc30ebd0b5ee250dacf658e3248ed92f01e0e8ee/regex-2026.5.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46f1326ca6e65b0879d23ca302c0f2415aad42ff0309b9c818e7949fe19a41d8", size = 291271, upload-time = "2026-05-09T23:12:07.731Z" }, - { url = "https://files.pythonhosted.org/packages/58/b6/14b2c84ff90ddb370c81d27503f4a0fcf071496416f4855f6cc8c5d81c35/regex-2026.5.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ef31cbfe458e21c6122ba8150ff060e0c7789ed0d26eb423f25472584920b555", size = 289212, upload-time = "2026-05-09T23:12:09.266Z" }, - { url = "https://files.pythonhosted.org/packages/03/d0/4db86529117320de0c84afd90e70bb47434625875e34fcef9d8c127c5b16/regex-2026.5.9-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:992604d02e6d9c6d786c24a706a71ecffe1020fc1ef264044474cd81fa2c3919", size = 792310, upload-time = "2026-05-09T23:12:11.416Z" }, - { url = "https://files.pythonhosted.org/packages/07/78/fe4800cd322f862ecffd2d553409b20d80650e5ed71b9d178f853d020b82/regex-2026.5.9-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c9411dd64ca95477225734a93dfc8583b51916b8d5942f99d6cac21e09965451", size = 861721, upload-time = "2026-05-09T23:12:13.681Z" }, - { url = "https://files.pythonhosted.org/packages/b5/d0/b3618a895dd8feb897c61bb2954edd265e1767d82a01d53065d5871127a3/regex-2026.5.9-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3dd4a3ff360dfb836fecdb93a4598f9d6e2ac81e3e397125145c6221bf58cf4c", size = 906460, upload-time = "2026-05-09T23:12:15.443Z" }, - { url = "https://files.pythonhosted.org/packages/33/6f/1481597e859ef19508b345eec4afd1416ed6e6b459c75a64026ef193aecf/regex-2026.5.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2a661a7d270a61f7cf460caee8b9fa2d5ef9e5c681234bcb9e0fe14f488e7dfc", size = 799843, upload-time = "2026-05-09T23:12:16.892Z" }, - { url = "https://files.pythonhosted.org/packages/73/59/955734c803f59108deccba3597ae440c76b62a652733c0006e6243758420/regex-2026.5.9-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f079e50a0d3cc3cd5091fa9ff45869a2e6b2cd35895731edafb0327901a8d86d", size = 773610, upload-time = "2026-05-09T23:12:19.127Z" }, - { url = "https://files.pythonhosted.org/packages/68/8f/70c04a236d651c81881dac42ef8538bddda6121434509d0a22d9e601503b/regex-2026.5.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4ebe8f0b5ec5a5024dc4a4c59f444c4e9afc5f2abdbb8962065b75d27fb971f9", size = 781645, upload-time = "2026-05-09T23:12:20.806Z" }, - { url = "https://files.pythonhosted.org/packages/1d/96/05c7434d88185e5d27fe54aeb74df86bd77cd79f52f0b4eae54faa8fea70/regex-2026.5.9-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:97cf3bc1b7d7d2306772ec07366c80d9df00ff79e79cea32898883a646d2fae2", size = 854473, upload-time = "2026-05-09T23:12:22.465Z" }, - { url = "https://files.pythonhosted.org/packages/4e/c1/6e3d8202d981f3117004bf341ee74893ba4ba8a9fbaf4b94615846550a08/regex-2026.5.9-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0f9eede6a5cbdc02d4978090186390936e1776a7d1359b21e41014c609880bcf", size = 763311, upload-time = "2026-05-09T23:12:24.351Z" }, - { url = "https://files.pythonhosted.org/packages/93/c7/e7737f1526b3fb32bd4c337fd6c71c3ebb5c8296fc34d11197e0955d2e35/regex-2026.5.9-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:01f0f5f55f4b64dacec85dc116d3c05fd23ad3ff037bbc73a2085775953c2611", size = 844593, upload-time = "2026-05-09T23:12:26.341Z" }, - { url = "https://files.pythonhosted.org/packages/a5/27/0daffb1a535bb39f422c3d200f4ab023c71110ad66a32b366bee708baba0/regex-2026.5.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1268eddd8486dc561d08eee1156e40aa3a8fe10f4bdec8fa653b455fcbffd12c", size = 789167, upload-time = "2026-05-09T23:12:27.975Z" }, - { url = "https://files.pythonhosted.org/packages/ce/fc/294fe4fac4f2ed67207b17471815870c1c45b3a489e08e0ac96daea16ef6/regex-2026.5.9-cp311-cp311-win32.whl", hash = "sha256:8676474c07469d6f33dd1085ca2cd45f65785f32518f2b20e36d9953ca07f994", size = 266249, upload-time = "2026-05-09T23:12:30.141Z" }, - { url = "https://files.pythonhosted.org/packages/d0/b0/8dce459f6245bcf8f6e9f23ac9569f1a0f15c131cc0745e82b43226204cf/regex-2026.5.9-cp311-cp311-win_amd64.whl", hash = "sha256:246de9d60aa3f8538b519834dd95cbf276ea263d6a7bd5a3666dc3fa0230505b", size = 278423, upload-time = "2026-05-09T23:12:31.676Z" }, - { url = "https://files.pythonhosted.org/packages/db/8d/f9aeff6ad63a3ef720386f2907e6d34a35a510a6e498ebad28b0fb3f6ab6/regex-2026.5.9-cp311-cp311-win_arm64.whl", hash = "sha256:d726ca3f0d76969bf1e8e477d160d3d666bbf999f6860bd314889e5345782046", size = 270420, upload-time = "2026-05-09T23:12:33.194Z" }, - { url = "https://files.pythonhosted.org/packages/50/9b/6550044bc44e17c84d312c031c2ec42fbdb6a4ec4e29093be3a172d08772/regex-2026.5.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:57eeeb05db7979413dec5438f2db21d7ecbba787cde7a711df1a6f6df672aa06", size = 490451, upload-time = "2026-05-09T23:12:34.72Z" }, - { url = "https://files.pythonhosted.org/packages/1e/95/fc7ba4303b5a0f92446a12ee6778ef2c6c799233f5060042a31bf390cfe9/regex-2026.5.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:398c521292f4c7fb807001dcd54694d3a1fcafc179a36ad9cc56f98df85930b6", size = 292112, upload-time = "2026-05-09T23:12:36.285Z" }, - { url = "https://files.pythonhosted.org/packages/54/4b/ee27938d1b2c443e89a9a10e00d2d19aa5ee300cd3d61140644e93bb083e/regex-2026.5.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f7a7c26137296beba7784de6eba69c6a93a63ccebc385e4962fe67e267a91225", size = 289599, upload-time = "2026-05-09T23:12:38.089Z" }, - { url = "https://files.pythonhosted.org/packages/d8/dd/ba103dc19614e25f3880800ca67ce093d6e21b325d72b8383c7bf906e9fa/regex-2026.5.9-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6441cc660d76107934a09c22167200839a0e89604a6297f78a974e66e931d2c0", size = 796732, upload-time = "2026-05-09T23:12:40.062Z" }, - { url = "https://files.pythonhosted.org/packages/cf/e7/f035b4fd858b050b0080bf302968dc0f59ba34e391872d54936758e6844e/regex-2026.5.9-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:91328f1c23d47595ca3ef0a7557fa129c5a23404b775c770697d2f35b33e0107", size = 865440, upload-time = "2026-05-09T23:12:42.059Z" }, - { url = "https://files.pythonhosted.org/packages/0a/51/8cd301ecc899aea28124357f729f4272f44de7806fc7ca02490bfbe253e8/regex-2026.5.9-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:93a7860539414dddaefba2b40f8771765ae17949d4c7182b876ce429e11a8309", size = 912329, upload-time = "2026-05-09T23:12:44.373Z" }, - { url = "https://files.pythonhosted.org/packages/cc/1e/3fbe2fa1e8cebd62f3bb7d3321cff1640aca2e240b51d9bd624aad949260/regex-2026.5.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd2810d22146b6d838acc5ec15602cb6b47920aa4e33015df3868eedfd20bab8", size = 801239, upload-time = "2026-05-09T23:12:46.268Z" }, - { url = "https://files.pythonhosted.org/packages/17/2f/6f6008682bf2cf98040a0d3153a8e557b6ab728d7713d045cee4ce544ab8/regex-2026.5.9-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:daff2bdbaf1d23e52fdff7c0b7bc2048b68f978df6a4d107ac981f94caef2e66", size = 777054, upload-time = "2026-05-09T23:12:48.051Z" }, - { url = "https://files.pythonhosted.org/packages/19/2b/eee0d20a6842ba04df4b8847a920b57ef56853f14ef85405473e586b605a/regex-2026.5.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4eeb011098fcb77af513dcef521a3dbecbf8849b1e38940759d293b7a93f5026", size = 785098, upload-time = "2026-05-09T23:12:49.851Z" }, - { url = "https://files.pythonhosted.org/packages/4a/98/6fc1e6410feefb92159edaed5041992bfe390e8d26c721865434acbca558/regex-2026.5.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ea9c8ecfa1b73c73b626534d6626e5340d429630943672b8480724f44e84b962", size = 860095, upload-time = "2026-05-09T23:12:51.666Z" }, - { url = "https://files.pythonhosted.org/packages/18/a3/bd855e0f2cb1a978ecf6fa6bb69632dd9c3f6ea3b81cde62fde14c9daec7/regex-2026.5.9-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:cd2846168eb9ee3c513902bc8225409cb1caab31d04728b145171fa1625d9621", size = 765762, upload-time = "2026-05-09T23:12:53.413Z" }, - { url = "https://files.pythonhosted.org/packages/dc/66/0ae8c092e60b14c79d24f8e0b7f0aea5bfbffdcab00b5483d13404d3c3a5/regex-2026.5.9-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:39617fb0cde9c0e6306dc70e3bfc096f3da793219879f7ae7aa341a69fbdcf6d", size = 852100, upload-time = "2026-05-09T23:12:55.256Z" }, - { url = "https://files.pythonhosted.org/packages/21/de/8dfde60fc1b21c946a893ba273403b72617edb261370cb1087099a83f088/regex-2026.5.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fd03c4f0e33280d15cae17159b899245d6b7c53d21def19b263b39655061f5ce", size = 789479, upload-time = "2026-05-09T23:12:57.573Z" }, - { url = "https://files.pythonhosted.org/packages/c3/1c/bdcc98f9a4af4fdd166c74941174619ccff4726d3ce32faa8e9a2ecd38dd/regex-2026.5.9-cp312-cp312-win32.whl", hash = "sha256:164eba9b755ea6f244b0d881196fbc1fac09714e9782c9e2732b813142033c8e", size = 266699, upload-time = "2026-05-09T23:12:59.14Z" }, - { url = "https://files.pythonhosted.org/packages/78/87/240d36864f9e48ace85f72e79ced97ceb7f27ce87739a947dcb834b4e6bc/regex-2026.5.9-cp312-cp312-win_amd64.whl", hash = "sha256:86f40a5d6444db30a125c9c9177e6b25dad981cbc37451fd838f145e6edac92e", size = 277783, upload-time = "2026-05-09T23:13:00.789Z" }, - { url = "https://files.pythonhosted.org/packages/4f/b5/7b30f312b0669dff5beebe5b0989dc2d1a312b1a44fab852199c387a5b96/regex-2026.5.9-cp312-cp312-win_arm64.whl", hash = "sha256:96f5f58b54a063d7ea9dca08e1cf57bfe10499c4d579ee672da284f57f5f0070", size = 270513, upload-time = "2026-05-09T23:13:02.426Z" }, - { url = "https://files.pythonhosted.org/packages/aa/da/797e91ecec6f84135da778ddce78c20e0af5d2a15c26f87a81bc3eadb6db/regex-2026.5.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d626b84406444b165fc0ba981604edea39f0588ff1f92baa23fe50799ea9afdb", size = 490303, upload-time = "2026-05-09T23:13:04.382Z" }, - { url = "https://files.pythonhosted.org/packages/44/da/bf30abaaa737b58f4a4b8c4a03659e02fd92092c822e0197ed9e0daab917/regex-2026.5.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d7bdc0ab8f3dd7e1b4f9ab88634e13374669db86bb3c72e8292f07ae313f539f", size = 292019, upload-time = "2026-05-09T23:13:06.022Z" }, - { url = "https://files.pythonhosted.org/packages/2d/e7/d0eaf5713828417b9e5648cf81fa9bacd4961f6ab98c380c2034f8716e35/regex-2026.5.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a8820737949116ffff55fe18f9fc644530063ba6ebfcb8314239416e78f1347c", size = 289468, upload-time = "2026-05-09T23:13:08.214Z" }, - { url = "https://files.pythonhosted.org/packages/d3/9b/b3fdd62b003baa1a9b593cd8c8699c9651c2e80cc21a5c715707983c42d7/regex-2026.5.9-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa0fbdbac82cb3e4450d0ccde7d7a35607f4cb2dd9fba4b8b69bfaf8c9fa6aed", size = 796749, upload-time = "2026-05-09T23:13:10.573Z" }, - { url = "https://files.pythonhosted.org/packages/d4/30/66ab84588765f5b4b271a9ca09ef7ce2b87caa95176ec3d2ad65d7bc4902/regex-2026.5.9-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:57e8915c7986aa33d25e4d3629cef711cd2863f2961b10409f0c04cb8b7d9020", size = 865445, upload-time = "2026-05-09T23:13:12.523Z" }, - { url = "https://files.pythonhosted.org/packages/1a/89/f05169e8588aac365f35ffc7f3bc3184f095ef4cfded7cfaa3c7fd5dbd89/regex-2026.5.9-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:508f56a89ba9cb26e4168cbc37dbd60a28d82430a9e18ad1d25fe0883c314ca2", size = 912322, upload-time = "2026-05-09T23:13:14.281Z" }, - { url = "https://files.pythonhosted.org/packages/30/e1/c93444052cf41581f3c884ab3fb5823daf0992f11cd4388d4275ca610558/regex-2026.5.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6d189041f15691cfa2b6c4290448ec221244d225b3f5fe9e7771b34ffcdf6e2", size = 801269, upload-time = "2026-05-09T23:13:16.569Z" }, - { url = "https://files.pythonhosted.org/packages/50/fe/0cf96b882f540e62e8b9956599798203d599c44cf4c77917ca27400ff69b/regex-2026.5.9-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e82db382b44d0111b22601c509c89f64434816c9e0eef9d1989cda8cc6ff1c04", size = 777085, upload-time = "2026-05-09T23:13:18.675Z" }, - { url = "https://files.pythonhosted.org/packages/23/5c/d78d4924e7fc875557b9e9b768423925fdfaac5549d06da7810019a9bd26/regex-2026.5.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2acfb48634f64996b57f90f39afa692ff362162722581921fe92239a59960f3c", size = 785153, upload-time = "2026-05-09T23:13:20.525Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e0/5214774090e7b4524dcea3e3c4aa74141d43043f8beb49c1599db1c8b53a/regex-2026.5.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d29eebfc9525db68cad3c97eedd7f754fa265aa5cd0cf4f863b2421e1b48fc9f", size = 860164, upload-time = "2026-05-09T23:13:22.263Z" }, - { url = "https://files.pythonhosted.org/packages/6e/e1/4a57a83350319b1271f0d7a249b8672513ed928b237a741631270de6caea/regex-2026.5.9-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:debb893095e944091c16e641a6e33c1b0f4cb61ab945ec5afbf53ce7068834d8", size = 765731, upload-time = "2026-05-09T23:13:24.277Z" }, - { url = "https://files.pythonhosted.org/packages/12/f4/499e74a20c156fc75836ee04a72a38d1a063978f600937f9760467beb1b0/regex-2026.5.9-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d659eee77986549c9ea45b861c7567e44d6287c3dc9a4565478853f7b9fe2ff6", size = 852062, upload-time = "2026-05-09T23:13:26.125Z" }, - { url = "https://files.pythonhosted.org/packages/5b/92/7eebc0d0a01e78629695f342ba17e0deaff8fb45e79cc0d7b98287da6e3e/regex-2026.5.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2efa205e6d98b24d1f3ab395c11aa15cdf10935bca283d0285e0499c284fba21", size = 789577, upload-time = "2026-05-09T23:13:27.814Z" }, - { url = "https://files.pythonhosted.org/packages/05/a4/018e71f7d2ad48c1ebe6d3ae0026f9b7cb4802fd15c7cc02fdf724355102/regex-2026.5.9-cp313-cp313-win32.whl", hash = "sha256:f3844f134e834076677dd369976e9f5068679fcb8e50102fdf6b7ac96a3ec127", size = 266691, upload-time = "2026-05-09T23:13:29.549Z" }, - { url = "https://files.pythonhosted.org/packages/e6/1d/861a93719fb9ee7dbfc3761b3797b7a3e112a5d42c6129459d2d741be9b5/regex-2026.5.9-cp313-cp313-win_amd64.whl", hash = "sha256:3527bb4942d2c14552155406cdedd906567456821848aed1cb4933a391bf5eca", size = 277747, upload-time = "2026-05-09T23:13:31.859Z" }, - { url = "https://files.pythonhosted.org/packages/d9/c6/0a2436ae4da1ba76e51cb98943c6838a9a721faa40ebe2dce07694ae34e3/regex-2026.5.9-cp313-cp313-win_arm64.whl", hash = "sha256:56a33f191f17d8c417f99945ebdc1e691d3af9605d86ec68c7e54a57e3e17af6", size = 270500, upload-time = "2026-05-09T23:13:33.525Z" }, - { url = "https://files.pythonhosted.org/packages/e8/e9/d21346f7b60ed58789371358ed66b09d00f832e1bd7c06e55d9da5679882/regex-2026.5.9-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:01f28d868834624c934b8d2e0aa1c8341337e37831f4a012f18a5afcba4cbaf3", size = 494172, upload-time = "2026-05-09T23:13:35.935Z" }, - { url = "https://files.pythonhosted.org/packages/c4/43/fd1177a2032037c681baecdb3422ee4e1424aec4e4f470ef47793d325274/regex-2026.5.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:48036f6374aaa79eb3b754ec29c61d1c6b1606749d705a13f8854fa2539671f6", size = 293952, upload-time = "2026-05-09T23:13:38.307Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7d/9fbf919768368d3f8a4f6c692cf2aa61e482b2b81ec6a298ace4cbf02480/regex-2026.5.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b96350aa424e79d4fd6b567b344dcbe2b2d6bfc48dfe7717587e1fa6d43da6ff", size = 292314, upload-time = "2026-05-09T23:13:40.353Z" }, - { url = "https://files.pythonhosted.org/packages/e2/6c/e41bfeecb589716843e7c4df09ba46ff2a42961457afece19059d85caeef/regex-2026.5.9-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8f3af7a4903c5c04a11a196a5aa75cdd7dd3f8508132f9fb3259d9f5908e3b88", size = 811681, upload-time = "2026-05-09T23:13:42.543Z" }, - { url = "https://files.pythonhosted.org/packages/87/83/a5c1c525fba0aa656e88ad0face0b1829788ef4c2fb6b26df58aa1151b84/regex-2026.5.9-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7e87577720152d2caae19fe2baaf1f8d5ca12091e9e229f03915c37d1e4b9178", size = 871135, upload-time = "2026-05-09T23:13:44.326Z" }, - { url = "https://files.pythonhosted.org/packages/18/d4/80882e799e440dd878b0979cbebf8fa4d54624a332c83037c7a701649e3f/regex-2026.5.9-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c8b9b9d294cfea3cd19c718ade7cc93492b2c4991abd9a68d0b3477ae6d8e100", size = 917265, upload-time = "2026-05-09T23:13:47.295Z" }, - { url = "https://files.pythonhosted.org/packages/ae/ff/8db60211e2286e396aad7dc7725356c502bff0901ea05bd6cdc2e1a042b9/regex-2026.5.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:728d8bfd28a8845c8b6bc5dc7ce010453d206396786c0765c2740cb65f37791e", size = 816311, upload-time = "2026-05-09T23:13:49.885Z" }, - { url = "https://files.pythonhosted.org/packages/4c/47/742ef579c61730f8d268e5cf1f9ce0e37e2ea041ad0f5644724f2378e463/regex-2026.5.9-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7e30b874d341fac767d7df5a0870540541c2c054b80cfaac116e8d367a8a7ff2", size = 785498, upload-time = "2026-05-09T23:13:52.25Z" }, - { url = "https://files.pythonhosted.org/packages/7f/ab/cb0999802dcb0fb95b1ab005e8d4163d8afdd67efc2cb6b6630ac13f8cb1/regex-2026.5.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:fd190e88a895a8901325fad284a3f74ea52b1da8525b76cc811fa9b1edf0ce2b", size = 801348, upload-time = "2026-05-09T23:13:54.127Z" }, - { url = "https://files.pythonhosted.org/packages/7d/62/8ca59a24c55bc34d166eefaf3717bd77772f329fdbf984d86581e0a3571c/regex-2026.5.9-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:8e76e8161ad00694cfce6767d5dea860c6391ac5b83e5c3a39661e696f11fc7e", size = 866493, upload-time = "2026-05-09T23:13:56.067Z" }, - { url = "https://files.pythonhosted.org/packages/8d/3d/30f2ae62cef3278bb5bb821f467277a55fb73f01032cf85997e15e8289a8/regex-2026.5.9-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ddda5340e6c01a293027dd46232fa79eaff1b48058ce7a98f572b6445b088041", size = 772811, upload-time = "2026-05-09T23:13:57.867Z" }, - { url = "https://files.pythonhosted.org/packages/d8/ae/7d2089bcd78ad0c0161bc684339df50032acb438a7bd3305e7ddb1193cec/regex-2026.5.9-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:205109e96b3cf5adf8f4cd62bedde9487feb282b9497a3535451e5a24cd706a0", size = 856584, upload-time = "2026-05-09T23:13:59.679Z" }, - { url = "https://files.pythonhosted.org/packages/a9/29/92ff47f75990131ea4f24ba17819e5a9d141e10819807e09addd73409af6/regex-2026.5.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dfbe4579b9f08036aa7d101d1835437a20783574ac66327e6b29b4018a138081", size = 803453, upload-time = "2026-05-09T23:14:01.978Z" }, - { url = "https://files.pythonhosted.org/packages/04/99/eff29f1037dcab36702c9ee5d6858cf1ce2336ea8ea2987f64245b99ea5e/regex-2026.5.9-cp313-cp313t-win32.whl", hash = "sha256:ed2c9e8068b614c574d8d30e543d617cf5379b0535d46f97ef00e904745a08b5", size = 269951, upload-time = "2026-05-09T23:14:03.661Z" }, - { url = "https://files.pythonhosted.org/packages/0e/9d/8870b8981d27b22cda77bb26a5ac7ebfa9c7d9e0dea195a834a82380e748/regex-2026.5.9-cp313-cp313t-win_amd64.whl", hash = "sha256:b46b0f094dc1d3b90356c85a0bd2c9bafc4a6a190b9d6f8ddd5a033b6e088ed4", size = 281240, upload-time = "2026-05-09T23:14:05.56Z" }, - { url = "https://files.pythonhosted.org/packages/72/b1/3379415e8f135c13ac551353397cc4fe97b4978f3cac73c5fcbcded548b8/regex-2026.5.9-cp313-cp313t-win_arm64.whl", hash = "sha256:872acc074bd29ffc9913ecdfedf6ea77502312ca44a4aa0d3779089c6069d8de", size = 272383, upload-time = "2026-05-09T23:14:07.843Z" }, - { url = "https://files.pythonhosted.org/packages/13/3e/9c3cd292d8808b3645a2ce517e200179b6d0e903f176300bd8b542e14de5/regex-2026.5.9-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:1bd7587a2948b4085195d5a3374eaf4a425dc3e55784c038175355ecf3bbbf8a", size = 490376, upload-time = "2026-05-09T23:14:09.64Z" }, - { url = "https://files.pythonhosted.org/packages/60/70/d43ee8a2ca0a8b68d167f21658b85520ac0574617c7f320367c5047f7556/regex-2026.5.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:dea2e88e1cce4522496cce630e11e67b98b7076620bc4336c3f674bc21a375f4", size = 291964, upload-time = "2026-05-09T23:14:11.424Z" }, - { url = "https://files.pythonhosted.org/packages/21/91/9d50b433828d8e74196904e168a43abf1e6e88b2a15d47ed742456720c37/regex-2026.5.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2099f7e7ff7b6aa3192312650a56e91cc091e49d50b04e4f6f8b6e28b3b27f1c", size = 289682, upload-time = "2026-05-09T23:14:13.123Z" }, - { url = "https://files.pythonhosted.org/packages/3e/d2/b835e3cafbb9d977736912436259ff551d60919f7d7b3d37d46659c63564/regex-2026.5.9-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecd353045824e4477562a2ac718c25799cdaaa41f7aa925a806a8a3e6848a5b9", size = 796996, upload-time = "2026-05-09T23:14:14.923Z" }, - { url = "https://files.pythonhosted.org/packages/2c/a6/9f992d00019166b9de01c546dd4549bc679f2a68df11b877740b0760b7c2/regex-2026.5.9-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:65c8c8c37377794bd5b2f3ebe51919042bf17aec802e23c833d89782ed0c78af", size = 866089, upload-time = "2026-05-09T23:14:17.757Z" }, - { url = "https://files.pythonhosted.org/packages/e0/08/4d32af657e049b19cb62b02e46e38fe1518797bfb2203ee93a510b21b0dc/regex-2026.5.9-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5b73ab8afcf66c622db143d1c6fda4e58e4d537ee4f125229ad47b1ab80f34c0", size = 911530, upload-time = "2026-05-09T23:14:20.353Z" }, - { url = "https://files.pythonhosted.org/packages/d9/27/2af43dd1dc201d1fecefda64a45f4ad0995855b92724f795a777b402ee69/regex-2026.5.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0de5cf193997384ed2ca6f1cd4f78055b255d93d82d5a8cd6ba0d11c10b167e4", size = 800643, upload-time = "2026-05-09T23:14:22.265Z" }, - { url = "https://files.pythonhosted.org/packages/a4/dd/23a249047013b5321d4a60c4d2437462086f601b061776a525e5fba2a59f/regex-2026.5.9-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d641a8c9a61618047796d572a39a79b26167b0411d2c3031937b2fe2d081e2cf", size = 777223, upload-time = "2026-05-09T23:14:24.179Z" }, - { url = "https://files.pythonhosted.org/packages/94/6a/e85ed9538cd19586d0465076a4578a12e093ce776d15f3f8ce92733a8dd6/regex-2026.5.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:24b2355ef5cc9aa5b8f07d17704face1c166fdcc2290fa7bd6e6c925655a8346", size = 785760, upload-time = "2026-05-09T23:14:26.065Z" }, - { url = "https://files.pythonhosted.org/packages/2a/c4/f25473209438638e947c55f9156fd8f236f74169229028cc99116380868e/regex-2026.5.9-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:a24852d3c29ad9e47593593d8a247c44ccc3d0548ef12c822d6ed0810affe676", size = 860891, upload-time = "2026-05-09T23:14:28.17Z" }, - { url = "https://files.pythonhosted.org/packages/f9/f7/f4f86e3c74419c37370e91f150ae0c2ef7d34b2e0e4cdd5da046a02e4022/regex-2026.5.9-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:916714069da19329ef7de197dcbc77bb3104145c7c2c864dbfbe318f46b88b14", size = 765891, upload-time = "2026-05-09T23:14:30.06Z" }, - { url = "https://files.pythonhosted.org/packages/26/70/704d8e13765939146b1cd0ef4e2feb71d7929727d2290f026eed10095955/regex-2026.5.9-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:fa411799ca8da32a8d38d020a88faa5b6f91657d284761352940ecf9f7c3bbdd", size = 851380, upload-time = "2026-05-09T23:14:32.123Z" }, - { url = "https://files.pythonhosted.org/packages/26/29/1a13582a8460038edc38e49f64ceb0dd7c60f5caba77571f4bf6601965d9/regex-2026.5.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1e6da47d679b7010ef27556b6e0f99771b744936db1792a10ceac6547ae1503e", size = 789350, upload-time = "2026-05-09T23:14:34.799Z" }, - { url = "https://files.pythonhosted.org/packages/73/56/3dcafe34fc72e271d62ad9a291801e88a1457bb251c132f15fcc2e5aad1a/regex-2026.5.9-cp314-cp314-win32.whl", hash = "sha256:98bd73080e8756255137e1bd3f3f00295bbc5aa383c0e0f973920e9134d7c4ad", size = 272130, upload-time = "2026-05-09T23:14:36.729Z" }, - { url = "https://files.pythonhosted.org/packages/d0/9c/02eebf0be95efe416c664db7fb8b6b05b7a0b06a7544f2884f2558b0526f/regex-2026.5.9-cp314-cp314-win_amd64.whl", hash = "sha256:ff8d372ac2acdc048d1c19916f27ee61bc5722728458ba6ca5052f2c72d51763", size = 280999, upload-time = "2026-05-09T23:14:39.126Z" }, - { url = "https://files.pythonhosted.org/packages/70/5a/1dd1abee76cb7a846a0bcf42fdc87e5720c3c33c24f3e37814310a513d9f/regex-2026.5.9-cp314-cp314-win_arm64.whl", hash = "sha256:e1d93bf647916292e8edcec150c07ddf3dc50179ccaf770c04a7f9e452155372", size = 273500, upload-time = "2026-05-09T23:14:41.059Z" }, - { url = "https://files.pythonhosted.org/packages/86/c1/c5f619b0057a7965cb78ec559c1d7a45ce8c99a35bea95483d64959a93d9/regex-2026.5.9-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:83d0ee4a57d1c87cb549e195ec300b8f0ec3a82eba66d835e4e2ed8634fe4499", size = 494269, upload-time = "2026-05-09T23:14:42.869Z" }, - { url = "https://files.pythonhosted.org/packages/05/2c/5d01f1aee33de4bbe60c8452945bfc8477ca7c5ae4450f6bfe711036cb36/regex-2026.5.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d3d7eb5c9a7f6df82ed3cfac9beb93882a5cbcb5b8b157b56cb2b3b276574ac1", size = 293954, upload-time = "2026-05-09T23:14:44.822Z" }, - { url = "https://files.pythonhosted.org/packages/7a/fe/e8988b2ae2108c6ef71bd4aa8d87fbe257976dd0810e826cd75f701c68b6/regex-2026.5.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:075160bf16658e16d35233300b8453aac25de4cbea808d22348b6979668e924d", size = 292405, upload-time = "2026-05-09T23:14:47.211Z" }, - { url = "https://files.pythonhosted.org/packages/79/34/d2b0937faa7859263f7f0a3c6b103a1296306be6952dc173d0154e9a2f49/regex-2026.5.9-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45375819235558a4ff1c4971dc32881f022613abdb180128f5cb4768c1765a1c", size = 811855, upload-time = "2026-05-09T23:14:49.21Z" }, - { url = "https://files.pythonhosted.org/packages/80/fe/daf53a47457a8486db66c66c01ceb9c2303eecee3f87197f1e77eb1a736d/regex-2026.5.9-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ead4b163ac30a29574510cd4b3e2e985ac5290c05fc7095557d6a5f403fc31b5", size = 871189, upload-time = "2026-05-09T23:14:51.555Z" }, - { url = "https://files.pythonhosted.org/packages/1c/75/058fc4470cbfbf57d800aff1a0022b929a3f9fa553ee10a0cdf2070eb31f/regex-2026.5.9-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8c6e4218fbdfbcd4f6c19efca40930d24a621bf4b48cb76bc6640543bd28ef20", size = 917485, upload-time = "2026-05-09T23:14:53.633Z" }, - { url = "https://files.pythonhosted.org/packages/88/e7/179cfda3a28bc843b5c6cfe7f79f23489c791ed95f151083803660878432/regex-2026.5.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6351571c8a42b505eb555c0dc47d740d0fb66977dc142919eea6f4325b7c56a0", size = 816369, upload-time = "2026-05-09T23:14:56.198Z" }, - { url = "https://files.pythonhosted.org/packages/41/90/6f0cc422071688266d344fca8462d787cba0a2c144acb25721f9a61ec265/regex-2026.5.9-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:002205cafd2a9e78c6290c7d1df277bf3277b3b7a30e0b4bb0dac2e2e3f7cb2d", size = 785869, upload-time = "2026-05-09T23:14:58.602Z" }, - { url = "https://files.pythonhosted.org/packages/02/67/a31f1760f09c27b251ef39e9beb541f462cf977381d067faa764c2c0e393/regex-2026.5.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8abd33fef90b2a9efac5557d6033ca82d1195ed3a15fea5af15ba7b463c6a63b", size = 801427, upload-time = "2026-05-09T23:15:00.642Z" }, - { url = "https://files.pythonhosted.org/packages/e3/c4/1a80654597b6bc1e1ea0494824c31200e8a956abe290afae9b19a166a148/regex-2026.5.9-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:31037c82eccb44b7ea2e9e221d7c01429430e989a1f4b91ea5a855f6017b509a", size = 866482, upload-time = "2026-05-09T23:15:03.384Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/960724e06482c08466ff5611e242e86f80062949cdf6b4b9cc317b9dd93d/regex-2026.5.9-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:5604dfd046dc37eca90250fc3be938b076c8059fa772ac0ed6f499b0f0fb0415", size = 773022, upload-time = "2026-05-09T23:15:05.625Z" }, - { url = "https://files.pythonhosted.org/packages/50/a8/a9979c3e7918280e93159ebcab5ef1a65116dd4f3bd6091be0eae4a126e8/regex-2026.5.9-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0e1b1b4e496afbb24f4a62aba855ee4f88f25578927697b340702e48c9ee6bc2", size = 856642, upload-time = "2026-05-09T23:15:07.966Z" }, - { url = "https://files.pythonhosted.org/packages/fe/d4/a9b732f2f0072c0ab12227483abb24fffcb9f73f8a2b203df0a6d0434735/regex-2026.5.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:be3372b9df6ddecff6486d37e19095a7b4973137caf5512407a89f4455361f41", size = 803552, upload-time = "2026-05-09T23:15:10.215Z" }, - { url = "https://files.pythonhosted.org/packages/d5/fe/1b3113817447a1d4155e4ac76d2e072f42c0bcba2f43fa8a0e756ea2cd91/regex-2026.5.9-cp314-cp314t-win32.whl", hash = "sha256:3ddd90103f9e5c471c49c7852ecc1fe27c7e45eb99e977aefe7caa4e779f4f58", size = 275746, upload-time = "2026-05-09T23:15:12.609Z" }, - { url = "https://files.pythonhosted.org/packages/92/73/93d42045302636c91f2e5ef588b65b84b01428f28ec77de256b1dfdfbe5c/regex-2026.5.9-cp314-cp314t-win_amd64.whl", hash = "sha256:ca518ed29c46eecba6010b15f1b9a479314d2de409536e71b6a13aa04e3b8a77", size = 285685, upload-time = "2026-05-09T23:15:15.086Z" }, - { url = "https://files.pythonhosted.org/packages/da/80/35b4c33c804a165a7f55289afda3ea9e3eb6d15800341a2d66455c0f1f30/regex-2026.5.9-cp314-cp314t-win_arm64.whl", hash = "sha256:5e41809d2683fcde7d5a8c87a6567ba1fb1ce0de9f31bff578de00a4b2d76daa", size = 275713, upload-time = "2026-05-09T23:15:16.98Z" }, -] - -[[package]] -name = "requests" -version = "2.34.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "charset-normalizer" }, - { name = "idna" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, -] - -[[package]] -name = "rich" -version = "15.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, -] - -[[package]] -name = "safe-netrc" -version = "1.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e4/f1/fb3c8ccd4a8963d7400cfd80100d5ae7fd95d49ace3a6722736bb4c1c1a6/safe-netrc-1.0.1.tar.gz", hash = "sha256:1dcc7263b4d9ce72e0109d8e2bc9ba89c8056ccc618d26c8c94802c6fd753720", size = 10846, upload-time = "2022-11-29T13:24:35.252Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/26/2a/b5b09ff2781c7ea694b337f65f52a00319396578c049af8890ff9b4b8232/safe_netrc-1.0.1-py3-none-any.whl", hash = "sha256:5f0dd6a5e304b1da3be220f15efedbf09e50779fe90462143c228c781b9d8218", size = 10891, upload-time = "2022-11-29T13:24:33.626Z" }, -] - -[[package]] -name = "scikit-learn" -version = "1.7.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -dependencies = [ - { name = "joblib", marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "threadpoolctl", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda", size = 7193136, upload-time = "2025-09-09T08:21:29.075Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/3e/daed796fd69cce768b8788401cc464ea90b306fb196ae1ffed0b98182859/scikit_learn-1.7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b33579c10a3081d076ab403df4a4190da4f4432d443521674637677dc91e61f", size = 9336221, upload-time = "2025-09-09T08:20:19.328Z" }, - { url = "https://files.pythonhosted.org/packages/1c/ce/af9d99533b24c55ff4e18d9b7b4d9919bbc6cd8f22fe7a7be01519a347d5/scikit_learn-1.7.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:36749fb62b3d961b1ce4fedf08fa57a1986cd409eff2d783bca5d4b9b5fce51c", size = 8653834, upload-time = "2025-09-09T08:20:22.073Z" }, - { url = "https://files.pythonhosted.org/packages/58/0e/8c2a03d518fb6bd0b6b0d4b114c63d5f1db01ff0f9925d8eb10960d01c01/scikit_learn-1.7.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7a58814265dfc52b3295b1900cfb5701589d30a8bb026c7540f1e9d3499d5ec8", size = 9660938, upload-time = "2025-09-09T08:20:24.327Z" }, - { url = "https://files.pythonhosted.org/packages/2b/75/4311605069b5d220e7cf5adabb38535bd96f0079313cdbb04b291479b22a/scikit_learn-1.7.2-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a847fea807e278f821a0406ca01e387f97653e284ecbd9750e3ee7c90347f18", size = 9477818, upload-time = "2025-09-09T08:20:26.845Z" }, - { url = "https://files.pythonhosted.org/packages/7f/9b/87961813c34adbca21a6b3f6b2bea344c43b30217a6d24cc437c6147f3e8/scikit_learn-1.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:ca250e6836d10e6f402436d6463d6c0e4d8e0234cfb6a9a47835bd392b852ce5", size = 8886969, upload-time = "2025-09-09T08:20:29.329Z" }, - { url = "https://files.pythonhosted.org/packages/43/83/564e141eef908a5863a54da8ca342a137f45a0bfb71d1d79704c9894c9d1/scikit_learn-1.7.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7509693451651cd7361d30ce4e86a1347493554f172b1c72a39300fa2aea79e", size = 9331967, upload-time = "2025-09-09T08:20:32.421Z" }, - { url = "https://files.pythonhosted.org/packages/18/d6/ba863a4171ac9d7314c4d3fc251f015704a2caeee41ced89f321c049ed83/scikit_learn-1.7.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:0486c8f827c2e7b64837c731c8feff72c0bd2b998067a8a9cbc10643c31f0fe1", size = 8648645, upload-time = "2025-09-09T08:20:34.436Z" }, - { url = "https://files.pythonhosted.org/packages/ef/0e/97dbca66347b8cf0ea8b529e6bb9367e337ba2e8be0ef5c1a545232abfde/scikit_learn-1.7.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:89877e19a80c7b11a2891a27c21c4894fb18e2c2e077815bcade10d34287b20d", size = 9715424, upload-time = "2025-09-09T08:20:36.776Z" }, - { url = "https://files.pythonhosted.org/packages/f7/32/1f3b22e3207e1d2c883a7e09abb956362e7d1bd2f14458c7de258a26ac15/scikit_learn-1.7.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8da8bf89d4d79aaec192d2bda62f9b56ae4e5b4ef93b6a56b5de4977e375c1f1", size = 9509234, upload-time = "2025-09-09T08:20:38.957Z" }, - { url = "https://files.pythonhosted.org/packages/9f/71/34ddbd21f1da67c7a768146968b4d0220ee6831e4bcbad3e03dd3eae88b6/scikit_learn-1.7.2-cp311-cp311-win_amd64.whl", hash = "sha256:9b7ed8d58725030568523e937c43e56bc01cadb478fc43c042a9aca1dacb3ba1", size = 8894244, upload-time = "2025-09-09T08:20:41.166Z" }, - { url = "https://files.pythonhosted.org/packages/a7/aa/3996e2196075689afb9fce0410ebdb4a09099d7964d061d7213700204409/scikit_learn-1.7.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8d91a97fa2b706943822398ab943cde71858a50245e31bc71dba62aab1d60a96", size = 9259818, upload-time = "2025-09-09T08:20:43.19Z" }, - { url = "https://files.pythonhosted.org/packages/43/5d/779320063e88af9c4a7c2cf463ff11c21ac9c8bd730c4a294b0000b666c9/scikit_learn-1.7.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:acbc0f5fd2edd3432a22c69bed78e837c70cf896cd7993d71d51ba6708507476", size = 8636997, upload-time = "2025-09-09T08:20:45.468Z" }, - { url = "https://files.pythonhosted.org/packages/5c/d0/0c577d9325b05594fdd33aa970bf53fb673f051a45496842caee13cfd7fe/scikit_learn-1.7.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e5bf3d930aee75a65478df91ac1225ff89cd28e9ac7bd1196853a9229b6adb0b", size = 9478381, upload-time = "2025-09-09T08:20:47.982Z" }, - { url = "https://files.pythonhosted.org/packages/82/70/8bf44b933837ba8494ca0fc9a9ab60f1c13b062ad0197f60a56e2fc4c43e/scikit_learn-1.7.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4d6e9deed1a47aca9fe2f267ab8e8fe82ee20b4526b2c0cd9e135cea10feb44", size = 9300296, upload-time = "2025-09-09T08:20:50.366Z" }, - { url = "https://files.pythonhosted.org/packages/c6/99/ed35197a158f1fdc2fe7c3680e9c70d0128f662e1fee4ed495f4b5e13db0/scikit_learn-1.7.2-cp312-cp312-win_amd64.whl", hash = "sha256:6088aa475f0785e01bcf8529f55280a3d7d298679f50c0bb70a2364a82d0b290", size = 8731256, upload-time = "2025-09-09T08:20:52.627Z" }, - { url = "https://files.pythonhosted.org/packages/ae/93/a3038cb0293037fd335f77f31fe053b89c72f17b1c8908c576c29d953e84/scikit_learn-1.7.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0b7dacaa05e5d76759fb071558a8b5130f4845166d88654a0f9bdf3eb57851b7", size = 9212382, upload-time = "2025-09-09T08:20:54.731Z" }, - { url = "https://files.pythonhosted.org/packages/40/dd/9a88879b0c1104259136146e4742026b52df8540c39fec21a6383f8292c7/scikit_learn-1.7.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:abebbd61ad9e1deed54cca45caea8ad5f79e1b93173dece40bb8e0c658dbe6fe", size = 8592042, upload-time = "2025-09-09T08:20:57.313Z" }, - { url = "https://files.pythonhosted.org/packages/46/af/c5e286471b7d10871b811b72ae794ac5fe2989c0a2df07f0ec723030f5f5/scikit_learn-1.7.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:502c18e39849c0ea1a5d681af1dbcf15f6cce601aebb657aabbfe84133c1907f", size = 9434180, upload-time = "2025-09-09T08:20:59.671Z" }, - { url = "https://files.pythonhosted.org/packages/f1/fd/df59faa53312d585023b2da27e866524ffb8faf87a68516c23896c718320/scikit_learn-1.7.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a4c328a71785382fe3fe676a9ecf2c86189249beff90bf85e22bdb7efaf9ae0", size = 9283660, upload-time = "2025-09-09T08:21:01.71Z" }, - { url = "https://files.pythonhosted.org/packages/a7/c7/03000262759d7b6f38c836ff9d512f438a70d8a8ddae68ee80de72dcfb63/scikit_learn-1.7.2-cp313-cp313-win_amd64.whl", hash = "sha256:63a9afd6f7b229aad94618c01c252ce9e6fa97918c5ca19c9a17a087d819440c", size = 8702057, upload-time = "2025-09-09T08:21:04.234Z" }, - { url = "https://files.pythonhosted.org/packages/55/87/ef5eb1f267084532c8e4aef98a28b6ffe7425acbfd64b5e2f2e066bc29b3/scikit_learn-1.7.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:9acb6c5e867447b4e1390930e3944a005e2cb115922e693c08a323421a6966e8", size = 9558731, upload-time = "2025-09-09T08:21:06.381Z" }, - { url = "https://files.pythonhosted.org/packages/93/f8/6c1e3fc14b10118068d7938878a9f3f4e6d7b74a8ddb1e5bed65159ccda8/scikit_learn-1.7.2-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:2a41e2a0ef45063e654152ec9d8bcfc39f7afce35b08902bfe290c2498a67a6a", size = 9038852, upload-time = "2025-09-09T08:21:08.628Z" }, - { url = "https://files.pythonhosted.org/packages/83/87/066cafc896ee540c34becf95d30375fe5cbe93c3b75a0ee9aa852cd60021/scikit_learn-1.7.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:98335fb98509b73385b3ab2bd0639b1f610541d3988ee675c670371d6a87aa7c", size = 9527094, upload-time = "2025-09-09T08:21:11.486Z" }, - { url = "https://files.pythonhosted.org/packages/9c/2b/4903e1ccafa1f6453b1ab78413938c8800633988c838aa0be386cbb33072/scikit_learn-1.7.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:191e5550980d45449126e23ed1d5e9e24b2c68329ee1f691a3987476e115e09c", size = 9367436, upload-time = "2025-09-09T08:21:13.602Z" }, - { url = "https://files.pythonhosted.org/packages/b5/aa/8444be3cfb10451617ff9d177b3c190288f4563e6c50ff02728be67ad094/scikit_learn-1.7.2-cp313-cp313t-win_amd64.whl", hash = "sha256:57dc4deb1d3762c75d685507fbd0bc17160144b2f2ba4ccea5dc285ab0d0e973", size = 9275749, upload-time = "2025-09-09T08:21:15.96Z" }, - { url = "https://files.pythonhosted.org/packages/d9/82/dee5acf66837852e8e68df6d8d3a6cb22d3df997b733b032f513d95205b7/scikit_learn-1.7.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fa8f63940e29c82d1e67a45d5297bdebbcb585f5a5a50c4914cc2e852ab77f33", size = 9208906, upload-time = "2025-09-09T08:21:18.557Z" }, - { url = "https://files.pythonhosted.org/packages/3c/30/9029e54e17b87cb7d50d51a5926429c683d5b4c1732f0507a6c3bed9bf65/scikit_learn-1.7.2-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:f95dc55b7902b91331fa4e5845dd5bde0580c9cd9612b1b2791b7e80c3d32615", size = 8627836, upload-time = "2025-09-09T08:21:20.695Z" }, - { url = "https://files.pythonhosted.org/packages/60/18/4a52c635c71b536879f4b971c2cedf32c35ee78f48367885ed8025d1f7ee/scikit_learn-1.7.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9656e4a53e54578ad10a434dc1f993330568cfee176dff07112b8785fb413106", size = 9426236, upload-time = "2025-09-09T08:21:22.645Z" }, - { url = "https://files.pythonhosted.org/packages/99/7e/290362f6ab582128c53445458a5befd471ed1ea37953d5bcf80604619250/scikit_learn-1.7.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96dc05a854add0e50d3f47a1ef21a10a595016da5b007c7d9cd9d0bffd1fcc61", size = 9312593, upload-time = "2025-09-09T08:21:24.65Z" }, - { url = "https://files.pythonhosted.org/packages/8e/87/24f541b6d62b1794939ae6422f8023703bbf6900378b2b34e0b4384dfefd/scikit_learn-1.7.2-cp314-cp314-win_amd64.whl", hash = "sha256:bb24510ed3f9f61476181e4db51ce801e2ba37541def12dc9333b946fc7a9cf8", size = 8820007, upload-time = "2025-09-09T08:21:26.713Z" }, -] - -[[package]] -name = "scikit-learn" -version = "1.8.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'win32'", - "python_full_version == '3.12.*' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version == '3.12.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", -] -dependencies = [ - { name = "joblib", marker = "python_full_version >= '3.11'" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "threadpoolctl", marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0e/d4/40988bf3b8e34feec1d0e6a051446b1f66225f8529b9309becaeef62b6c4/scikit_learn-1.8.0.tar.gz", hash = "sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd", size = 7335585, upload-time = "2025-12-10T07:08:53.618Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c9/92/53ea2181da8ac6bf27170191028aee7251f8f841f8d3edbfdcaf2008fde9/scikit_learn-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:146b4d36f800c013d267b29168813f7a03a43ecd2895d04861f1240b564421da", size = 8595835, upload-time = "2025-12-10T07:07:39.385Z" }, - { url = "https://files.pythonhosted.org/packages/01/18/d154dc1638803adf987910cdd07097d9c526663a55666a97c124d09fb96a/scikit_learn-1.8.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:f984ca4b14914e6b4094c5d52a32ea16b49832c03bd17a110f004db3c223e8e1", size = 8080381, upload-time = "2025-12-10T07:07:41.93Z" }, - { url = "https://files.pythonhosted.org/packages/8a/44/226142fcb7b7101e64fdee5f49dbe6288d4c7af8abf593237b70fca080a4/scikit_learn-1.8.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e30adb87f0cc81c7690a84f7932dd66be5bac57cfe16b91cb9151683a4a2d3b", size = 8799632, upload-time = "2025-12-10T07:07:43.899Z" }, - { url = "https://files.pythonhosted.org/packages/36/4d/4a67f30778a45d542bbea5db2dbfa1e9e100bf9ba64aefe34215ba9f11f6/scikit_learn-1.8.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ada8121bcb4dac28d930febc791a69f7cb1673c8495e5eee274190b73a4559c1", size = 9103788, upload-time = "2025-12-10T07:07:45.982Z" }, - { url = "https://files.pythonhosted.org/packages/89/3c/45c352094cfa60050bcbb967b1faf246b22e93cb459f2f907b600f2ceda5/scikit_learn-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:c57b1b610bd1f40ba43970e11ce62821c2e6569e4d74023db19c6b26f246cb3b", size = 8081706, upload-time = "2025-12-10T07:07:48.111Z" }, - { url = "https://files.pythonhosted.org/packages/3d/46/5416595bb395757f754feb20c3d776553a386b661658fb21b7c814e89efe/scikit_learn-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:2838551e011a64e3053ad7618dda9310175f7515f1742fa2d756f7c874c05961", size = 7688451, upload-time = "2025-12-10T07:07:49.873Z" }, - { url = "https://files.pythonhosted.org/packages/90/74/e6a7cc4b820e95cc38cf36cd74d5aa2b42e8ffc2d21fe5a9a9c45c1c7630/scikit_learn-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5fb63362b5a7ddab88e52b6dbb47dac3fd7dafeee740dc6c8d8a446ddedade8e", size = 8548242, upload-time = "2025-12-10T07:07:51.568Z" }, - { url = "https://files.pythonhosted.org/packages/49/d8/9be608c6024d021041c7f0b3928d4749a706f4e2c3832bbede4fb4f58c95/scikit_learn-1.8.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:5025ce924beccb28298246e589c691fe1b8c1c96507e6d27d12c5fadd85bfd76", size = 8079075, upload-time = "2025-12-10T07:07:53.697Z" }, - { url = "https://files.pythonhosted.org/packages/dd/47/f187b4636ff80cc63f21cd40b7b2d177134acaa10f6bb73746130ee8c2e5/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4496bb2cf7a43ce1a2d7524a79e40bc5da45cf598dbf9545b7e8316ccba47bb4", size = 8660492, upload-time = "2025-12-10T07:07:55.574Z" }, - { url = "https://files.pythonhosted.org/packages/97/74/b7a304feb2b49df9fafa9382d4d09061a96ee9a9449a7cbea7988dda0828/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0bcfe4d0d14aec44921545fd2af2338c7471de9cb701f1da4c9d85906ab847a", size = 8931904, upload-time = "2025-12-10T07:07:57.666Z" }, - { url = "https://files.pythonhosted.org/packages/9f/c4/0ab22726a04ede56f689476b760f98f8f46607caecff993017ac1b64aa5d/scikit_learn-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:35c007dedb2ffe38fe3ee7d201ebac4a2deccd2408e8621d53067733e3c74809", size = 8019359, upload-time = "2025-12-10T07:07:59.838Z" }, - { url = "https://files.pythonhosted.org/packages/24/90/344a67811cfd561d7335c1b96ca21455e7e472d281c3c279c4d3f2300236/scikit_learn-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:8c497fff237d7b4e07e9ef1a640887fa4fb765647f86fbe00f969ff6280ce2bb", size = 7641898, upload-time = "2025-12-10T07:08:01.36Z" }, - { url = "https://files.pythonhosted.org/packages/03/aa/e22e0768512ce9255eba34775be2e85c2048da73da1193e841707f8f039c/scikit_learn-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0d6ae97234d5d7079dc0040990a6f7aeb97cb7fa7e8945f1999a429b23569e0a", size = 8513770, upload-time = "2025-12-10T07:08:03.251Z" }, - { url = "https://files.pythonhosted.org/packages/58/37/31b83b2594105f61a381fc74ca19e8780ee923be2d496fcd8d2e1147bd99/scikit_learn-1.8.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:edec98c5e7c128328124a029bceb09eda2d526997780fef8d65e9a69eead963e", size = 8044458, upload-time = "2025-12-10T07:08:05.336Z" }, - { url = "https://files.pythonhosted.org/packages/2d/5a/3f1caed8765f33eabb723596666da4ebbf43d11e96550fb18bdec42b467b/scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:74b66d8689d52ed04c271e1329f0c61635bcaf5b926db9b12d58914cdc01fe57", size = 8610341, upload-time = "2025-12-10T07:08:07.732Z" }, - { url = "https://files.pythonhosted.org/packages/38/cf/06896db3f71c75902a8e9943b444a56e727418f6b4b4a90c98c934f51ed4/scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8fdf95767f989b0cfedb85f7ed8ca215d4be728031f56ff5a519ee1e3276dc2e", size = 8900022, upload-time = "2025-12-10T07:08:09.862Z" }, - { url = "https://files.pythonhosted.org/packages/1c/f9/9b7563caf3ec8873e17a31401858efab6b39a882daf6c1bfa88879c0aa11/scikit_learn-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:2de443b9373b3b615aec1bb57f9baa6bb3a9bd093f1269ba95c17d870422b271", size = 7989409, upload-time = "2025-12-10T07:08:12.028Z" }, - { url = "https://files.pythonhosted.org/packages/49/bd/1f4001503650e72c4f6009ac0c4413cb17d2d601cef6f71c0453da2732fc/scikit_learn-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:eddde82a035681427cbedded4e6eff5e57fa59216c2e3e90b10b19ab1d0a65c3", size = 7619760, upload-time = "2025-12-10T07:08:13.688Z" }, - { url = "https://files.pythonhosted.org/packages/d2/7d/a630359fc9dcc95496588c8d8e3245cc8fd81980251079bc09c70d41d951/scikit_learn-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7cc267b6108f0a1499a734167282c00c4ebf61328566b55ef262d48e9849c735", size = 8826045, upload-time = "2025-12-10T07:08:15.215Z" }, - { url = "https://files.pythonhosted.org/packages/cc/56/a0c86f6930cfcd1c7054a2bc417e26960bb88d32444fe7f71d5c2cfae891/scikit_learn-1.8.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:fe1c011a640a9f0791146011dfd3c7d9669785f9fed2b2a5f9e207536cf5c2fd", size = 8420324, upload-time = "2025-12-10T07:08:17.561Z" }, - { url = "https://files.pythonhosted.org/packages/46/1e/05962ea1cebc1cf3876667ecb14c283ef755bf409993c5946ade3b77e303/scikit_learn-1.8.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72358cce49465d140cc4e7792015bb1f0296a9742d5622c67e31399b75468b9e", size = 8680651, upload-time = "2025-12-10T07:08:19.952Z" }, - { url = "https://files.pythonhosted.org/packages/fe/56/a85473cd75f200c9759e3a5f0bcab2d116c92a8a02ee08ccd73b870f8bb4/scikit_learn-1.8.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:80832434a6cc114f5219211eec13dcbc16c2bac0e31ef64c6d346cde3cf054cb", size = 8925045, upload-time = "2025-12-10T07:08:22.11Z" }, - { url = "https://files.pythonhosted.org/packages/cc/b7/64d8cfa896c64435ae57f4917a548d7ac7a44762ff9802f75a79b77cb633/scikit_learn-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ee787491dbfe082d9c3013f01f5991658b0f38aa8177e4cd4bf434c58f551702", size = 8507994, upload-time = "2025-12-10T07:08:23.943Z" }, - { url = "https://files.pythonhosted.org/packages/5e/37/e192ea709551799379958b4c4771ec507347027bb7c942662c7fbeba31cb/scikit_learn-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf97c10a3f5a7543f9b88cbf488d33d175e9146115a451ae34568597ba33dcde", size = 7869518, upload-time = "2025-12-10T07:08:25.71Z" }, - { url = "https://files.pythonhosted.org/packages/24/05/1af2c186174cc92dcab2233f327336058c077d38f6fe2aceb08e6ab4d509/scikit_learn-1.8.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c22a2da7a198c28dd1a6e1136f19c830beab7fdca5b3e5c8bba8394f8a5c45b3", size = 8528667, upload-time = "2025-12-10T07:08:27.541Z" }, - { url = "https://files.pythonhosted.org/packages/a8/25/01c0af38fe969473fb292bba9dc2b8f9b451f3112ff242c647fee3d0dfe7/scikit_learn-1.8.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:6b595b07a03069a2b1740dc08c2299993850ea81cce4fe19b2421e0c970de6b7", size = 8066524, upload-time = "2025-12-10T07:08:29.822Z" }, - { url = "https://files.pythonhosted.org/packages/be/ce/a0623350aa0b68647333940ee46fe45086c6060ec604874e38e9ab7d8e6c/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:29ffc74089f3d5e87dfca4c2c8450f88bdc61b0fc6ed5d267f3988f19a1309f6", size = 8657133, upload-time = "2025-12-10T07:08:31.865Z" }, - { url = "https://files.pythonhosted.org/packages/b8/cb/861b41341d6f1245e6ca80b1c1a8c4dfce43255b03df034429089ca2a2c5/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fb65db5d7531bccf3a4f6bec3462223bea71384e2cda41da0f10b7c292b9e7c4", size = 8923223, upload-time = "2025-12-10T07:08:34.166Z" }, - { url = "https://files.pythonhosted.org/packages/76/18/a8def8f91b18cd1ba6e05dbe02540168cb24d47e8dcf69e8d00b7da42a08/scikit_learn-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:56079a99c20d230e873ea40753102102734c5953366972a71d5cb39a32bc40c6", size = 8096518, upload-time = "2025-12-10T07:08:36.339Z" }, - { url = "https://files.pythonhosted.org/packages/d1/77/482076a678458307f0deb44e29891d6022617b2a64c840c725495bee343f/scikit_learn-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:3bad7565bc9cf37ce19a7c0d107742b320c1285df7aab1a6e2d28780df167242", size = 7754546, upload-time = "2025-12-10T07:08:38.128Z" }, - { url = "https://files.pythonhosted.org/packages/2d/d1/ef294ca754826daa043b2a104e59960abfab4cf653891037d19dd5b6f3cf/scikit_learn-1.8.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:4511be56637e46c25721e83d1a9cea9614e7badc7040c4d573d75fbe257d6fd7", size = 8848305, upload-time = "2025-12-10T07:08:41.013Z" }, - { url = "https://files.pythonhosted.org/packages/5b/e2/b1f8b05138ee813b8e1a4149f2f0d289547e60851fd1bb268886915adbda/scikit_learn-1.8.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:a69525355a641bf8ef136a7fa447672fb54fe8d60cab5538d9eb7c6438543fb9", size = 8432257, upload-time = "2025-12-10T07:08:42.873Z" }, - { url = "https://files.pythonhosted.org/packages/26/11/c32b2138a85dcb0c99f6afd13a70a951bfdff8a6ab42d8160522542fb647/scikit_learn-1.8.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c2656924ec73e5939c76ac4c8b026fc203b83d8900362eb2599d8aee80e4880f", size = 8678673, upload-time = "2025-12-10T07:08:45.362Z" }, - { url = "https://files.pythonhosted.org/packages/c7/57/51f2384575bdec454f4fe4e7a919d696c9ebce914590abf3e52d47607ab8/scikit_learn-1.8.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15fc3b5d19cc2be65404786857f2e13c70c83dd4782676dd6814e3b89dc8f5b9", size = 8922467, upload-time = "2025-12-10T07:08:47.408Z" }, - { url = "https://files.pythonhosted.org/packages/35/4d/748c9e2872637a57981a04adc038dacaa16ba8ca887b23e34953f0b3f742/scikit_learn-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:00d6f1d66fbcf4eba6e356e1420d33cc06c70a45bb1363cd6f6a8e4ebbbdece2", size = 8774395, upload-time = "2025-12-10T07:08:49.337Z" }, - { url = "https://files.pythonhosted.org/packages/60/22/d7b2ebe4704a5e50790ba089d5c2ae308ab6bb852719e6c3bd4f04c3a363/scikit_learn-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:f28dd15c6bb0b66ba09728cf09fd8736c304be29409bd8445a080c1280619e8c", size = 8002647, upload-time = "2025-12-10T07:08:51.601Z" }, -] - -[[package]] -name = "scipy" -version = "1.15.3" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214, upload-time = "2025-05-08T16:13:05.955Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/78/2f/4966032c5f8cc7e6a60f1b2e0ad686293b9474b65246b0c642e3ef3badd0/scipy-1.15.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a345928c86d535060c9c2b25e71e87c39ab2f22fc96e9636bd74d1dbf9de448c", size = 38702770, upload-time = "2025-05-08T16:04:20.849Z" }, - { url = "https://files.pythonhosted.org/packages/a0/6e/0c3bf90fae0e910c274db43304ebe25a6b391327f3f10b5dcc638c090795/scipy-1.15.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:ad3432cb0f9ed87477a8d97f03b763fd1d57709f1bbde3c9369b1dff5503b253", size = 30094511, upload-time = "2025-05-08T16:04:27.103Z" }, - { url = "https://files.pythonhosted.org/packages/ea/b1/4deb37252311c1acff7f101f6453f0440794f51b6eacb1aad4459a134081/scipy-1.15.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:aef683a9ae6eb00728a542b796f52a5477b78252edede72b8327a886ab63293f", size = 22368151, upload-time = "2025-05-08T16:04:31.731Z" }, - { url = "https://files.pythonhosted.org/packages/38/7d/f457626e3cd3c29b3a49ca115a304cebb8cc6f31b04678f03b216899d3c6/scipy-1.15.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:1c832e1bd78dea67d5c16f786681b28dd695a8cb1fb90af2e27580d3d0967e92", size = 25121732, upload-time = "2025-05-08T16:04:36.596Z" }, - { url = "https://files.pythonhosted.org/packages/db/0a/92b1de4a7adc7a15dcf5bddc6e191f6f29ee663b30511ce20467ef9b82e4/scipy-1.15.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:263961f658ce2165bbd7b99fa5135195c3a12d9bef045345016b8b50c315cb82", size = 35547617, upload-time = "2025-05-08T16:04:43.546Z" }, - { url = "https://files.pythonhosted.org/packages/8e/6d/41991e503e51fc1134502694c5fa7a1671501a17ffa12716a4a9151af3df/scipy-1.15.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2abc762b0811e09a0d3258abee2d98e0c703eee49464ce0069590846f31d40", size = 37662964, upload-time = "2025-05-08T16:04:49.431Z" }, - { url = "https://files.pythonhosted.org/packages/25/e1/3df8f83cb15f3500478c889be8fb18700813b95e9e087328230b98d547ff/scipy-1.15.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ed7284b21a7a0c8f1b6e5977ac05396c0d008b89e05498c8b7e8f4a1423bba0e", size = 37238749, upload-time = "2025-05-08T16:04:55.215Z" }, - { url = "https://files.pythonhosted.org/packages/93/3e/b3257cf446f2a3533ed7809757039016b74cd6f38271de91682aa844cfc5/scipy-1.15.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5380741e53df2c566f4d234b100a484b420af85deb39ea35a1cc1be84ff53a5c", size = 40022383, upload-time = "2025-05-08T16:05:01.914Z" }, - { url = "https://files.pythonhosted.org/packages/d1/84/55bc4881973d3f79b479a5a2e2df61c8c9a04fcb986a213ac9c02cfb659b/scipy-1.15.3-cp310-cp310-win_amd64.whl", hash = "sha256:9d61e97b186a57350f6d6fd72640f9e99d5a4a2b8fbf4b9ee9a841eab327dc13", size = 41259201, upload-time = "2025-05-08T16:05:08.166Z" }, - { url = "https://files.pythonhosted.org/packages/96/ab/5cc9f80f28f6a7dff646c5756e559823614a42b1939d86dd0ed550470210/scipy-1.15.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:993439ce220d25e3696d1b23b233dd010169b62f6456488567e830654ee37a6b", size = 38714255, upload-time = "2025-05-08T16:05:14.596Z" }, - { url = "https://files.pythonhosted.org/packages/4a/4a/66ba30abe5ad1a3ad15bfb0b59d22174012e8056ff448cb1644deccbfed2/scipy-1.15.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:34716e281f181a02341ddeaad584205bd2fd3c242063bd3423d61ac259ca7eba", size = 30111035, upload-time = "2025-05-08T16:05:20.152Z" }, - { url = "https://files.pythonhosted.org/packages/4b/fa/a7e5b95afd80d24313307f03624acc65801846fa75599034f8ceb9e2cbf6/scipy-1.15.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3b0334816afb8b91dab859281b1b9786934392aa3d527cd847e41bb6f45bee65", size = 22384499, upload-time = "2025-05-08T16:05:24.494Z" }, - { url = "https://files.pythonhosted.org/packages/17/99/f3aaddccf3588bb4aea70ba35328c204cadd89517a1612ecfda5b2dd9d7a/scipy-1.15.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:6db907c7368e3092e24919b5e31c76998b0ce1684d51a90943cb0ed1b4ffd6c1", size = 25152602, upload-time = "2025-05-08T16:05:29.313Z" }, - { url = "https://files.pythonhosted.org/packages/56/c5/1032cdb565f146109212153339f9cb8b993701e9fe56b1c97699eee12586/scipy-1.15.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:721d6b4ef5dc82ca8968c25b111e307083d7ca9091bc38163fb89243e85e3889", size = 35503415, upload-time = "2025-05-08T16:05:34.699Z" }, - { url = "https://files.pythonhosted.org/packages/bd/37/89f19c8c05505d0601ed5650156e50eb881ae3918786c8fd7262b4ee66d3/scipy-1.15.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39cb9c62e471b1bb3750066ecc3a3f3052b37751c7c3dfd0fd7e48900ed52982", size = 37652622, upload-time = "2025-05-08T16:05:40.762Z" }, - { url = "https://files.pythonhosted.org/packages/7e/31/be59513aa9695519b18e1851bb9e487de66f2d31f835201f1b42f5d4d475/scipy-1.15.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:795c46999bae845966368a3c013e0e00947932d68e235702b5c3f6ea799aa8c9", size = 37244796, upload-time = "2025-05-08T16:05:48.119Z" }, - { url = "https://files.pythonhosted.org/packages/10/c0/4f5f3eeccc235632aab79b27a74a9130c6c35df358129f7ac8b29f562ac7/scipy-1.15.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:18aaacb735ab38b38db42cb01f6b92a2d0d4b6aabefeb07f02849e47f8fb3594", size = 40047684, upload-time = "2025-05-08T16:05:54.22Z" }, - { url = "https://files.pythonhosted.org/packages/ab/a7/0ddaf514ce8a8714f6ed243a2b391b41dbb65251affe21ee3077ec45ea9a/scipy-1.15.3-cp311-cp311-win_amd64.whl", hash = "sha256:ae48a786a28412d744c62fd7816a4118ef97e5be0bee968ce8f0a2fba7acf3bb", size = 41246504, upload-time = "2025-05-08T16:06:00.437Z" }, - { url = "https://files.pythonhosted.org/packages/37/4b/683aa044c4162e10ed7a7ea30527f2cbd92e6999c10a8ed8edb253836e9c/scipy-1.15.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac6310fdbfb7aa6612408bd2f07295bcbd3fda00d2d702178434751fe48e019", size = 38766735, upload-time = "2025-05-08T16:06:06.471Z" }, - { url = "https://files.pythonhosted.org/packages/7b/7e/f30be3d03de07f25dc0ec926d1681fed5c732d759ac8f51079708c79e680/scipy-1.15.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:185cd3d6d05ca4b44a8f1595af87f9c372bb6acf9c808e99aa3e9aa03bd98cf6", size = 30173284, upload-time = "2025-05-08T16:06:11.686Z" }, - { url = "https://files.pythonhosted.org/packages/07/9c/0ddb0d0abdabe0d181c1793db51f02cd59e4901da6f9f7848e1f96759f0d/scipy-1.15.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:05dc6abcd105e1a29f95eada46d4a3f251743cfd7d3ae8ddb4088047f24ea477", size = 22446958, upload-time = "2025-05-08T16:06:15.97Z" }, - { url = "https://files.pythonhosted.org/packages/af/43/0bce905a965f36c58ff80d8bea33f1f9351b05fad4beaad4eae34699b7a1/scipy-1.15.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:06efcba926324df1696931a57a176c80848ccd67ce6ad020c810736bfd58eb1c", size = 25242454, upload-time = "2025-05-08T16:06:20.394Z" }, - { url = "https://files.pythonhosted.org/packages/56/30/a6f08f84ee5b7b28b4c597aca4cbe545535c39fe911845a96414700b64ba/scipy-1.15.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05045d8b9bfd807ee1b9f38761993297b10b245f012b11b13b91ba8945f7e45", size = 35210199, upload-time = "2025-05-08T16:06:26.159Z" }, - { url = "https://files.pythonhosted.org/packages/0b/1f/03f52c282437a168ee2c7c14a1a0d0781a9a4a8962d84ac05c06b4c5b555/scipy-1.15.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271e3713e645149ea5ea3e97b57fdab61ce61333f97cfae392c28ba786f9bb49", size = 37309455, upload-time = "2025-05-08T16:06:32.778Z" }, - { url = "https://files.pythonhosted.org/packages/89/b1/fbb53137f42c4bf630b1ffdfc2151a62d1d1b903b249f030d2b1c0280af8/scipy-1.15.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6cfd56fc1a8e53f6e89ba3a7a7251f7396412d655bca2aa5611c8ec9a6784a1e", size = 36885140, upload-time = "2025-05-08T16:06:39.249Z" }, - { url = "https://files.pythonhosted.org/packages/2e/2e/025e39e339f5090df1ff266d021892694dbb7e63568edcfe43f892fa381d/scipy-1.15.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ff17c0bb1cb32952c09217d8d1eed9b53d1463e5f1dd6052c7857f83127d539", size = 39710549, upload-time = "2025-05-08T16:06:45.729Z" }, - { url = "https://files.pythonhosted.org/packages/e6/eb/3bf6ea8ab7f1503dca3a10df2e4b9c3f6b3316df07f6c0ded94b281c7101/scipy-1.15.3-cp312-cp312-win_amd64.whl", hash = "sha256:52092bc0472cfd17df49ff17e70624345efece4e1a12b23783a1ac59a1b728ed", size = 40966184, upload-time = "2025-05-08T16:06:52.623Z" }, - { url = "https://files.pythonhosted.org/packages/73/18/ec27848c9baae6e0d6573eda6e01a602e5649ee72c27c3a8aad673ebecfd/scipy-1.15.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c620736bcc334782e24d173c0fdbb7590a0a436d2fdf39310a8902505008759", size = 38728256, upload-time = "2025-05-08T16:06:58.696Z" }, - { url = "https://files.pythonhosted.org/packages/74/cd/1aef2184948728b4b6e21267d53b3339762c285a46a274ebb7863c9e4742/scipy-1.15.3-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:7e11270a000969409d37ed399585ee530b9ef6aa99d50c019de4cb01e8e54e62", size = 30109540, upload-time = "2025-05-08T16:07:04.209Z" }, - { url = "https://files.pythonhosted.org/packages/5b/d8/59e452c0a255ec352bd0a833537a3bc1bfb679944c4938ab375b0a6b3a3e/scipy-1.15.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8c9ed3ba2c8a2ce098163a9bdb26f891746d02136995df25227a20e71c396ebb", size = 22383115, upload-time = "2025-05-08T16:07:08.998Z" }, - { url = "https://files.pythonhosted.org/packages/08/f5/456f56bbbfccf696263b47095291040655e3cbaf05d063bdc7c7517f32ac/scipy-1.15.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0bdd905264c0c9cfa74a4772cdb2070171790381a5c4d312c973382fc6eaf730", size = 25163884, upload-time = "2025-05-08T16:07:14.091Z" }, - { url = "https://files.pythonhosted.org/packages/a2/66/a9618b6a435a0f0c0b8a6d0a2efb32d4ec5a85f023c2b79d39512040355b/scipy-1.15.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79167bba085c31f38603e11a267d862957cbb3ce018d8b38f79ac043bc92d825", size = 35174018, upload-time = "2025-05-08T16:07:19.427Z" }, - { url = "https://files.pythonhosted.org/packages/b5/09/c5b6734a50ad4882432b6bb7c02baf757f5b2f256041da5df242e2d7e6b6/scipy-1.15.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9deabd6d547aee2c9a81dee6cc96c6d7e9a9b1953f74850c179f91fdc729cb7", size = 37269716, upload-time = "2025-05-08T16:07:25.712Z" }, - { url = "https://files.pythonhosted.org/packages/77/0a/eac00ff741f23bcabd352731ed9b8995a0a60ef57f5fd788d611d43d69a1/scipy-1.15.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dde4fc32993071ac0c7dd2d82569e544f0bdaff66269cb475e0f369adad13f11", size = 36872342, upload-time = "2025-05-08T16:07:31.468Z" }, - { url = "https://files.pythonhosted.org/packages/fe/54/4379be86dd74b6ad81551689107360d9a3e18f24d20767a2d5b9253a3f0a/scipy-1.15.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f77f853d584e72e874d87357ad70f44b437331507d1c311457bed8ed2b956126", size = 39670869, upload-time = "2025-05-08T16:07:38.002Z" }, - { url = "https://files.pythonhosted.org/packages/87/2e/892ad2862ba54f084ffe8cc4a22667eaf9c2bcec6d2bff1d15713c6c0703/scipy-1.15.3-cp313-cp313-win_amd64.whl", hash = "sha256:b90ab29d0c37ec9bf55424c064312930ca5f4bde15ee8619ee44e69319aab163", size = 40988851, upload-time = "2025-05-08T16:08:33.671Z" }, - { url = "https://files.pythonhosted.org/packages/1b/e9/7a879c137f7e55b30d75d90ce3eb468197646bc7b443ac036ae3fe109055/scipy-1.15.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3ac07623267feb3ae308487c260ac684b32ea35fd81e12845039952f558047b8", size = 38863011, upload-time = "2025-05-08T16:07:44.039Z" }, - { url = "https://files.pythonhosted.org/packages/51/d1/226a806bbd69f62ce5ef5f3ffadc35286e9fbc802f606a07eb83bf2359de/scipy-1.15.3-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6487aa99c2a3d509a5227d9a5e889ff05830a06b2ce08ec30df6d79db5fcd5c5", size = 30266407, upload-time = "2025-05-08T16:07:49.891Z" }, - { url = "https://files.pythonhosted.org/packages/e5/9b/f32d1d6093ab9eeabbd839b0f7619c62e46cc4b7b6dbf05b6e615bbd4400/scipy-1.15.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:50f9e62461c95d933d5c5ef4a1f2ebf9a2b4e83b0db374cb3f1de104d935922e", size = 22540030, upload-time = "2025-05-08T16:07:54.121Z" }, - { url = "https://files.pythonhosted.org/packages/e7/29/c278f699b095c1a884f29fda126340fcc201461ee8bfea5c8bdb1c7c958b/scipy-1.15.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:14ed70039d182f411ffc74789a16df3835e05dc469b898233a245cdfd7f162cb", size = 25218709, upload-time = "2025-05-08T16:07:58.506Z" }, - { url = "https://files.pythonhosted.org/packages/24/18/9e5374b617aba742a990581373cd6b68a2945d65cc588482749ef2e64467/scipy-1.15.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a769105537aa07a69468a0eefcd121be52006db61cdd8cac8a0e68980bbb723", size = 34809045, upload-time = "2025-05-08T16:08:03.929Z" }, - { url = "https://files.pythonhosted.org/packages/e1/fe/9c4361e7ba2927074360856db6135ef4904d505e9b3afbbcb073c4008328/scipy-1.15.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db984639887e3dffb3928d118145ffe40eff2fa40cb241a306ec57c219ebbbb", size = 36703062, upload-time = "2025-05-08T16:08:09.558Z" }, - { url = "https://files.pythonhosted.org/packages/b7/8e/038ccfe29d272b30086b25a4960f757f97122cb2ec42e62b460d02fe98e9/scipy-1.15.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:40e54d5c7e7ebf1aa596c374c49fa3135f04648a0caabcb66c52884b943f02b4", size = 36393132, upload-time = "2025-05-08T16:08:15.34Z" }, - { url = "https://files.pythonhosted.org/packages/10/7e/5c12285452970be5bdbe8352c619250b97ebf7917d7a9a9e96b8a8140f17/scipy-1.15.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5e721fed53187e71d0ccf382b6bf977644c533e506c4d33c3fb24de89f5c3ed5", size = 38979503, upload-time = "2025-05-08T16:08:21.513Z" }, - { url = "https://files.pythonhosted.org/packages/81/06/0a5e5349474e1cbc5757975b21bd4fad0e72ebf138c5592f191646154e06/scipy-1.15.3-cp313-cp313t-win_amd64.whl", hash = "sha256:76ad1fb5f8752eabf0fa02e4cc0336b4e8f021e2d5f061ed37d6d264db35e3ca", size = 40308097, upload-time = "2025-05-08T16:08:27.627Z" }, -] - -[[package]] -name = "scipy" -version = "1.17.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'win32'", - "python_full_version == '3.12.*' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version == '3.12.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", -] -dependencies = [ - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/75/b4ce781849931fef6fd529afa6b63711d5a733065722d0c3e2724af9e40a/scipy-1.17.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:1f95b894f13729334fb990162e911c9e5dc1ab390c58aa6cbecb389c5b5e28ec", size = 31613675, upload-time = "2026-02-23T00:16:00.13Z" }, - { url = "https://files.pythonhosted.org/packages/f7/58/bccc2861b305abdd1b8663d6130c0b3d7cc22e8d86663edbc8401bfd40d4/scipy-1.17.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:e18f12c6b0bc5a592ed23d3f7b891f68fd7f8241d69b7883769eb5d5dfb52696", size = 28162057, upload-time = "2026-02-23T00:16:09.456Z" }, - { url = "https://files.pythonhosted.org/packages/6d/ee/18146b7757ed4976276b9c9819108adbc73c5aad636e5353e20746b73069/scipy-1.17.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a3472cfbca0a54177d0faa68f697d8ba4c80bbdc19908c3465556d9f7efce9ee", size = 20334032, upload-time = "2026-02-23T00:16:17.358Z" }, - { url = "https://files.pythonhosted.org/packages/ec/e6/cef1cf3557f0c54954198554a10016b6a03b2ec9e22a4e1df734936bd99c/scipy-1.17.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:766e0dc5a616d026a3a1cffa379af959671729083882f50307e18175797b3dfd", size = 22709533, upload-time = "2026-02-23T00:16:25.791Z" }, - { url = "https://files.pythonhosted.org/packages/4d/60/8804678875fc59362b0fb759ab3ecce1f09c10a735680318ac30da8cd76b/scipy-1.17.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:744b2bf3640d907b79f3fd7874efe432d1cf171ee721243e350f55234b4cec4c", size = 33062057, upload-time = "2026-02-23T00:16:36.931Z" }, - { url = "https://files.pythonhosted.org/packages/09/7d/af933f0f6e0767995b4e2d705a0665e454d1c19402aa7e895de3951ebb04/scipy-1.17.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43af8d1f3bea642559019edfe64e9b11192a8978efbd1539d7bc2aaa23d92de4", size = 35349300, upload-time = "2026-02-23T00:16:49.108Z" }, - { url = "https://files.pythonhosted.org/packages/b4/3d/7ccbbdcbb54c8fdc20d3b6930137c782a163fa626f0aef920349873421ba/scipy-1.17.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd96a1898c0a47be4520327e01f874acfd61fb48a9420f8aa9f6483412ffa444", size = 35127333, upload-time = "2026-02-23T00:17:01.293Z" }, - { url = "https://files.pythonhosted.org/packages/e8/19/f926cb11c42b15ba08e3a71e376d816ac08614f769b4f47e06c3580c836a/scipy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4eb6c25dd62ee8d5edf68a8e1c171dd71c292fdae95d8aeb3dd7d7de4c364082", size = 37741314, upload-time = "2026-02-23T00:17:12.576Z" }, - { url = "https://files.pythonhosted.org/packages/95/da/0d1df507cf574b3f224ccc3d45244c9a1d732c81dcb26b1e8a766ae271a8/scipy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:d30e57c72013c2a4fe441c2fcb8e77b14e152ad48b5464858e07e2ad9fbfceff", size = 36607512, upload-time = "2026-02-23T00:17:23.424Z" }, - { url = "https://files.pythonhosted.org/packages/68/7f/bdd79ceaad24b671543ffe0ef61ed8e659440eb683b66f033454dcee90eb/scipy-1.17.1-cp311-cp311-win_arm64.whl", hash = "sha256:9ecb4efb1cd6e8c4afea0daa91a87fbddbce1b99d2895d151596716c0b2e859d", size = 24599248, upload-time = "2026-02-23T00:17:34.561Z" }, - { url = "https://files.pythonhosted.org/packages/35/48/b992b488d6f299dbe3f11a20b24d3dda3d46f1a635ede1c46b5b17a7b163/scipy-1.17.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:35c3a56d2ef83efc372eaec584314bd0ef2e2f0d2adb21c55e6ad5b344c0dcb8", size = 31610954, upload-time = "2026-02-23T00:17:49.855Z" }, - { url = "https://files.pythonhosted.org/packages/b2/02/cf107b01494c19dc100f1d0b7ac3cc08666e96ba2d64db7626066cee895e/scipy-1.17.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:fcb310ddb270a06114bb64bbe53c94926b943f5b7f0842194d585c65eb4edd76", size = 28172662, upload-time = "2026-02-23T00:18:01.64Z" }, - { url = "https://files.pythonhosted.org/packages/cf/a9/599c28631bad314d219cf9ffd40e985b24d603fc8a2f4ccc5ae8419a535b/scipy-1.17.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:cc90d2e9c7e5c7f1a482c9875007c095c3194b1cfedca3c2f3291cdc2bc7c086", size = 20344366, upload-time = "2026-02-23T00:18:12.015Z" }, - { url = "https://files.pythonhosted.org/packages/35/f5/906eda513271c8deb5af284e5ef0206d17a96239af79f9fa0aebfe0e36b4/scipy-1.17.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:c80be5ede8f3f8eded4eff73cc99a25c388ce98e555b17d31da05287015ffa5b", size = 22704017, upload-time = "2026-02-23T00:18:21.502Z" }, - { url = "https://files.pythonhosted.org/packages/da/34/16f10e3042d2f1d6b66e0428308ab52224b6a23049cb2f5c1756f713815f/scipy-1.17.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e19ebea31758fac5893a2ac360fedd00116cbb7628e650842a6691ba7ca28a21", size = 32927842, upload-time = "2026-02-23T00:18:35.367Z" }, - { url = "https://files.pythonhosted.org/packages/01/8e/1e35281b8ab6d5d72ebe9911edcdffa3f36b04ed9d51dec6dd140396e220/scipy-1.17.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02ae3b274fde71c5e92ac4d54bc06c42d80e399fec704383dcd99b301df37458", size = 35235890, upload-time = "2026-02-23T00:18:49.188Z" }, - { url = "https://files.pythonhosted.org/packages/c5/5c/9d7f4c88bea6e0d5a4f1bc0506a53a00e9fcb198de372bfe4d3652cef482/scipy-1.17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a604bae87c6195d8b1045eddece0514d041604b14f2727bbc2b3020172045eb", size = 35003557, upload-time = "2026-02-23T00:18:54.74Z" }, - { url = "https://files.pythonhosted.org/packages/65/94/7698add8f276dbab7a9de9fb6b0e02fc13ee61d51c7c3f85ac28b65e1239/scipy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f590cd684941912d10becc07325a3eeb77886fe981415660d9265c4c418d0bea", size = 37625856, upload-time = "2026-02-23T00:19:00.307Z" }, - { url = "https://files.pythonhosted.org/packages/a2/84/dc08d77fbf3d87d3ee27f6a0c6dcce1de5829a64f2eae85a0ecc1f0daa73/scipy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:41b71f4a3a4cab9d366cd9065b288efc4d4f3c0b37a91a8e0947fb5bd7f31d87", size = 36549682, upload-time = "2026-02-23T00:19:07.67Z" }, - { url = "https://files.pythonhosted.org/packages/bc/98/fe9ae9ffb3b54b62559f52dedaebe204b408db8109a8c66fdd04869e6424/scipy-1.17.1-cp312-cp312-win_arm64.whl", hash = "sha256:f4115102802df98b2b0db3cce5cb9b92572633a1197c77b7553e5203f284a5b3", size = 24547340, upload-time = "2026-02-23T00:19:12.024Z" }, - { url = "https://files.pythonhosted.org/packages/76/27/07ee1b57b65e92645f219b37148a7e7928b82e2b5dbeccecb4dff7c64f0b/scipy-1.17.1-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:5e3c5c011904115f88a39308379c17f91546f77c1667cea98739fe0fccea804c", size = 31590199, upload-time = "2026-02-23T00:19:17.192Z" }, - { url = "https://files.pythonhosted.org/packages/ec/ae/db19f8ab842e9b724bf5dbb7db29302a91f1e55bc4d04b1025d6d605a2c5/scipy-1.17.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:6fac755ca3d2c3edcb22f479fceaa241704111414831ddd3bc6056e18516892f", size = 28154001, upload-time = "2026-02-23T00:19:22.241Z" }, - { url = "https://files.pythonhosted.org/packages/5b/58/3ce96251560107b381cbd6e8413c483bbb1228a6b919fa8652b0d4090e7f/scipy-1.17.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:7ff200bf9d24f2e4d5dc6ee8c3ac64d739d3a89e2326ba68aaf6c4a2b838fd7d", size = 20325719, upload-time = "2026-02-23T00:19:26.329Z" }, - { url = "https://files.pythonhosted.org/packages/b2/83/15087d945e0e4d48ce2377498abf5ad171ae013232ae31d06f336e64c999/scipy-1.17.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4b400bdc6f79fa02a4d86640310dde87a21fba0c979efff5248908c6f15fad1b", size = 22683595, upload-time = "2026-02-23T00:19:30.304Z" }, - { url = "https://files.pythonhosted.org/packages/b4/e0/e58fbde4a1a594c8be8114eb4aac1a55bcd6587047efc18a61eb1f5c0d30/scipy-1.17.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b64ca7d4aee0102a97f3ba22124052b4bd2152522355073580bf4845e2550b6", size = 32896429, upload-time = "2026-02-23T00:19:35.536Z" }, - { url = "https://files.pythonhosted.org/packages/f5/5f/f17563f28ff03c7b6799c50d01d5d856a1d55f2676f537ca8d28c7f627cd/scipy-1.17.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:581b2264fc0aa555f3f435a5944da7504ea3a065d7029ad60e7c3d1ae09c5464", size = 35203952, upload-time = "2026-02-23T00:19:42.259Z" }, - { url = "https://files.pythonhosted.org/packages/8d/a5/9afd17de24f657fdfe4df9a3f1ea049b39aef7c06000c13db1530d81ccca/scipy-1.17.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:beeda3d4ae615106d7094f7e7cef6218392e4465cc95d25f900bebabfded0950", size = 34979063, upload-time = "2026-02-23T00:19:47.547Z" }, - { url = "https://files.pythonhosted.org/packages/8b/13/88b1d2384b424bf7c924f2038c1c409f8d88bb2a8d49d097861dd64a57b2/scipy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6609bc224e9568f65064cfa72edc0f24ee6655b47575954ec6339534b2798369", size = 37598449, upload-time = "2026-02-23T00:19:53.238Z" }, - { url = "https://files.pythonhosted.org/packages/35/e5/d6d0e51fc888f692a35134336866341c08655d92614f492c6860dc45bb2c/scipy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:37425bc9175607b0268f493d79a292c39f9d001a357bebb6b88fdfaff13f6448", size = 36510943, upload-time = "2026-02-23T00:20:50.89Z" }, - { url = "https://files.pythonhosted.org/packages/2a/fd/3be73c564e2a01e690e19cc618811540ba5354c67c8680dce3281123fb79/scipy-1.17.1-cp313-cp313-win_arm64.whl", hash = "sha256:5cf36e801231b6a2059bf354720274b7558746f3b1a4efb43fcf557ccd484a87", size = 24545621, upload-time = "2026-02-23T00:20:55.871Z" }, - { url = "https://files.pythonhosted.org/packages/6f/6b/17787db8b8114933a66f9dcc479a8272e4b4da75fe03b0c282f7b0ade8cd/scipy-1.17.1-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:d59c30000a16d8edc7e64152e30220bfbd724c9bbb08368c054e24c651314f0a", size = 31936708, upload-time = "2026-02-23T00:19:58.694Z" }, - { url = "https://files.pythonhosted.org/packages/38/2e/524405c2b6392765ab1e2b722a41d5da33dc5c7b7278184a8ad29b6cb206/scipy-1.17.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:010f4333c96c9bb1a4516269e33cb5917b08ef2166d5556ca2fd9f082a9e6ea0", size = 28570135, upload-time = "2026-02-23T00:20:03.934Z" }, - { url = "https://files.pythonhosted.org/packages/fd/c3/5bd7199f4ea8556c0c8e39f04ccb014ac37d1468e6cfa6a95c6b3562b76e/scipy-1.17.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:2ceb2d3e01c5f1d83c4189737a42d9cb2fc38a6eeed225e7515eef71ad301dce", size = 20741977, upload-time = "2026-02-23T00:20:07.935Z" }, - { url = "https://files.pythonhosted.org/packages/d9/b8/8ccd9b766ad14c78386599708eb745f6b44f08400a5fd0ade7cf89b6fc93/scipy-1.17.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:844e165636711ef41f80b4103ed234181646b98a53c8f05da12ca5ca289134f6", size = 23029601, upload-time = "2026-02-23T00:20:12.161Z" }, - { url = "https://files.pythonhosted.org/packages/6d/a0/3cb6f4d2fb3e17428ad2880333cac878909ad1a89f678527b5328b93c1d4/scipy-1.17.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:158dd96d2207e21c966063e1635b1063cd7787b627b6f07305315dd73d9c679e", size = 33019667, upload-time = "2026-02-23T00:20:17.208Z" }, - { url = "https://files.pythonhosted.org/packages/f3/c3/2d834a5ac7bf3a0c806ad1508efc02dda3c8c61472a56132d7894c312dea/scipy-1.17.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74cbb80d93260fe2ffa334efa24cb8f2f0f622a9b9febf8b483c0b865bfb3475", size = 35264159, upload-time = "2026-02-23T00:20:23.087Z" }, - { url = "https://files.pythonhosted.org/packages/4d/77/d3ed4becfdbd217c52062fafe35a72388d1bd82c2d0ba5ca19d6fcc93e11/scipy-1.17.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:dbc12c9f3d185f5c737d801da555fb74b3dcfa1a50b66a1a93e09190f41fab50", size = 35102771, upload-time = "2026-02-23T00:20:28.636Z" }, - { url = "https://files.pythonhosted.org/packages/bd/12/d19da97efde68ca1ee5538bb261d5d2c062f0c055575128f11a2730e3ac1/scipy-1.17.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94055a11dfebe37c656e70317e1996dc197e1a15bbcc351bcdd4610e128fe1ca", size = 37665910, upload-time = "2026-02-23T00:20:34.743Z" }, - { url = "https://files.pythonhosted.org/packages/06/1c/1172a88d507a4baaf72c5a09bb6c018fe2ae0ab622e5830b703a46cc9e44/scipy-1.17.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e30bdeaa5deed6bc27b4cc490823cd0347d7dae09119b8803ae576ea0ce52e4c", size = 36562980, upload-time = "2026-02-23T00:20:40.575Z" }, - { url = "https://files.pythonhosted.org/packages/70/b0/eb757336e5a76dfa7911f63252e3b7d1de00935d7705cf772db5b45ec238/scipy-1.17.1-cp313-cp313t-win_arm64.whl", hash = "sha256:a720477885a9d2411f94a93d16f9d89bad0f28ca23c3f8daa521e2dcc3f44d49", size = 24856543, upload-time = "2026-02-23T00:20:45.313Z" }, - { url = "https://files.pythonhosted.org/packages/cf/83/333afb452af6f0fd70414dc04f898647ee1423979ce02efa75c3b0f2c28e/scipy-1.17.1-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:a48a72c77a310327f6a3a920092fa2b8fd03d7deaa60f093038f22d98e096717", size = 31584510, upload-time = "2026-02-23T00:21:01.015Z" }, - { url = "https://files.pythonhosted.org/packages/ed/a6/d05a85fd51daeb2e4ea71d102f15b34fedca8e931af02594193ae4fd25f7/scipy-1.17.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:45abad819184f07240d8a696117a7aacd39787af9e0b719d00285549ed19a1e9", size = 28170131, upload-time = "2026-02-23T00:21:05.888Z" }, - { url = "https://files.pythonhosted.org/packages/db/7b/8624a203326675d7746a254083a187398090a179335b2e4a20e2ddc46e83/scipy-1.17.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:3fd1fcdab3ea951b610dc4cef356d416d5802991e7e32b5254828d342f7b7e0b", size = 20342032, upload-time = "2026-02-23T00:21:09.904Z" }, - { url = "https://files.pythonhosted.org/packages/c9/35/2c342897c00775d688d8ff3987aced3426858fd89d5a0e26e020b660b301/scipy-1.17.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:7bdf2da170b67fdf10bca777614b1c7d96ae3ca5794fd9587dce41eb2966e866", size = 22678766, upload-time = "2026-02-23T00:21:14.313Z" }, - { url = "https://files.pythonhosted.org/packages/ef/f2/7cdb8eb308a1a6ae1e19f945913c82c23c0c442a462a46480ce487fdc0ac/scipy-1.17.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:adb2642e060a6549c343603a3851ba76ef0b74cc8c079a9a58121c7ec9fe2350", size = 32957007, upload-time = "2026-02-23T00:21:19.663Z" }, - { url = "https://files.pythonhosted.org/packages/0b/2e/7eea398450457ecb54e18e9d10110993fa65561c4f3add5e8eccd2b9cd41/scipy-1.17.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eee2cfda04c00a857206a4330f0c5e3e56535494e30ca445eb19ec624ae75118", size = 35221333, upload-time = "2026-02-23T00:21:25.278Z" }, - { url = "https://files.pythonhosted.org/packages/d9/77/5b8509d03b77f093a0d52e606d3c4f79e8b06d1d38c441dacb1e26cacf46/scipy-1.17.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d2650c1fb97e184d12d8ba010493ee7b322864f7d3d00d3f9bb97d9c21de4068", size = 35042066, upload-time = "2026-02-23T00:21:31.358Z" }, - { url = "https://files.pythonhosted.org/packages/f9/df/18f80fb99df40b4070328d5ae5c596f2f00fffb50167e31439e932f29e7d/scipy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:08b900519463543aa604a06bec02461558a6e1cef8fdbb8098f77a48a83c8118", size = 37612763, upload-time = "2026-02-23T00:21:37.247Z" }, - { url = "https://files.pythonhosted.org/packages/4b/39/f0e8ea762a764a9dc52aa7dabcfad51a354819de1f0d4652b6a1122424d6/scipy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:3877ac408e14da24a6196de0ddcace62092bfc12a83823e92e49e40747e52c19", size = 37290984, upload-time = "2026-02-23T00:22:35.023Z" }, - { url = "https://files.pythonhosted.org/packages/7c/56/fe201e3b0f93d1a8bcf75d3379affd228a63d7e2d80ab45467a74b494947/scipy-1.17.1-cp314-cp314-win_arm64.whl", hash = "sha256:f8885db0bc2bffa59d5c1b72fad7a6a92d3e80e7257f967dd81abb553a90d293", size = 25192877, upload-time = "2026-02-23T00:22:39.798Z" }, - { url = "https://files.pythonhosted.org/packages/96/ad/f8c414e121f82e02d76f310f16db9899c4fcde36710329502a6b2a3c0392/scipy-1.17.1-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:1cc682cea2ae55524432f3cdff9e9a3be743d52a7443d0cba9017c23c87ae2f6", size = 31949750, upload-time = "2026-02-23T00:21:42.289Z" }, - { url = "https://files.pythonhosted.org/packages/7c/b0/c741e8865d61b67c81e255f4f0a832846c064e426636cd7de84e74d209be/scipy-1.17.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:2040ad4d1795a0ae89bfc7e8429677f365d45aa9fd5e4587cf1ea737f927b4a1", size = 28585858, upload-time = "2026-02-23T00:21:47.706Z" }, - { url = "https://files.pythonhosted.org/packages/ed/1b/3985219c6177866628fa7c2595bfd23f193ceebbe472c98a08824b9466ff/scipy-1.17.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:131f5aaea57602008f9822e2115029b55d4b5f7c070287699fe45c661d051e39", size = 20757723, upload-time = "2026-02-23T00:21:52.039Z" }, - { url = "https://files.pythonhosted.org/packages/c0/19/2a04aa25050d656d6f7b9e7b685cc83d6957fb101665bfd9369ca6534563/scipy-1.17.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:9cdc1a2fcfd5c52cfb3045feb399f7b3ce822abdde3a193a6b9a60b3cb5854ca", size = 23043098, upload-time = "2026-02-23T00:21:56.185Z" }, - { url = "https://files.pythonhosted.org/packages/86/f1/3383beb9b5d0dbddd030335bf8a8b32d4317185efe495374f134d8be6cce/scipy-1.17.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e3dcd57ab780c741fde8dc68619de988b966db759a3c3152e8e9142c26295ad", size = 33030397, upload-time = "2026-02-23T00:22:01.404Z" }, - { url = "https://files.pythonhosted.org/packages/41/68/8f21e8a65a5a03f25a79165ec9d2b28c00e66dc80546cf5eb803aeeff35b/scipy-1.17.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a9956e4d4f4a301ebf6cde39850333a6b6110799d470dbbb1e25326ac447f52a", size = 35281163, upload-time = "2026-02-23T00:22:07.024Z" }, - { url = "https://files.pythonhosted.org/packages/84/8d/c8a5e19479554007a5632ed7529e665c315ae7492b4f946b0deb39870e39/scipy-1.17.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:a4328d245944d09fd639771de275701ccadf5f781ba0ff092ad141e017eccda4", size = 35116291, upload-time = "2026-02-23T00:22:12.585Z" }, - { url = "https://files.pythonhosted.org/packages/52/52/e57eceff0e342a1f50e274264ed47497b59e6a4e3118808ee58ddda7b74a/scipy-1.17.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a77cbd07b940d326d39a1d1b37817e2ee4d79cb30e7338f3d0cddffae70fcaa2", size = 37682317, upload-time = "2026-02-23T00:22:18.513Z" }, - { url = "https://files.pythonhosted.org/packages/11/2f/b29eafe4a3fbc3d6de9662b36e028d5f039e72d345e05c250e121a230dd4/scipy-1.17.1-cp314-cp314t-win_amd64.whl", hash = "sha256:eb092099205ef62cd1782b006658db09e2fed75bffcae7cc0d44052d8aa0f484", size = 37345327, upload-time = "2026-02-23T00:22:24.442Z" }, - { url = "https://files.pythonhosted.org/packages/07/39/338d9219c4e87f3e708f18857ecd24d22a0c3094752393319553096b98af/scipy-1.17.1-cp314-cp314t-win_arm64.whl", hash = "sha256:200e1050faffacc162be6a486a984a0497866ec54149a01270adc8a59b7c7d21", size = 25489165, upload-time = "2026-02-23T00:22:29.563Z" }, -] - -[[package]] -name = "scitokens" -version = "1.9.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, - { name = "pyjwt" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ed/d3/f7a71bb402afbc602e1f6624514115ad59a588a1ad696274f957d532f35f/scitokens-1.9.7.tar.gz", hash = "sha256:9aabefbd68859e94a3909f3dc08bd73c0e7a9c08203c16de338c9512ace821e3", size = 54241, upload-time = "2026-03-13T21:28:51.67Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/93/5b/ed5d8ce3d0e9d2bd38c7901d8131a346fe69d941357f92700bceee12b32e/scitokens-1.9.7-py3-none-any.whl", hash = "sha256:05a539ece3a114233e28c5b1552e5d1fbd13282c6ce683f5a073de116b228df0", size = 34527, upload-time = "2026-03-13T21:28:50.43Z" }, -] - -[[package]] -name = "setuptools" -version = "81.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0d/1c/73e719955c59b8e424d015ab450f51c0af856ae46ea2da83eba51cc88de1/setuptools-81.0.0.tar.gz", hash = "sha256:487b53915f52501f0a79ccfd0c02c165ffe06631443a886740b91af4b7a5845a", size = 1198299, upload-time = "2026-02-06T21:10:39.601Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/e3/c164c88b2e5ce7b24d667b9bd83589cf4f3520d97cad01534cd3c4f55fdb/setuptools-81.0.0-py3-none-any.whl", hash = "sha256:fdd925d5c5d9f62e4b74b30d6dd7828ce236fd6ed998a08d81de62ce5a6310d6", size = 1062021, upload-time = "2026-02-06T21:10:37.175Z" }, -] - -[[package]] -name = "six" -version = "1.17.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, -] - -[[package]] -name = "sympy" -version = "1.14.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mpmath" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, -] - -[[package]] -name = "tenacity" -version = "9.1.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/47/c6/ee486fd809e357697ee8a44d3d69222b344920433d3b6666ccd9b374630c/tenacity-9.1.4.tar.gz", hash = "sha256:adb31d4c263f2bd041081ab33b498309a57c77f9acf2db65aadf0898179cf93a", size = 49413, upload-time = "2026-02-07T10:45:33.841Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/c1/eb8f9debc45d3b7918a32ab756658a0904732f75e555402972246b0b8e71/tenacity-9.1.4-py3-none-any.whl", hash = "sha256:6095a360c919085f28c6527de529e76a06ad89b23659fa881ae0649b867a9d55", size = 28926, upload-time = "2026-02-07T10:45:32.24Z" }, -] - -[[package]] -name = "threadpoolctl" -version = "3.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274, upload-time = "2025-03-13T13:49:23.031Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" }, -] - -[[package]] -name = "tomli" -version = "2.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30", size = 154704, upload-time = "2026-03-25T20:21:10.473Z" }, - { url = "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a", size = 149454, upload-time = "2026-03-25T20:21:12.036Z" }, - { url = "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076", size = 237561, upload-time = "2026-03-25T20:21:13.098Z" }, - { url = "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", size = 243824, upload-time = "2026-03-25T20:21:14.569Z" }, - { url = "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c", size = 242227, upload-time = "2026-03-25T20:21:15.712Z" }, - { url = "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc", size = 247859, upload-time = "2026-03-25T20:21:17.001Z" }, - { url = "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049", size = 97204, upload-time = "2026-03-25T20:21:18.079Z" }, - { url = "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e", size = 108084, upload-time = "2026-03-25T20:21:18.978Z" }, - { url = "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece", size = 95285, upload-time = "2026-03-25T20:21:20.309Z" }, - { url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" }, - { url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" }, - { url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" }, - { url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" }, - { url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" }, - { url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" }, - { url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" }, - { url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" }, - { url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" }, - { url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" }, - { url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" }, - { url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" }, - { url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" }, - { url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" }, - { url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" }, - { url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" }, - { url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" }, - { url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" }, - { url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726, upload-time = "2026-03-25T20:21:42.23Z" }, - { url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859, upload-time = "2026-03-25T20:21:43.386Z" }, - { url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713, upload-time = "2026-03-25T20:21:44.474Z" }, - { url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084, upload-time = "2026-03-25T20:21:45.62Z" }, - { url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973, upload-time = "2026-03-25T20:21:46.937Z" }, - { url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223, upload-time = "2026-03-25T20:21:48.467Z" }, - { url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973, upload-time = "2026-03-25T20:21:49.526Z" }, - { url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082, upload-time = "2026-03-25T20:21:50.506Z" }, - { url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490, upload-time = "2026-03-25T20:21:51.474Z" }, - { url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263, upload-time = "2026-03-25T20:21:52.543Z" }, - { url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736, upload-time = "2026-03-25T20:21:53.674Z" }, - { url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717, upload-time = "2026-03-25T20:21:55.129Z" }, - { url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461, upload-time = "2026-03-25T20:21:56.228Z" }, - { url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855, upload-time = "2026-03-25T20:21:57.653Z" }, - { url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144, upload-time = "2026-03-25T20:21:59.089Z" }, - { url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683, upload-time = "2026-03-25T20:22:00.214Z" }, - { url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196, upload-time = "2026-03-25T20:22:01.169Z" }, - { url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393, upload-time = "2026-03-25T20:22:02.137Z" }, - { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, -] - -[[package]] -name = "torch" -version = "2.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cuda-bindings", marker = "sys_platform == 'linux'" }, - { name = "cuda-toolkit", extra = ["cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "sys_platform == 'linux'" }, - { name = "filelock" }, - { name = "fsspec" }, - { name = "jinja2" }, - { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "nvidia-cublas", marker = "sys_platform == 'linux'" }, - { name = "nvidia-cudnn-cu13", marker = "sys_platform == 'linux'" }, - { name = "nvidia-cusparselt-cu13", marker = "sys_platform == 'linux'" }, - { name = "nvidia-nccl-cu13", marker = "sys_platform == 'linux'" }, - { name = "nvidia-nvshmem-cu13", marker = "sys_platform == 'linux'" }, - { name = "setuptools" }, - { name = "sympy" }, - { name = "triton", marker = "sys_platform == 'linux'" }, - { name = "typing-extensions" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/b7/53fe0436586716ab7aecff41e26b9302d57c85ded481fd83a2cd741e6b4e/torch-2.12.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:1834bd984f8a2f4f16bdfbeecca9146184b220aa46276bf5756735b5dae12812", size = 87981887, upload-time = "2026-05-13T14:55:53.234Z" }, - { url = "https://files.pythonhosted.org/packages/34/60/d930eac44c30de06ed16f6d1ba4e785e1632532b50d8f0bf9bf699a4d0c7/torch-2.12.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:d4d029801cb7b6df858804a2a21b00cc2aa0bf0ee5d2ab18d343c9e9e5681f35", size = 426355000, upload-time = "2026-05-13T14:54:31.944Z" }, - { url = "https://files.pythonhosted.org/packages/8e/0c/c76b6a087820bab55705b94dfc074e520de9ae91f5ef90da2ecbf2a3ef12/torch-2.12.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:d47e7dee68ac4cd7a068b26bcd6b989935427709fae1c8f7bd0019978f829e15", size = 532144998, upload-time = "2026-05-13T14:56:05.523Z" }, - { url = "https://files.pythonhosted.org/packages/4a/64/8a0d036e166a6aa85ee09bef072f3655d1ba5d5486a68d1b03b6813c01b3/torch-2.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:cf9839790285dd472e7a16aafcb4a4e6bf58ec1b494045044b0eefb0eb4bd1f2", size = 122949877, upload-time = "2026-05-13T14:55:46.841Z" }, - { url = "https://files.pythonhosted.org/packages/18/62/131124fb95df03811b8260d1d43dcc5ee85ea1a344b964613d7efe77fb08/torch-2.12.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:10802fd383bbfed646212e765a72c37d2185205d4f26eb197a254e8ac7ddcb25", size = 87990344, upload-time = "2026-05-13T14:55:42.154Z" }, - { url = "https://files.pythonhosted.org/packages/12/9c/dda0dbd547dc549839824135f223792fd0e725f28ed0715dda366b7acaa2/torch-2.12.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:c12592630aef72feaf18bd3f197ef587bbfa21131b31c38b23ab2e55fce92e36", size = 426362932, upload-time = "2026-05-13T14:54:15.295Z" }, - { url = "https://files.pythonhosted.org/packages/e2/d2/a7dd5a3f9bdaa7842124e8e2359202b317c48d47d2fc5816fafdf2049adb/torch-2.12.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:415c1b8d0412f67551c8e89a2daca0fb3e56694af0281ba155eaa9da481f58b4", size = 532170085, upload-time = "2026-05-13T14:55:20.788Z" }, - { url = "https://files.pythonhosted.org/packages/12/1b/a61ce2004f9ab0ea8964a6e6168133a127795667639e2ff4f8f2bdb16a65/torch-2.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:dd37188ea325042cb1f6cafa56822b11ada2520c04791a52629b0af25bdfbfd9", size = 122953128, upload-time = "2026-05-13T14:54:52.744Z" }, - { url = "https://files.pythonhosted.org/packages/ef/bb/285d643f254731294c9b595a007eac39db4600a98682d7bca688f42ca164/torch-2.12.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:b41339df93d491435e790ff8bcbae1c0ce777175889bfd1281d119862793e6a2", size = 88010197, upload-time = "2026-05-13T14:55:35.414Z" }, - { url = "https://files.pythonhosted.org/packages/79/81/76debf1db1343bd929bbb5d74c89fb437c2ed88eb144712557e7bd3eea45/torch-2.12.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:8fbef9f108a863e7722a73740998967e3b074742a834fc5be3a535a2befa7057", size = 426376751, upload-time = "2026-05-13T14:55:03.353Z" }, - { url = "https://files.pythonhosted.org/packages/de/f0/80026028b603c4650ff270fc3785bdef4bd6738765a9cc5a0f5a637d65a2/torch-2.12.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:4b4f64c2c2b11f7510d93dd6412b87025ff6eddd6bb61c3b5a3d892ea20c4756", size = 532261691, upload-time = "2026-05-13T14:52:54.453Z" }, - { url = "https://files.pythonhosted.org/packages/b9/c2/64b06cbb7830fb3cd9be13e1158b31a3f36b68e6a209105ee3c9d9480be0/torch-2.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:8b958caff4a14d3a3b0b2dfc6a378f64dda9728a9dad28c08a0db9ce4dafb549", size = 122988114, upload-time = "2026-05-13T14:54:42.153Z" }, - { url = "https://files.pythonhosted.org/packages/86/ca/01896c80ba921676aa45886b2c5b8d774912de2a1f719de48169c6f755cd/torch-2.12.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:90dd587a5f61bfe1307148b581e2084fc5bc4a06e2b90a20e9a36b81087ff16b", size = 88009511, upload-time = "2026-05-13T14:54:47.411Z" }, - { url = "https://files.pythonhosted.org/packages/a5/04/52bdaf4787eab6ac7d7f5851dff934e4def0bc8ead9c8fd2b69b3e529699/torch-2.12.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:864392c73b7654f4d2b3ae712f607937d0dbb1101c4555fbb41848106b297f39", size = 426383231, upload-time = "2026-05-13T14:53:32.129Z" }, - { url = "https://files.pythonhosted.org/packages/49/8a/94bdecd13f5aaa90d45920b89789d9fe7c6f4af8c3cdd7ce01fcb59908fc/torch-2.12.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5d6b560dfa7d56291c07d615c3bb73e8d9943d9b6d87f76cd0d9d570c4797fa6", size = 532269288, upload-time = "2026-05-13T14:53:49.423Z" }, - { url = "https://files.pythonhosted.org/packages/3e/2f/bdbaaa267de519ef1b73054bf590d8c93c37a266c9a4e24a01bd38b6918f/torch-2.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:3fee918902090ade827643e758e98363278815de583c75d111fdd665ebffde9f", size = 122987706, upload-time = "2026-05-13T14:54:00.335Z" }, - { url = "https://files.pythonhosted.org/packages/9b/ad/e95e822f3538171e22640a7fbe839a1fdb666600bf6487025de2ff03b11a/torch-2.12.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:10ee1448a9f304d3b987eb4656f664ba6e4d7b410ca7a5a7c642199777a2cf88", size = 88319556, upload-time = "2026-05-13T14:54:05.574Z" }, - { url = "https://files.pythonhosted.org/packages/b7/07/055d06d985b445d67422d25b033c11cf55bbb81785d4c4e68e28bca5820e/torch-2.12.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:af68dbf403439cae9ceaeaaf92f8352b460787dcd27b92aa05c40dd4a19c0f1e", size = 426397656, upload-time = "2026-05-13T14:52:38.84Z" }, - { url = "https://files.pythonhosted.org/packages/43/94/b0b4fdc3014122e0a7302fb90086d352aa48f2576f0b252561ebb38c01a8/torch-2.12.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:a6a2eebb237d3b1d9ad3b378e86d9b9e0782afdea8b1e0eba6a13646b9b49c07", size = 532183124, upload-time = "2026-05-13T14:53:16.178Z" }, - { url = "https://files.pythonhosted.org/packages/d8/c8/052405e6ad05d3237bfe5a4df78f917773956f8e17813a2d44c059068b74/torch-2.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2140e373e9a51a3e22ef62e8d14366d0b470d18f0adf19fdc757368077133a34", size = 123232462, upload-time = "2026-05-13T14:52:27.26Z" }, - { url = "https://files.pythonhosted.org/packages/67/dc/ac069f8d6e8be701535921141055293b0d4819d3d7f224a4612cf157c7f9/torch-2.12.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:f7dfae4a519197dfa050e98d8e36378a0fb5899625a875c2b54445005a2e404e", size = 88027282, upload-time = "2026-05-13T14:53:05.258Z" }, - { url = "https://files.pythonhosted.org/packages/33/c3/1c1eb00e34555b536dddf792676026a988d710ed36981aa00499b36b0620/torch-2.12.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:891c769072637c74e9a5a77a3bc782894696d8ffec83b938df8536dee7f0ba78", size = 426386961, upload-time = "2026-05-13T14:51:28.406Z" }, - { url = "https://files.pythonhosted.org/packages/cd/d4/7e730dba0c7032a4154dc9056b76cf9625515e030e269cfbf8098fcfee7d/torch-2.12.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:e2ad3eb85d39c3cab62dfa93ed5a73516e6a53c6713cb97d004004fe089f0f1f", size = 532272265, upload-time = "2026-05-13T14:51:59.308Z" }, - { url = "https://files.pythonhosted.org/packages/f1/b4/92c80d1bbfee1c0036c06d1d2155a3065bd2423134c83bf8a47e65cd6b9b/torch-2.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:c66696857e987efb8bc1777a37357ec4f60ab5e8af6250b83d6034437fa2d8f3", size = 122987138, upload-time = "2026-05-13T14:51:45.942Z" }, - { url = "https://files.pythonhosted.org/packages/7b/78/2e12b37ce50a19a037d7bc62d652a5a8f27385a7b05859d6bc9204f20cfe/torch-2.12.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:b4556715c8572758625d62b6e0ae3b1f76c440221913a6fb5e100f321fb4fb02", size = 88320100, upload-time = "2026-05-13T14:51:39.955Z" }, - { url = "https://files.pythonhosted.org/packages/56/5e/83c450ec7b0bb40a7b74611c1b5440f9260e33c54c90d556fd4a1f0fd955/torch-2.12.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:a43ac605a5e13116c72b64c359644cce0229f213dde48d2ae0ae5eb5becf7feb", size = 426391871, upload-time = "2026-05-13T14:52:14.989Z" }, - { url = "https://files.pythonhosted.org/packages/c9/e9/1a0b575d98d0afedd8f157d23fa3d2759421483660448e60d0a4b10b6daa/torch-2.12.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6a7512adfdd7f6732e40de1c620831e3c75b39b98cef60b11d0c5f0a76473ec5", size = 532192241, upload-time = "2026-05-13T14:51:07.795Z" }, - { url = "https://files.pythonhosted.org/packages/88/21/afadd25ecd81b3cea1e11c73cf1ab41a983a50271548c3ec7ec3b9efc3e9/torch-2.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:5f96b63f8287f66a005dd1b5a6abba2920f11156c5e5c4d815f3e2050fd1aa16", size = 123231092, upload-time = "2026-05-13T14:51:18.854Z" }, -] - -[[package]] -name = "tqdm" -version = "4.67.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", size = 169598, upload-time = "2026-02-03T17:35:53.048Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" }, -] - -[[package]] -name = "triton" -version = "3.7.0" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/97/dcd1f2a0f8336691bff74abc59b2ed9c69a0c0f8f65cd77109c49e05f068/triton-3.7.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223ac302091491436c248a34ee1e6c47a1026486579103c906ffd805be50cb89", size = 188367104, upload-time = "2026-05-07T19:04:56.68Z" }, - { url = "https://files.pythonhosted.org/packages/b2/c0/c2ac4fd2d8809b7579d4a820a0f9e5de62a9bc8a757ed4b3abf4f7ee964a/triton-3.7.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c631b65668d4951213b948a413c0564184305b77bb45cc9d686d3e1ecc4701a3", size = 201313191, upload-time = "2026-05-07T18:45:58.444Z" }, - { url = "https://files.pythonhosted.org/packages/b8/c1/5d842314bb6c78442cc60437928781701c6050b8d479bc2a1aed691d37ca/triton-3.7.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a9e71fc392675fac364e0ecf4ef3f76f85b7f5433a16f4c3c5fe5f05a52c85fe", size = 188480277, upload-time = "2026-05-07T19:05:03.231Z" }, - { url = "https://files.pythonhosted.org/packages/13/31/8315ea5f8dd18e60970b3022e3a8b93fd37e0b784fbbef86e10c8e6e5ca1/triton-3.7.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22bacffce443f54593dd20f05294d5a40622e0ea9ab632816f87154504356221", size = 201415942, upload-time = "2026-05-07T18:46:06.479Z" }, - { url = "https://files.pythonhosted.org/packages/f7/13/ec05adfcd87311d532ba61e3af143e8be59fcd26675884c4682841406a20/triton-3.7.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4bf49b00a7a377a68a6da603a876e797614e6455a80e9021669c476a953ad9a", size = 188505104, upload-time = "2026-05-07T19:05:09.843Z" }, - { url = "https://files.pythonhosted.org/packages/62/7b/468a576e35beef1426e0828e28e9ba9e65f5474d496f16ee126c15646324/triton-3.7.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f111161d49bf903c0eaedde3962353a3d841c08a836839b7cc1025b8426efcf", size = 201457567, upload-time = "2026-05-07T18:46:13.505Z" }, - { url = "https://files.pythonhosted.org/packages/01/e1/a59a583de59b8f62c495d67c80ee3ea97d09e91ac80c4c6e76456ed8d8ac/triton-3.7.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:abdf6beaa89b1bcfb9a43cd990536ce66091a997841a4814b260b7bee4c88c3c", size = 188503209, upload-time = "2026-05-07T19:05:17.935Z" }, - { url = "https://files.pythonhosted.org/packages/30/b1/b7507bb9815d403927c8dd51d4158ed2e11751a92dbc118a044f247b6848/triton-3.7.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a35d7afe3f3f058e7ec49fcce09794049e0ffc5c59019ac25ec3413741b8c4e7", size = 201453566, upload-time = "2026-05-07T18:46:20.427Z" }, - { url = "https://files.pythonhosted.org/packages/a6/8f/0bea7a6a0c989315c9135a1d7fb37e41905cfb3a17cbc1f10044ebd4cc3a/triton-3.7.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc1d61c172d257db80ddf42595131fb196ad2e9bdd751e90fe2ef13531734e8b", size = 188612899, upload-time = "2026-05-07T19:05:24.955Z" }, - { url = "https://files.pythonhosted.org/packages/e1/02/d96f57828d0912aec733b9bc7e0e7dbfd2c6f079a8fa433ac25cb93d1a30/triton-3.7.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:70fb9bbdc9f400afc54bbf6eb2670af28829a6ae3996863317964783141daf56", size = 201553816, upload-time = "2026-05-07T18:46:27.49Z" }, - { url = "https://files.pythonhosted.org/packages/40/fb/82a802dac4689f2a2fb2e69302e6a138eecc3e175bbe976ba3cfc717683a/triton-3.7.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a44a8476d0d3571eac4e4d1048e1ff75aad81a09ff4602ccfc56c6dea1672e", size = 188507879, upload-time = "2026-05-07T19:05:32.209Z" }, - { url = "https://files.pythonhosted.org/packages/8f/af/9904ec6d3c93d9b24e5ec360445bbdf758b7f00bfbeedb89cb0eb64eb8bb/triton-3.7.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b9b85e72968a9d8bba5ddb24e9b64aaabaf48affb042f2755cb7cfa92b7531ce", size = 201460637, upload-time = "2026-05-07T18:46:34.749Z" }, - { url = "https://files.pythonhosted.org/packages/a1/f9/4835a8ea746b88727d8899f4e3ccce4f9cacb38abfc3bb0a638266c53111/triton-3.7.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:18a160de426fd99f92b0baf509045360afbd3bfaa0b4a5171dde800ec9f09684", size = 188608706, upload-time = "2026-05-07T19:05:39.218Z" }, - { url = "https://files.pythonhosted.org/packages/c1/68/fa86e5a39608000f645535b2c124920126327ab731f8c4fafd5b07ff8d4b/triton-3.7.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce061073102714b725f3660ec6939d94a1da7984b3aa99c921417cae273672f5", size = 201546766, upload-time = "2026-05-07T18:46:42.088Z" }, -] - -[[package]] -name = "typing-extensions" -version = "4.15.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, -] - -[[package]] -name = "tzdata" -version = "2026.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/19/1b9b0e29f30c6d35cb345486df41110984ea67ae69dddbc0e8a100999493/tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10", size = 198254, upload-time = "2026-04-24T15:22:08.651Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321, upload-time = "2026-04-24T15:22:05.876Z" }, -] - -[[package]] -name = "tzlocal" -version = "5.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "tzdata", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8b/2e/c14812d3d4d9cd1773c6be938f89e5735a1f11a9f184ac3639b93cef35d5/tzlocal-5.3.1.tar.gz", hash = "sha256:cceffc7edecefea1f595541dbd6e990cb1ea3d19bf01b2809f362a03dd7921fd", size = 30761, upload-time = "2025-03-05T21:17:41.549Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/14/e2a54fabd4f08cd7af1c07030603c3356b74da07f7cc056e600436edfa17/tzlocal-5.3.1-py3-none-any.whl", hash = "sha256:eb1a66c3ef5847adf7a834f1be0800581b683b5608e74f86ecbcef8ab91bb85d", size = 18026, upload-time = "2025-03-05T21:17:39.857Z" }, -] - -[[package]] -name = "urllib3" -version = "2.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, -] - -[[package]] -name = "wadler-lindig" -version = "0.1.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1e/67/cbae4bf7683a64755c2c1778c418fea96d00e34395bb91743f08bd951571/wadler_lindig-0.1.7.tar.gz", hash = "sha256:81d14d3fe77d441acf3ebd7f4aefac20c74128bf460e84b512806dccf7b2cd55", size = 15842, upload-time = "2025-06-18T07:00:42.843Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/96/04e7b441807b26b794da5b11e59ed7f83b2cf8af202bd7eba8ad2fa6046e/wadler_lindig-0.1.7-py3-none-any.whl", hash = "sha256:e3ec83835570fd0a9509f969162aeb9c65618f998b1f42918cfc8d45122fe953", size = 20516, upload-time = "2025-06-18T07:00:41.684Z" }, -] From 607059bc720c1335df5851712c94311a3d21038a Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Fri, 15 May 2026 13:44:57 +0000 Subject: [PATCH 115/140] MAINT: drop python 3.10 support --- .github/workflows/basic-install.yml | 2 +- .github/workflows/unit-tests.yml | 5 ----- docs/installation.txt | 2 +- pyproject.toml | 5 ++++- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/.github/workflows/basic-install.yml b/.github/workflows/basic-install.yml index 9652b0758..6b7010458 100644 --- a/.github/workflows/basic-install.yml +++ b/.github/workflows/basic-install.yml @@ -21,7 +21,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - python-version: ["3.10", "3.11", "3.12", "3.13"] + python-version: ["3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v4 with: diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 5829c23ec..cd50ff979 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -32,9 +32,6 @@ jobs: fail-fast: false matrix: python: - - name: Python 3.10 - version: 3.10 - short-version: 310 - name: Python 3.11 version: 3.11 short-version: 311 @@ -71,8 +68,6 @@ jobs: python -m pip install .[jax] SCIPY_ARRAY_API=1 pytest --array-backend jax --durations 10 - name: Run torch-backend unit tests - # there are scipy version issues with python 3.10 and torch - if: matrix.python.version > 3.10 run: | SCIPY_ARRAY_API=1 pytest --array-backend torch --durations 10 - name: Run sampler tests diff --git a/docs/installation.txt b/docs/installation.txt index ae2820a0c..8d4fcee5c 100644 --- a/docs/installation.txt +++ b/docs/installation.txt @@ -47,7 +47,7 @@ wave inference, please additionally run the following commands. Install bilby from source ------------------------- -:code:`bilby` is developed and tested with Python 3.10-3.12. In the +:code:`bilby` is developed and tested with Python 3.11+. In the following, we assume you have a working python installation, `python pip `_, and `git `_. See :ref:`installing-python` for our diff --git a/pyproject.toml b/pyproject.toml index 30d311f3f..3c5a36e57 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,6 +15,9 @@ classifiers = [ "Intended Audience :: Science/Research", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Topic :: Scientific/Engineering :: Physics" ] description = "A user-friendly Bayesian inference library" @@ -29,7 +32,7 @@ maintainers = [ ] name = "bilby" readme = "README.rst" -requires-python = ">=3.10" +requires-python = ">=3.11" [project.entry-points."bilby.samplers"] "bilby.bilby_mcmc" = "bilby.bilby_mcmc.sampler:Bilby_MCMC" From 5254b5f7282816e1b4f6b903518284c7a79da87c Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Fri, 15 May 2026 14:25:58 +0000 Subject: [PATCH 116/140] Remove extra multibanding time marginalization lines --- bilby/gw/likelihood/multiband.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/bilby/gw/likelihood/multiband.py b/bilby/gw/likelihood/multiband.py index f3f99ebcf..c9023b181 100644 --- a/bilby/gw/likelihood/multiband.py +++ b/bilby/gw/likelihood/multiband.py @@ -746,9 +746,6 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array An object containing the SNR quantities. """ - if self.time_marginalization: - original_time = parameters["geocent_time"] - parameters["geocent_time"] = self._beam_pattern_reference_time modes = { mode: value[self.unique_to_original_frequencies] @@ -758,9 +755,6 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array modes, parameters, frequencies=self.banded_frequency_points ) - if self.time_marginalization: - parameters["geocent_time"] = original_time - d_inner_h = (strain @ self.linear_coeffs[interferometer.name]).conj() xp = array_module(strain) From 310b69a4c4bdba9ac97dd698c797c7a9f2134109 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Fri, 15 May 2026 14:54:26 +0000 Subject: [PATCH 117/140] TEST: fix test failures --- bilby/core/likelihood.py | 2 +- bilby/gw/detector/networks.py | 17 ----------------- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/bilby/core/likelihood.py b/bilby/core/likelihood.py index d69a3a469..0d3fd4537 100644 --- a/bilby/core/likelihood.py +++ b/bilby/core/likelihood.py @@ -311,7 +311,7 @@ def __init__(self, x, y, func, **kwargs): def log_likelihood(self, parameters): mu = self.func(self.x, **self.model_parameters(parameters=parameters), **self.kwargs) - xp = aac.get_namespace(mu) + xp = array_module(mu) if xp.any(mu < 0.): return -np.inf return -xp.sum(xp.log(mu) + (self.y / mu)) diff --git a/bilby/gw/detector/networks.py b/bilby/gw/detector/networks.py index 4ac52454a..5159d73fe 100644 --- a/bilby/gw/detector/networks.py +++ b/bilby/gw/detector/networks.py @@ -485,22 +485,5 @@ def load_interferometer(filename): @zenith_azimuth_to_theta_phi.dispatch def zenith_azimuth_to_theta_phi(zenith, azimuth, ifos: InterferometerList | list): - """ - Convert from the 'detector frame' to the Earth frame. - - Parameters - ========== - kappa: float - The zenith angle in the detector frame - eta: float - The azimuthal angle in the detector frame - ifos: list - List of Interferometer objects defining the detector frame - - Returns - ======= - theta, phi: float - The zenith and azimuthal angles in the earth frame. - """ delta_x = ifos[0].geometry.vertex - ifos[1].geometry.vertex return zenith_azimuth_to_theta_phi(zenith, azimuth, delta_x) From 716696ef3625b76eba7cd807e3f85dec92e56dfe Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Mon, 18 May 2026 18:47:37 +0000 Subject: [PATCH 118/140] FEAT: update random number generation for non-numpy backends --- bilby/compat/utils.py | 9 ++ bilby/core/prior/base.py | 10 +- bilby/core/prior/conditional.py | 7 +- bilby/core/prior/dict.py | 117 +++++++++++------- bilby/core/prior/joint.py | 28 ++--- bilby/core/utils/random.py | 63 ++++++++++ bilby/core/utils/series.py | 72 +++++++---- bilby/gw/detector/calibration.py | 3 +- bilby/gw/detector/interferometer.py | 5 +- bilby/gw/detector/networks.py | 3 +- bilby/gw/detector/psd.py | 89 ++++++++----- bilby/gw/detector/strain_data.py | 16 ++- bilby/gw/likelihood/multiband.py | 11 +- bilby/gw/prior.py | 38 +++--- bilby/gw/time.py | 2 +- bilby/gw/waveform_generator.py | 18 +-- bilby/hyper/likelihood.py | 26 ++-- docs/rng.rst | 25 ++++ .../injection_examples/jax_fast_tutorial.py | 2 +- requirements.txt | 1 + test/conftest.py | 12 ++ test/core/prior/analytical_test.py | 14 +-- test/core/prior/base_test.py | 2 +- test/core/prior/conditional_test.py | 2 +- test/core/prior/dict_test.py | 25 ++-- test/core/prior/prior_test.py | 48 +++---- test/gw/likelihood_test.py | 6 +- test/gw/prior_test.py | 10 +- test/hyper/hyper_pe_test.py | 2 +- 29 files changed, 442 insertions(+), 224 deletions(-) diff --git a/bilby/compat/utils.py b/bilby/compat/utils.py index f6cd73387..dc89520c8 100644 --- a/bilby/compat/utils.py +++ b/bilby/compat/utils.py @@ -71,6 +71,15 @@ def array_module(arr): >>> array_module(5) """ + # FIXME: remove direct import of orng to avoid hard dependency + import orng + if isinstance(arr, orng.ArrayRNG): + match arr.backend: + case "jax": + import jax.numpy as jnp + return jnp + case _: + return np if isinstance(arr, tuple) and len(arr) == 1: arr = arr[0] try: diff --git a/bilby/core/prior/base.py b/bilby/core/prior/base.py index 137f6df5d..37beeed89 100644 --- a/bilby/core/prior/base.py +++ b/bilby/core/prior/base.py @@ -60,7 +60,7 @@ def __init__(self, name=None, latex_label=None, unit=None, minimum=-np.inf, self._is_fixed = False def __init_subclass__(cls): - for method_name in ["prob", "ln_prob", "rescale", "cdf", "sample"]: + for method_name in ["prob", "ln_prob", "rescale", "cdf"]: method = getattr(cls, method_name, None) if method is not None: from inspect import signature @@ -139,7 +139,7 @@ def __eq__(self, other): return False return True - def sample(self, size=None, *, xp=np): + def sample(self, size=None, *, random_state=None): """Draw a sample from the prior Parameters @@ -153,10 +153,10 @@ def sample(self, size=None, *, xp=np): """ from ..utils import random + rng = random.resolve_random_state(random_state) - self.least_recently_sampled = self.rescale( - xp.asarray(random.rng.uniform(0, 1, size)) - ) + unit = rng.uniform(low=0, high=1, size=size) + self.least_recently_sampled = self.rescale(unit) return self.least_recently_sampled def rescale(self, val, *, xp=None): diff --git a/bilby/core/prior/conditional.py b/bilby/core/prior/conditional.py index f42f83239..c9f8502fe 100644 --- a/bilby/core/prior/conditional.py +++ b/bilby/core/prior/conditional.py @@ -62,7 +62,7 @@ def condition_func(reference_params, y): self.__class__.__name__ = 'Conditional{}'.format(prior_class.__name__) self.__class__.__qualname__ = 'Conditional{}'.format(prior_class.__qualname__) - def sample(self, size=None, *, xp=np, **required_variables): + def sample(self, size=None, *, random_state=None, **required_variables): """Draw a sample from the prior Parameters @@ -78,11 +78,10 @@ def sample(self, size=None, *, xp=np, **required_variables): """ from ..utils import random + rng = random.resolve_random_state(random_state) self.least_recently_sampled = self.rescale( - xp.asarray(random.rng.uniform(0, 1, size)), - xp=xp, - **required_variables, + rng.uniform(0, 1, size), **required_variables ) return self.least_recently_sampled diff --git a/bilby/core/prior/dict.py b/bilby/core/prior/dict.py index 68f76e06a..65688a620 100644 --- a/bilby/core/prior/dict.py +++ b/bilby/core/prior/dict.py @@ -17,6 +17,7 @@ BilbyJsonEncoder, decode_bilby_json, ) +from ..utils.random import resolve_random_state, random_array_module from ...compat.utils import array_module, xp_wrap @@ -355,7 +356,7 @@ def fill_priors(self, likelihood=None, default_priors_file=None): for key in self: self.test_redundancy(key) - def sample(self, size=None, *, xp=np): + def sample(self, size=None, *, random_state=None): """Draw samples from the prior set Parameters @@ -367,9 +368,13 @@ def sample(self, size=None, *, xp=np): ======= dict: Dictionary of the samples """ - return self.sample_subset_constrained(keys=list(self.keys()), size=size, xp=xp) + return self.sample_subset_constrained( + keys=list(self.keys()), size=size, random_state=random_state + ) - def sample_subset_constrained_as_array(self, keys=iter([]), size=None, *, xp=np): + def sample_subset_constrained_as_array( + self, keys=iter([]), size=None, *, random_state=None + ): """Return an array of samples Parameters @@ -384,12 +389,15 @@ def sample_subset_constrained_as_array(self, keys=iter([]), size=None, *, xp=np) array: array_like An array of shape (len(key), size) of the samples (ordered by keys) """ - samples_dict = self.sample_subset_constrained(keys=keys, size=size, xp=xp) + samples_dict = self.sample_subset_constrained( + keys=keys, size=size, random_state=random_state + ) + xp = random_array_module(random_state) samples_dict = {key: xp.atleast_1d(val) for key, val in samples_dict.items()} samples_list = [samples_dict[key] for key in keys] return xp.stack(samples_list) - def sample_subset(self, keys=iter([]), size=None, *, xp=np): + def sample_subset(self, keys=iter([]), size=None, *, random_state=None): """Draw samples from the prior set for parameters which are not a DeltaFunction Parameters @@ -409,7 +417,7 @@ def sample_subset(self, keys=iter([]), size=None, *, xp=np): if isinstance(self[key], Constraint): continue elif isinstance(self[key], Prior): - samples[key] = self[key].sample(size=size, xp=xp) + samples[key] = self[key].sample(size=size, random_state=random_state) else: logger.debug("{} not a known prior.".format(key)) return samples @@ -432,7 +440,7 @@ def fixed_keys(self): def constraint_keys(self): return [k for k, p in self.items() if isinstance(p, Constraint)] - def sample_subset_constrained(self, keys=iter([]), size=None, *, xp=np): + def sample_subset_constrained(self, keys=iter([]), size=None, *, random_state=None): """ Sample a subset of priors while ensuring constraints are satisfied. @@ -447,8 +455,10 @@ def sample_subset_constrained(self, keys=iter([]), size=None, *, xp=np): ======= dict: Dictionary of valid samples. """ + rng = resolve_random_state(random_state) + if not any(isinstance(self[key], Constraint) for key in self): - return self.sample_subset(keys=keys, size=size, xp=xp) + return self.sample_subset(keys=keys, size=size, random_state=rng) efficiency_warning_was_issued = False @@ -464,7 +474,7 @@ def check_efficiency(n_tested, n_valid): n_tested_samples, n_valid_samples = 0, 0 if size is None or size == 1: while True: - sample = self.sample_subset(keys=keys, size=size, xp=xp) + sample = self.sample_subset(keys=keys, size=size, random_state=rng) is_valid = self.evaluate_constraints(sample) n_tested_samples += 1 n_valid_samples += int(is_valid.item()) @@ -476,11 +486,12 @@ def check_efficiency(n_tested, n_valid): for key in keys.copy(): if isinstance(self[key], Constraint): del keys[keys.index(key)] + xp = random_array_module(random_state) all_samples = {key: xp.asarray([]) for key in keys} _first_key = list(all_samples.keys())[0] while len(all_samples[_first_key]) < needed: - samples = self.sample_subset(keys=keys, size=needed, xp=xp) - keep = self.evaluate_constraints(samples, xp=xp) + samples = self.sample_subset(keys=keys, size=needed, random_state=rng) + keep = self.evaluate_constraints(samples) for key in keys: all_samples[key] = xp.hstack( [all_samples[key], samples[key][keep].flatten()] @@ -516,23 +527,22 @@ def normalize_constraint_factor( self._cached_normalizations[keys] = factor_rounded return factor_rounded - def _estimate_normalization(self, keys, min_accept, sampling_chunk, *, xp=np): - samples = self.sample_subset(keys=keys, size=sampling_chunk, xp=xp) + def _estimate_normalization(self, keys, min_accept, sampling_chunk): + samples = self.sample_subset(keys=keys, size=sampling_chunk) keep = np.atleast_1d(self.evaluate_constraints(samples)) if len(keep) == 1: self._cached_normalizations[keys] = 1 return 1 all_samples = {key: np.array([]) for key in keys} while np.count_nonzero(keep) < min_accept: - samples = self.sample_subset(keys=keys, size=sampling_chunk, xp=xp) + samples = self.sample_subset(keys=keys, size=sampling_chunk) for key in samples: all_samples[key] = np.hstack([all_samples[key], samples[key].flatten()]) keep = np.array(self.evaluate_constraints(all_samples), dtype=bool) factor = len(keep) / np.count_nonzero(keep) return factor - @xp_wrap - def prob(self, sample, *, xp=None, **kwargs): + def prob(self, sample, *, normalized=True, xp=None, **kwargs): """ Parameters @@ -547,13 +557,17 @@ def prob(self, sample, *, xp=None, **kwargs): float: Joint probability of all individual sample probabilities """ + if xp is None: + xp = array_module(sample.values()) prob = xp.prod(xp.stack([self[key].prob(sample[key], xp=xp) for key in sample]), **kwargs) - return self.check_prob(sample, prob, xp=xp) + return self.check_prob(sample, prob, normalized=normalized, xp=xp) - @xp_wrap - def check_prob(self, sample, prob, *, xp=None): - ratio = self.normalize_constraint_factor(tuple(sample.keys())) + def check_prob(self, sample, prob, *, normalized=True, xp=None): + if normalized: + ratio = self.normalize_constraint_factor(tuple(sample.keys())) + else: + ratio = 1 if not aac.is_jax_namespace(xp) and xp.all(prob == 0.0): return prob * ratio else: @@ -567,8 +581,7 @@ def check_prob(self, sample, prob, *, xp=None): constrained_prob = xp.where(keep, prob * ratio, 0.0) return constrained_prob - @xp_wrap - def ln_prob(self, sample, axis=None, normalized=True, *, xp=None): + def ln_prob(self, sample, axis=None, *, normalized=True, xp=None): """ Parameters @@ -587,10 +600,14 @@ def ln_prob(self, sample, axis=None, normalized=True, *, xp=None): Joint log probability of all the individual sample probabilities """ + if xp is None and isinstance(sample, dict): + xp = array_module(sample.values()) + elif xp is None: + # assume input is a dataframe + xp = array_module(sample.values) ln_prob = xp.sum(xp.stack([self[key].ln_prob(sample[key], xp=xp) for key in sample]), axis=axis) return self.check_ln_prob(sample, ln_prob, normalized=normalized, xp=xp) - @xp_wrap def check_ln_prob(self, sample, ln_prob, normalized=True, *, xp=None): if normalized: ratio = self.normalize_constraint_factor(tuple(sample.keys())) @@ -746,7 +763,7 @@ def _check_conditions_resolved(self, key, sampled_keys): conditions_resolved = False return conditions_resolved - def sample_subset(self, keys=iter([]), size=None, *, xp=np): + def sample_subset(self, keys=iter([]), size=None, *, random_state=None): self.convert_floats_to_delta_functions() add_delta_keys = [ key @@ -767,22 +784,23 @@ def sample_subset(self, keys=iter([]), size=None, *, xp=np): try: samples[key] = subset_dict[key].sample( size=size, - xp=xp, + random_state=random_state, **subset_dict.get_required_variables(key), ) except ValueError: # Some prior classes can not handle an array of conditional parameters (e.g. alpha for PowerLaw) # If that is the case, we sample each sample individually. required_variables = subset_dict.get_required_variables(key) - samples[key] = np.zeros(size) - for i in range(size): + if size is None: + shape = () + else: + shape = (size,) + samples[key] = np.zeros(shape) + for i in range(size if size is not None else 1): rvars = { key: value[i] for key, value in required_variables.items() } - samples[key][i] = subset_dict[key].sample( - **rvars, - xp=xp, - ) + samples[key][i] = subset_dict[key].sample(**rvars, random_state=random_state) else: logger.debug("{} not a known prior.".format(key)) return samples @@ -804,8 +822,7 @@ def get_required_variables(self, key): for k in getattr(self[key], "required_variables", []) } - @xp_wrap - def prob(self, sample, *, xp=None, **kwargs): + def prob(self, sample, *, normalized=True, xp=None, **kwargs): """ Parameters @@ -821,14 +838,16 @@ def prob(self, sample, *, xp=None, **kwargs): """ self._prepare_evaluation(*zip(*sample.items())) + if xp is None: + xp = array_module(sample.values()) res = xp.asarray([ self[key].prob(sample[key], **self.get_required_variables(key), xp=xp) for key in sample ]) prob = xp.prod(res, **kwargs) - return self.check_prob(sample, prob, xp=xp) + return self.check_prob(sample, prob, normalized=normalized, xp=xp) - def ln_prob(self, sample, axis=None, normalized=True): + def ln_prob(self, sample, *, axis=None, normalized=True, xp=None): """ Parameters @@ -847,15 +866,14 @@ def ln_prob(self, sample, axis=None, normalized=True): """ self._prepare_evaluation(*zip(*sample.items())) - xp = array_module(sample.values()) + if xp is None: + xp = array_module(sample.values()) res = xp.asarray([ - self[key].ln_prob(sample[key], **self.get_required_variables(key)) + self[key].ln_prob(sample[key], **self.get_required_variables(key), xp=xp) for key in sample ]) ln_prob = xp.sum(res, axis=axis) - return ln_prob - # return self.check_ln_prob(sample, ln_prob, - # normalized=normalized) + return self.check_ln_prob(sample, ln_prob, normalized=normalized, xp=xp) @xp_wrap def cdf(self, sample, *, xp=None): @@ -888,18 +906,25 @@ def rescale(self, keys, theta): self._check_resolved() self._update_rescale_keys(keys) result = dict() + joint = dict() for key, index in zip( self.sorted_keys_without_fixed_parameters, self._rescale_indexes ): result[key] = self[key].rescale( theta[index], **self.get_required_variables(key) ) - if isinstance(self[key], JointPrior) and result[key] is not None: - for key, val in zip(self[key].dist.names, result[key]): - self[key].least_recently_sampled = val - result[key] = val - else: - self[key].least_recently_sampled = result[key] + self[key].least_recently_sampled = result[key] + if isinstance(self[key], JointPrior) and self[key].dist.distname not in joint: + joint[self[key].dist.distname] = [key] + elif isinstance(self[key], JointPrior): + joint[self[key].dist.distname].append(key) + for names in joint.values(): + for key in names: + if result[key] is None: + continue + for subkey, val in zip(self[key].dist.names, result[key]): + self[subkey].least_recently_sampled = val + result[subkey] = val return xp.asarray([result[key] for key in keys]) diff --git a/bilby/core/prior/joint.py b/bilby/core/prior/joint.py index 238c0d791..e85463c57 100644 --- a/bilby/core/prior/joint.py +++ b/bilby/core/prior/joint.py @@ -1,5 +1,6 @@ import re +import array_api_compat as aac import array_api_extra as xpx import numpy as np import scipy.stats @@ -263,7 +264,7 @@ def _ln_prob(self, samp, lnprob, outbounds, *, xp=None): """ return lnprob - def sample(self, size=1, *, xp=np, **kwargs): + def sample(self, size=1, *, random_state=None, **kwargs): """ Draw, and set, a sample from the Dist, accompanying method _sample needs to overwritten @@ -275,11 +276,11 @@ def sample(self, size=1, *, xp=np, **kwargs): if size is None: size = 1 - samps = self._sample(size=size, xp=xp, **kwargs) + samps = self._sample(size=size, random_state=random_state, **kwargs) for i, name in enumerate(self.names): self.current_sample[name] = samps[:, i].flatten()[()] - def _sample(self, size, *, xp=np, **kwargs): + def _sample(self, size, *, random_state=None, **kwargs): """ Draw, and set, a sample from the joint dist (**needs to be ovewritten by child class**) @@ -288,11 +289,7 @@ def _sample(self, size, *, xp=np, **kwargs): size: int number of samples to generate, defaults to 1 """ - samps = xp.zeros((size, len(self))) - """ - Here is where the subclass where overwrite sampling method - """ - return samps + raise NotImplementedError @xp_wrap def rescale(self, value, *, xp=None, **kwargs): @@ -634,23 +631,25 @@ def _rescale(self, samp, *, xp=None, **kwargs): ) return samp - def _sample(self, size, *, xp=np, **kwargs): + def _sample(self, size, *, random_state=None, **kwargs): try: mode = kwargs["mode"] except KeyError: mode = None + rng = random.resolve_random_state(random_state) + if mode is None: if self.nmodes == 1: mode = 0 else: if size == 1: - mode = np.argwhere(self.cumweights - random.rng.uniform(0, 1) > 0)[0][0] + mode = np.argwhere(self.cumweights - rng.uniform(0, 1) > 0)[0][0] else: # pick modes mode = [ np.argwhere(self.cumweights - r > 0)[0][0] - for r in random.rng.uniform(0, 1, size) + for r in rng.uniform(0, 1, size) ] samps = np.zeros((size, len(self))) @@ -658,7 +657,7 @@ def _sample(self, size, *, xp=np, **kwargs): inbound = False while not inbound: # sample the multivariate Gaussian keys - vals = random.rng.uniform(0, 1, len(self)) + vals = rng.uniform(0, 1, len(self)) if isinstance(mode, list): samp = np.atleast_1d(self.rescale(vals, mode=mode[i])) @@ -676,6 +675,7 @@ def _sample(self, size, *, xp=np, **kwargs): if not outbound: inbound = True + xp = aac.array_namespace(vals) return xp.asarray(samps) @xp_wrap @@ -814,7 +814,7 @@ def rescale(self, val, *, xp=None, **kwargs): else: return [] # return empty list - def sample(self, size=1, *, xp=np, **kwargs): + def sample(self, size=1, *, random_state=None, **kwargs): """ Draw a sample from the prior. @@ -839,7 +839,7 @@ def sample(self, size=1, *, xp=np, **kwargs): if len(self.dist.current_sample) == 0: # generate a sample - self.dist.sample(size=size, xp=xp, **kwargs) + self.dist.sample(size=size, random_state=random_state, **kwargs) sample = self.dist.current_sample[self.name] diff --git a/bilby/core/utils/random.py b/bilby/core/utils/random.py index ccb7654c6..7dac9011c 100644 --- a/bilby/core/utils/random.py +++ b/bilby/core/utils/random.py @@ -27,6 +27,8 @@ import sys import warnings +import array_api_compat as aac +import numpy as np from numpy.random import default_rng, SeedSequence @@ -104,3 +106,64 @@ def generate_seeds(nseeds): A SeedSequence object containing the generated seeds. """ return SeedSequence(Generator.rng.integers(0, 2**63 - 1, size=4)).spawn(nseeds) + + +def resolve_random_state(random_state): + """ + Resolve the provided random state into a random number generator. + + Parameters + ========== + random_state: None, int, np.random.Generator, or jax.random.KeyArray + The random state to resolve. + If None, the default random generator will be used. + If an int, a new :code:`numpy.random.default_rng` object will be + created with that seed. + If a :code:`numpy.random.Generator`, it will be returned as is. + If a :code:`jax.random.KeyArray`, a corresponding + :code:`orng.ArrayRNG` generator will be created and returned. + + Returns + ======= + np.random.Generator or orng.ArrayRNG + The resolved random number generator. + """ + import orng + if isinstance(random_state, (np.random.Generator, orng.ArrayRNG)): + return random_state + elif random_state is None: + return Generator.rng + elif aac.is_jax_array(random_state): + rng = orng.ArrayRNG(generator=random_state, backend="jax") + return rng + elif isinstance(random_state, int): + return np.random.default_rng(random_state) + else: + raise ValueError( + "Invalid random state. Must be None, int, or np.random.Generator." + ) + + +def random_array_module(random_state): + """ + Return the array module corresponding to the provided random state. + The the random state is a JAX random key, this will return :code:`jax.numpy`. + Otherwise, it will return :code:`numpy`. + + Parameters + ========== + random_state: None, int, np.random.Generator, or jax.random.KeyArray + The random state to resolve. + + Returns + ------- + numpy or jax.numpy + The array module corresponding to the provided random state. + """ + if random_state is None: + return np + elif aac.is_jax_array(random_state): + import jax.numpy as jnp + return jnp + else: + return np \ No newline at end of file diff --git a/bilby/core/utils/series.py b/bilby/core/utils/series.py index 4fa20b51a..aaf70ad0f 100644 --- a/bilby/core/utils/series.py +++ b/bilby/core/utils/series.py @@ -1,5 +1,9 @@ +import array_api_compat as aac +import array_api_extra as xpx import numpy as np + from ...compat.utils import array_module +from . import random _TOL = 14 @@ -122,7 +126,8 @@ def create_frequency_series(sampling_frequency, duration): """ xp = array_module(sampling_frequency) - _check_legal_sampling_frequency_and_duration(sampling_frequency, duration) + if not aac.is_jax_namespace(xp): + _check_legal_sampling_frequency_and_duration(sampling_frequency, duration) number_of_samples = xp.round(duration * sampling_frequency) number_of_frequencies = int(xp.round(number_of_samples / 2) + 1) @@ -151,46 +156,69 @@ def _check_legal_sampling_frequency_and_duration(sampling_frequency, duration): sampling_frequency * duration ) ) + - -def create_white_noise(sampling_frequency, duration): - """ Create white_noise which is then coloured by a given PSD +def safe_white_noise(number_of_samples: int, duration: float, *, random_state=None): + """ + A JIT-compilable function to generate white noise in the frequency domain. Parameters ========== - sampling_frequency: float + number_of_samples: int + The number of samples in the time domain. duration: float - duration of the data - + The duration of the time series. + random_state: None, int, np.random.Generator, or jax.random.KeyArray + The random state to use for noise generation. + Returns ======= - array_like: white noise - array_like: frequency array + white_noise: array_like + The generated complex white noise in the frequency domain. + frequencies: array_like + The corresponding frequency array for the white noise. """ - from . import random - - number_of_samples = duration * sampling_frequency - number_of_samples = int(np.round(number_of_samples)) + frequencies = np.arange(number_of_samples) / duration - frequencies = create_frequency_series(sampling_frequency, duration) + rng = random.resolve_random_state(random_state) - norm1 = 0.5 * duration**0.5 - re1, im1 = random.rng.normal(0, norm1, (2, len(frequencies))) + norm1 = 0.5 * duration**0.5 + re1, im1 = rng.normal(0, norm1, (2, len(frequencies))) + white_noise = re1 + 1j * im1 # set DC and Nyquist = 0 - white_noise[0] = 0 + white_noise = xpx.at(white_noise, 0).set(0) # no Nyquist frequency when N=odd - if np.mod(number_of_samples, 2) == 0: - white_noise[-1] = 0 - + if number_of_samples % 2 == 0: + white_noise = xpx.at(white_noise, -1).set(0) + # python: transpose for use with infft - white_noise = np.transpose(white_noise) - frequencies = np.transpose(frequencies) + white_noise = white_noise.T + frequencies = frequencies.T return white_noise, frequencies +def create_white_noise(sampling_frequency, duration, random_state=None): + """ Create white_noise which is then coloured by a given PSD + + Parameters + ========== + sampling_frequency: float + duration: float + duration of the data + + Returns + ======= + array_like: white noise + array_like: frequency array + """ + number_of_samples = duration * sampling_frequency + number_of_samples = int(np.round(number_of_samples)) + return safe_white_noise(number_of_samples, duration, random_state=random_state) + + def nfft(time_domain_strain, sampling_frequency): """ Perform an FFT while keeping track of the frequency bins. Assumes input time series is real (positive frequencies only). diff --git a/bilby/gw/detector/calibration.py b/bilby/gw/detector/calibration.py index a4cebffe6..c904c707d 100644 --- a/bilby/gw/detector/calibration.py +++ b/bilby/gw/detector/calibration.py @@ -243,7 +243,8 @@ def get_calibration_factor(self, frequency_array, **params): calibration_factor : array-like The factor to multiply the strain by. """ - return np.ones_like(frequency_array) + xp = aac.array_namespace(frequency_array) + return xp.ones_like(frequency_array) def set_calibration_parameters(self, **params): self.params.update({key[len(self.prefix):]: params[key] for key in params diff --git a/bilby/gw/detector/interferometer.py b/bilby/gw/detector/interferometer.py index e45112161..e9e920095 100644 --- a/bilby/gw/detector/interferometer.py +++ b/bilby/gw/detector/interferometer.py @@ -155,7 +155,7 @@ def set_strain_data_from_frequency_domain_strain( start_time=start_time, frequency_array=frequency_array) def set_strain_data_from_power_spectral_density( - self, sampling_frequency, duration, start_time=0): + self, sampling_frequency, duration, start_time=0, *, random_state=None): """ Set the `Interferometer.strain_data` from a power spectal density This uses the `interferometer.power_spectral_density` object to set @@ -174,7 +174,7 @@ def set_strain_data_from_power_spectral_density( """ self.strain_data.set_from_power_spectral_density( self.power_spectral_density, sampling_frequency=sampling_frequency, - duration=duration, start_time=start_time) + duration=duration, start_time=start_time, random_state=random_state) def set_strain_data_from_frame_file( self, frame_file, sampling_frequency, duration, start_time=0, @@ -955,6 +955,7 @@ def from_pickle(cls, filename=None): def set_array_backend(self, xp): self.geometry.set_array_backend(xp=xp) + self.power_spectral_density.set_array_backend(xp=xp) @property def array_backend(self): diff --git a/bilby/gw/detector/networks.py b/bilby/gw/detector/networks.py index 5159d73fe..50b99e175 100644 --- a/bilby/gw/detector/networks.py +++ b/bilby/gw/detector/networks.py @@ -85,7 +85,7 @@ def _check_interferometers(self): logger.warning(e) def set_strain_data_from_power_spectral_densities( - self, sampling_frequency, duration, start_time=0 + self, sampling_frequency, duration, start_time=0, *, random_state=None ): """Set the `Interferometer.strain_data` from the power spectral densities of the detectors @@ -108,6 +108,7 @@ def set_strain_data_from_power_spectral_densities( sampling_frequency=sampling_frequency, duration=duration, start_time=start_time, + random_state=random_state, ) def set_strain_data_from_zero_noise( diff --git a/bilby/gw/detector/psd.py b/bilby/gw/detector/psd.py index e3fe7091a..a5167deb4 100644 --- a/bilby/gw/detector/psd.py +++ b/bilby/gw/detector/psd.py @@ -1,9 +1,11 @@ import os +import array_api_compat as aac +import array_api_extra as xpx import numpy as np -from scipy.interpolate import interp1d from ...compat.utils import xp_wrap +from ...core.utils.calculus import interp1d from ...core import utils from ...core.utils import logger from .strain_data import InterferometerStrainData @@ -206,55 +208,59 @@ def from_aligo(): @property def psd_array(self): - return self.__psd_array + return self._psd_array @psd_array.setter def psd_array(self, psd_array): - self.__check_frequency_array_matches_density_array(psd_array) - self.__psd_array = np.array(psd_array) - self.__asd_array = psd_array ** 0.5 - self.__interpolate_power_spectral_density() + self._check_frequency_array_matches_density_array(psd_array) + self._psd_array = np.array(psd_array) + self._asd_array = psd_array ** 0.5 + self._interpolate_power_spectral_density() @property def asd_array(self): - return self.__asd_array + return self._asd_array @asd_array.setter def asd_array(self, asd_array): - self.__check_frequency_array_matches_density_array(asd_array) - self.__asd_array = np.array(asd_array) - self.__psd_array = asd_array ** 2 - self.__interpolate_power_spectral_density() + self._check_frequency_array_matches_density_array(asd_array) + self._asd_array = np.array(asd_array) + self._psd_array = asd_array ** 2 + self._interpolate_power_spectral_density() - def __check_frequency_array_matches_density_array(self, density_array): + def _check_frequency_array_matches_density_array(self, density_array): if len(self.frequency_array) != len(density_array): raise ValueError('Provided spectral density does not match frequency array. Not updating.\n' 'Length spectral density {}\n Length frequency array {}\n' .format(density_array, self.frequency_array)) - def __interpolate_power_spectral_density(self): + def _interpolate_power_spectral_density(self): """Interpolate the loaded power spectral density so it can be resampled for arbitrary frequency arrays. """ - self.__power_spectral_density_interpolated = interp1d(self.frequency_array, + self._power_spectral_density_interpolated = interp1d(self.frequency_array, self.psd_array, bounds_error=False, fill_value=np.inf) self._update_cache(self.frequency_array) def get_power_spectral_density_array(self, frequency_array): + if aac.is_jax_array(frequency_array): + return self.power_spectral_density_interpolated(frequency_array) if not np.array_equal(frequency_array, self._cache['frequency_array']): self._update_cache(frequency_array=frequency_array) return self._cache['psd_array'] def get_amplitude_spectral_density_array(self, frequency_array): + if aac.is_jax_array(frequency_array): + return self.power_spectral_density_interpolated(frequency_array)**0.5 if not np.array_equal(frequency_array, self._cache['frequency_array']): self._update_cache(frequency_array=frequency_array) return self._cache['asd_array'] @property def power_spectral_density_interpolated(self): - return self.__power_spectral_density_interpolated + return self._power_spectral_density_interpolated @property def asd_file(self): @@ -262,13 +268,13 @@ def asd_file(self): @asd_file.setter def asd_file(self, asd_file): - asd_file = self.__validate_file_name(file=asd_file) + asd_file = self._validate_file_name(file=asd_file) self._asd_file = asd_file if asd_file is not None: - self.__import_amplitude_spectral_density() - self.__check_file_was_asd_file() + self._import_amplitude_spectral_density() + self._check_file_was_asd_file() - def __check_file_was_asd_file(self): + def _check_file_was_asd_file(self): if min(self.asd_array) < 1e-30: logger.warning("You specified an amplitude spectral density file.") logger.warning("{} WARNING {}".format("*" * 30, "*" * 30)) @@ -281,13 +287,13 @@ def psd_file(self): @psd_file.setter def psd_file(self, psd_file): - psd_file = self.__validate_file_name(file=psd_file) + psd_file = self._validate_file_name(file=psd_file) self._psd_file = psd_file if psd_file is not None: - self.__import_power_spectral_density() - self.__check_file_was_psd_file() + self._import_power_spectral_density() + self._check_file_was_psd_file() - def __check_file_was_psd_file(self): + def _check_file_was_psd_file(self): if min(self.psd_array) > 1e-30: logger.warning("You specified a power spectral density file.") logger.warning("{} WARNING {}".format("*" * 30, "*" * 30)) @@ -295,7 +301,7 @@ def __check_file_was_psd_file(self): logger.warning("You may have intended to provide this as an amplitude spectral density.") @staticmethod - def __validate_file_name(file): + def _validate_file_name(file): """ Test if the file exists or is available in the default directory. @@ -334,16 +340,15 @@ def __validate_file_name(file): .format(file)) return file - def __import_amplitude_spectral_density(self): + def _import_amplitude_spectral_density(self): """ Automagically load an amplitude spectral density curve """ self.frequency_array, self.asd_array = np.genfromtxt(self.asd_file).T - def __import_power_spectral_density(self): + def _import_power_spectral_density(self): """ Automagically load a power spectral density curve """ self.frequency_array, self.psd_array = np.genfromtxt(self.psd_file).T - @xp_wrap - def get_noise_realisation(self, sampling_frequency, duration, *, xp=None): + def get_noise_realisation(self, number_of_samples, duration, *, random_state=None): """ Generate frequency Gaussian noise scaled to the power spectral density. @@ -360,9 +365,27 @@ def get_noise_realisation(self, sampling_frequency, duration, *, xp=None): array_like: frequencies related to the frequency domain strain """ - white_noise, frequencies = utils.create_white_noise(sampling_frequency, duration) + white_noise, frequencies = utils.safe_white_noise(number_of_samples, duration, random_state=random_state) with np.errstate(invalid="ignore"): - frequency_domain_strain = self.__power_spectral_density_interpolated(frequencies) ** 0.5 * white_noise - out_of_bounds = (frequencies < min(self.frequency_array)) | (frequencies > max(self.frequency_array)) - frequency_domain_strain[out_of_bounds] = 0 * (1 + 1j) - return xp.asarray(frequency_domain_strain), xp.asarray(frequencies) + frequency_domain_strain = self._power_spectral_density_interpolated(frequencies) ** 0.5 * white_noise + xp = aac.array_namespace(frequency_domain_strain) + out_of_bounds = (frequencies < xp.min(self.frequency_array)) | (frequencies > xp.max(self.frequency_array)) + frequency_domain_strain = xpx.at(frequency_domain_strain, out_of_bounds).set(0j) + return xp.nan_to_num(frequency_domain_strain), xp.asarray(frequencies) + + def set_array_backend(self, xp): + """ Set the array backend for the cached arrays + + Parameters + ========== + xp: module + The array backend to use for the cached arrays + + """ + self.frequency_array = xp.asarray(self.frequency_array) + self._asd_array = xp.asarray(self._asd_array) + self._psd_array = xp.asarray(self._psd_array) + self._cache['frequency_array'] = xp.asarray(self._cache['frequency_array']) + self._cache['psd_array'] = xp.asarray(self._cache['psd_array']) + self._cache['asd_array'] = xp.asarray(self._cache['asd_array']) + self._interpolate_power_spectral_density() diff --git a/bilby/gw/detector/strain_data.py b/bilby/gw/detector/strain_data.py index 017d2ea50..1383d9d8c 100644 --- a/bilby/gw/detector/strain_data.py +++ b/bilby/gw/detector/strain_data.py @@ -103,8 +103,10 @@ def minimum_frequency(self, minimum_frequency): def maximum_frequency(self): """ Force the maximum frequency be less than the Nyquist frequency """ if self.sampling_frequency is not None: - if 2 * self._maximum_frequency > self.sampling_frequency: - self._maximum_frequency = self.sampling_frequency / 2. + xp = array_module(self._maximum_frequency) + self._maximum_frequency = xp.minimum( + self._maximum_frequency, self.sampling_frequency / 2 + ) return self._maximum_frequency @maximum_frequency.setter @@ -624,7 +626,7 @@ def set_from_frequency_domain_strain( def set_from_power_spectral_density( self, power_spectral_density, sampling_frequency, duration, - start_time=0): + start_time=0, *, random_state=None): """ Set the `frequency_domain_strain` by generating a noise realisation Parameters @@ -648,9 +650,13 @@ def set_from_power_spectral_density( 'power_spectal_density') frequency_domain_strain, frequency_array = \ power_spectral_density.get_noise_realisation( - self.sampling_frequency, self.duration) + self.frequency_array.shape[0], self.duration, random_state=random_state) - if np.array_equal(frequency_array, self.frequency_array): + xp = aac.array_namespace(frequency_domain_strain) + self._frequency_array = xp.asarray(self.frequency_array) + self._time_array = xp.asarray(self.time_array) + + if self.duration == duration and frequency_array.shape == self.frequency_array.shape: self._frequency_domain_strain = frequency_domain_strain else: raise ValueError("Data frequencies do not match frequency_array") diff --git a/bilby/gw/likelihood/multiband.py b/bilby/gw/likelihood/multiband.py index c9023b181..264d9d8b9 100644 --- a/bilby/gw/likelihood/multiband.py +++ b/bilby/gw/likelihood/multiband.py @@ -712,9 +712,6 @@ def setup_multibanding_from_weights(self, weights): setattr(self, key, value) def _setup_time_marginalization_multiband(self): - self._beam_pattern_reference_time = ( - self.priors['geocent_time'].minimum + self.priors['geocent_time'].maximum - ) / 2 N = self.Nbs[-1] // 2 self._delta_tc = self.durations[0] / N self._times = self.interferometers.start_time + np.arange(N) * self._delta_tc @@ -723,6 +720,9 @@ def _setup_time_marginalization_multiband(self): self._full_d_h = np.zeros(N, dtype=complex) # idxs to convert full frequency points to banded frequency points, used for filling _full_d_h. self._full_to_multiband = [int(f * self.durations[0]) for f in self.banded_frequency_points] + self._beam_pattern_reference_time = ( + self.priors['geocent_time'].minimum + self.priors['geocent_time'].maximum + ) / 2 self.interferometers.reference_time = self._beam_pattern_reference_time def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array=True, parameters): @@ -755,10 +755,10 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array modes, parameters, frequencies=self.banded_frequency_points ) - d_inner_h = (strain @ self.linear_coeffs[interferometer.name]).conj() - xp = array_module(strain) + d_inner_h = xp.conj(xp.dot(strain, self.linear_coeffs[interferometer.name])) + if self.linear_interpolation: optimal_snr_squared = xp.vdot( xp.abs(strain)**2, @@ -784,7 +784,6 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array xp.asarray(self.wths[interferometer.name][b]) ) thbc = xp.fft.rfft(xp.asarray(self.hbcs[interferometer.name][b])) - print(self.Ibcs[interferometer.name][b]) optimal_snr_squared += (4. / self.Tbhats[b]) * xp.vdot( xp.abs(thbc)**2, xp.asarray(self.Ibcs[interferometer.name][b].real)) diff --git a/bilby/gw/prior.py b/bilby/gw/prior.py index 9127edeb2..d0dc00520 100644 --- a/bilby/gw/prior.py +++ b/bilby/gw/prior.py @@ -441,15 +441,16 @@ def rescale(self, val, *, xp=None): val = 2 * xp.minimum(val, 1 - val) return self.icdf(val) - def prob(self, val, *, xp=np): + def prob(self, val, *, xp=None): in_prior = (val >= self.minimum) & (val <= self.maximum) with np.errstate(invalid="ignore"): prob = (1. + val)**(2. / 5.) / (val**(6. / 5.)) / self.norm * in_prior return prob - def ln_prob(self, val, *, xp=np): + @xp_wrap + def ln_prob(self, val, *, xp=None): with np.errstate(divide="ignore"): - return np.log(self.prob(val, xp=xp)) + return xp.log(self.prob(val, xp=xp)) class AlignedSpin(Interped): @@ -1607,7 +1608,8 @@ def _check_norm(array): norm = np.finfo(array.dtype).eps return array / norm - def _sample(self, size, *, xp=np, **kwargs): + + def _sample(self, size, *, xp=np, random_state=None, **kwargs): """ Overwrites the _sample method of BaseJoint Prior. Picks a pixel value according to their probabilities, then uniformly samples ra, and decs that are contained in chosen pixel. If the PriorDist includes distance it then @@ -1626,21 +1628,23 @@ def _sample(self, size, *, xp=np, **kwargs): sample : array_like sample of ra, and dec (and distance if 3D=True) """ - sample_pix = random.rng.choice(self.npix, size=size, p=self.prob, replace=True) + rng = random.resolve_random_state(random_state) + + sample_pix = rng.choice(self.npix, size=size, p=self.prob, replace=True) sample = np.empty((size, self.num_vars)) for samp in range(size): theta, ra = self.hp.pix2ang(self.nside, sample_pix[samp]) dec = 0.5 * np.pi - theta if self.distance: self.update_distance(sample_pix[samp]) - dist = self.draw_distance(sample_pix[samp]) - ra_dec = self.draw_from_pixel(ra, dec, sample_pix[samp]) + dist = self.draw_distance(sample_pix[samp], random_state=rng) + ra_dec = self.draw_from_pixel(ra, dec, sample_pix[samp], random_state=rng) sample[samp, :] = [ra_dec[0], ra_dec[1], dist] else: - sample[samp, :] = self.draw_from_pixel(ra, dec, sample_pix[samp]) + sample[samp, :] = self.draw_from_pixel(ra, dec, sample_pix[samp], random_state=rng) return xp.asarray(sample.reshape((-1, self.num_vars))) - def draw_distance(self, pix): + def draw_distance(self, pix, *, random_state=None): """ Method to recursively draw a distance value from the given set distance distribution and check that it is in the bounds @@ -1656,16 +1660,18 @@ def draw_distance(self, pix): dist : float sample drawn from the distance distribution at set pixel index """ + rng = random.resolve_random_state(random_state) + if self.distmu[pix] == np.inf or self.distmu[pix] <= 0: return 0 - dist = self.distance_icdf(random.rng.uniform(0, 1)) + dist = self.distance_icdf(rng.uniform(0, 1)) name = self.names[-1] if (dist > self.bounds[name][1]) | (dist < self.bounds[name][0]): - self.draw_distance(pix) + self.draw_distance(pix, random_state=rng) else: return dist - def draw_from_pixel(self, ra, dec, pix): + def draw_from_pixel(self, ra, dec, pix, *, random_state=None): """ Recursive function to uniformly draw ra, and dec values that are located in the given pixel @@ -1683,12 +1689,14 @@ def draw_from_pixel(self, ra, dec, pix): ra_dec : tuple this returns a tuple of ra, and dec sampled uniformly that are in the pixel given """ + rng = random.resolve_random_state(random_state) + if not self.check_in_pixel(ra, dec, pix): - self.draw_from_pixel(ra, dec, pix) + self.draw_from_pixel(ra, dec, pix, random_state=rng) return np.array( [ - random.rng.uniform(ra - self.pixel_length, ra + self.pixel_length), - random.rng.uniform(dec - self.pixel_length, dec + self.pixel_length), + rng.uniform(ra - self.pixel_length, ra + self.pixel_length), + rng.uniform(dec - self.pixel_length, dec + self.pixel_length), ] ) diff --git a/bilby/gw/time.py b/bilby/gw/time.py index 3c115646b..367a95634 100644 --- a/bilby/gw/time.py +++ b/bilby/gw/time.py @@ -187,7 +187,7 @@ def n_leap_seconds(gps_time, leap_seconds): @dispatch -def n_leap_seconds(gps_time: np.ndarray | float | int): # noqa F811 +def n_leap_seconds(gps_time: np.ndarray | np.number | float | int): # noqa F811 xp = array_module(gps_time) return n_leap_seconds(gps_time, xp.asarray(LEAP_SECONDS)) diff --git a/bilby/gw/waveform_generator.py b/bilby/gw/waveform_generator.py index a27808738..cc00eb395 100644 --- a/bilby/gw/waveform_generator.py +++ b/bilby/gw/waveform_generator.py @@ -110,7 +110,7 @@ def __repr__(self): .format(self.duration, self.sampling_frequency, self.start_time, fdsm_name, tdsm_name, param_conv_name, self.waveform_arguments) - def frequency_domain_strain(self, parameters=None): + def frequency_domain_strain(self, parameters=None, *, xp=np): """ Wrapper to source_model. Converts parameters with self.parameter_conversion before handing it off to the source model. @@ -137,9 +137,10 @@ def frequency_domain_strain(self, parameters=None): parameters=parameters, transformation_function=utils.nfft, transformed_model=self.time_domain_source_model, - transformed_model_data_points=self.time_array) + transformed_model_data_points=self.time_array, + xp=xp) - def time_domain_strain(self, parameters=None): + def time_domain_strain(self, parameters=None, *, xp=np): """ Wrapper to source_model. Converts parameters with self.parameter_conversion before handing it off to the source model. @@ -148,7 +149,7 @@ def time_domain_strain(self, parameters=None): Parameters ========== - parameters: dict, None + parameters: dict, optional Parameters to evaluate the waveform for. If not passed and the generator has been called previously, the last used parameters will be used. @@ -167,10 +168,11 @@ def time_domain_strain(self, parameters=None): parameters=parameters, transformation_function=utils.infft, transformed_model=self.frequency_domain_source_model, - transformed_model_data_points=self.frequency_array) + transformed_model_data_points=self.frequency_array, + xp=xp) def _calculate_strain(self, model, model_data_points, transformation_function, transformed_model, - transformed_model_data_points, parameters): + transformed_model_data_points, parameters, *, xp=np): if parameters is None: parameters = self._cache.get('parameters', None) if parameters is None: @@ -188,9 +190,9 @@ def _calculate_strain(self, model, model_data_points, transformation_function, t self._cache['transformed_model'] = transformed_model parameters = self._format_parameters(parameters) if model is not None: - model_strain = self._strain_from_model(model_data_points, model, parameters) + model_strain = self._strain_from_model(xp.asarray(model_data_points), model, parameters) elif transformed_model is not None: - model_strain = self._strain_from_transformed_model(transformed_model_data_points, transformed_model, + model_strain = self._strain_from_transformed_model(xp.asarray(transformed_model_data_points), transformed_model, transformation_function, parameters) else: raise RuntimeError("No source model given") diff --git a/bilby/hyper/likelihood.py b/bilby/hyper/likelihood.py index f37ba1333..fa113a048 100644 --- a/bilby/hyper/likelihood.py +++ b/bilby/hyper/likelihood.py @@ -1,6 +1,7 @@ import logging +import array_api_compat as aac import numpy as np from ..compat.utils import array_module @@ -50,14 +51,17 @@ def __init__(self, posteriors, hyper_prior, sampling_prior=None, self.evidence_factor = np.sum(log_evidences) else: self.evidence_factor = np.nan - self.posteriors = posteriors + if aac.is_jax_namespace(xp): + self.posteriors = None + else: + self.posteriors = posteriors self.hyper_prior = hyper_prior self.sampling_prior = sampling_prior self.max_samples = max_samples super(HyperparameterLikelihood, self).__init__() - self.data = self.resample_posteriors(xp=xp) - self.n_posteriors = len(self.posteriors) + self.data = self.resample_posteriors(posteriors=posteriors, xp=xp) + self.n_posteriors = len(posteriors) self.samples_per_posterior = self.max_samples self.samples_factor =\ - self.n_posteriors * np.log(self.samples_per_posterior) @@ -75,7 +79,7 @@ def noise_log_likelihood(self): def log_likelihood(self, parameters): return self.noise_log_likelihood() + self.log_likelihood_ratio(parameters=parameters) - def resample_posteriors(self, max_samples=None, xp=np): + def resample_posteriors(self, posteriors=None, max_samples=None, xp=np): """ Convert list of pandas DataFrame object to dict of arrays. @@ -90,18 +94,26 @@ def resample_posteriors(self, max_samples=None, xp=np): Dictionary containing arrays of size (n_posteriors, max_samples) There is a key for each shared key in self.posteriors. """ + if posteriors is None: + posteriors = self.posteriors + if isinstance(posteriors, int): + raise ValueError( + "Input posteriors is an integer. This may have been intended for the " + "max_samples argument. The API changed in Bilby v3." + ) + if max_samples is not None: self.max_samples = max_samples - for posterior in self.posteriors: + for posterior in posteriors: self.max_samples = min(len(posterior), self.max_samples) - data = {key: [] for key in self.posteriors[0]} + data = {key: [] for key in posteriors[0]} if 'log_prior' in data.keys(): data.pop('log_prior') if 'prior' not in data.keys(): data['prior'] = [] logging.debug('Downsampling to {} samples per posterior.'.format( self.max_samples)) - for posterior in self.posteriors: + for posterior in posteriors: temp = posterior.sample(self.max_samples) if self.sampling_prior is not None: temp['prior'] = self.sampling_prior.prob(temp, axis=0) diff --git a/docs/rng.rst b/docs/rng.rst index 6024d5b2b..709657a5e 100644 --- a/docs/rng.rst +++ b/docs/rng.rst @@ -57,4 +57,29 @@ For example: .. note:: Some sampler interfaces do not support seeding. +-------------------------------------------------------- +Random number generation and non-:code:`NumpPy` backends +-------------------------------------------------------- + +To support random number generation with non-:code:`NumPy` array backends, +any :code:`bilby` function or method that supports random number generation and accepts a +:code:`random_state` argument. +This argument should be one of the following types: + +- :code:`None` (the default): the function will use the :code:`bilby` global + :code:`numpy` random number generator (set using :code:`bilby.core.random.seed`). +- :code:`numpy.random.Generator`: the function will use the provided generator. +- :code:`orng.ArrayRNG`: the function will use the provided :code:`orng` random number generator. +- :code:`int`: the function will create a new :code:`numpy` random number generator seeded with + the provided integer and use it for random number generation. +- :code:`jax.random.PRNGKey`: the function will create a new :code:`orng` random number generator + with the "jax" backend seeded with the provided key and use it for random number generation. + +For example, +.. code:: python + + >>> import orng + >>> rng = orng.ArrayRNG("jax", seed=1234) + >>> x = rng.uniform() + >>> priors.sample(xp=jnp, rng=rng) diff --git a/examples/gw_examples/injection_examples/jax_fast_tutorial.py b/examples/gw_examples/injection_examples/jax_fast_tutorial.py index 56b1b4d3a..22a64c935 100644 --- a/examples/gw_examples/injection_examples/jax_fast_tutorial.py +++ b/examples/gw_examples/injection_examples/jax_fast_tutorial.py @@ -19,7 +19,7 @@ import jax.numpy as jnp import numpy as np from bilby.compat.jax import JittedLikelihood -from ripple.waveforms import IMRPhenomPv2 +from ripplegw.waveforms import IMRPhenomPv2 jax.config.update("jax_enable_x64", True) diff --git a/requirements.txt b/requirements.txt index f1a91484d..b9fe7c056 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,3 +14,4 @@ tqdm h5py attrs plum-dispatch +orng diff --git a/test/conftest.py b/test/conftest.py index 83e7a89a6..b52098a7c 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -66,6 +66,17 @@ def _xp(request): return aac.get_namespace(xp.ones(1)) +def _rng(xp): + from bilby.core.utils.random import resolve_random_state + + if xp.__name__ == "numpy": + return resolve_random_state(12345) + elif xp.__name__ == "jax.numpy": + import jax.random + + return resolve_random_state(jax.random.PRNGKey(12345)) + + @pytest.fixture def xp(request): return _xp(request) @@ -74,3 +85,4 @@ def xp(request): @pytest.fixture(scope="class") def xp_class(request): request.cls.xp = _xp(request) + request.cls.rng = _rng(request.cls.xp) diff --git a/test/core/prior/analytical_test.py b/test/core/prior/analytical_test.py index 09942ba07..cbcadd880 100644 --- a/test/core/prior/analytical_test.py +++ b/test/core/prior/analytical_test.py @@ -18,7 +18,7 @@ def test_single_sample(self): discrete_value_prior = bilby.core.prior.DiscreteValues(values) in_prior = True for _ in range(1000): - s = discrete_value_prior.sample(xp=self.xp) + s = discrete_value_prior.sample(random_state=self.rng) if s not in values: in_prior = False self.assertTrue(in_prior) @@ -28,7 +28,7 @@ def test_array_sample(self): nvalues = 4 discrete_value_prior = bilby.core.prior.DiscreteValues(values) N = 100000 - s = discrete_value_prior.sample(N, xp=self.xp) + s = discrete_value_prior.sample(N, random_state=self.rng) zeros = np.sum(s == 1.0) ones = np.sum(s == 1.1) twos = np.sum(s == 1.2) @@ -100,7 +100,7 @@ def test_single_sample(self): categorical_prior = bilby.core.prior.Categorical(3) in_prior = True for _ in range(1000): - s = categorical_prior.sample(xp=self.xp) + s = categorical_prior.sample(random_state=self.rng) if s not in [0, 1, 2]: in_prior = False self.assertTrue(in_prior) @@ -109,7 +109,7 @@ def test_array_sample(self): ncat = 4 categorical_prior = bilby.core.prior.Categorical(ncat) N = 100000 - s = categorical_prior.sample(N, xp=self.xp) + s = categorical_prior.sample(N, random_state=self.rng) self.assertEqual(aac.get_namespace(s), self.xp) s = np.asarray(s) zeros = np.sum(s == 0) @@ -173,7 +173,7 @@ def test_single_sample(self): categorical_prior = bilby.core.prior.WeightedCategorical(3, [1, 2, 3]) in_prior = True for _ in range(1000): - s = categorical_prior.sample(xp=self.xp) + s = categorical_prior.sample(random_state=self.rng) if s not in [0, 1, 2]: in_prior = False self.assertTrue(in_prior) @@ -189,7 +189,7 @@ def test_array_sample(self): weights = np.arange(1, ncat + 1) categorical_prior = bilby.core.prior.WeightedCategorical(ncat, weights=weights) N = 100000 - s = categorical_prior.sample(N, xp=self.xp) + s = categorical_prior.sample(N, random_state=self.rng) self.assertEqual(aac.get_namespace(s), self.xp) s = np.asarray(s) cases = 0 @@ -257,7 +257,7 @@ def test_cdf(self): weights = np.arange(1, N + 1) categorical_prior = bilby.core.prior.WeightedCategorical(N, weights=weights) - sample = categorical_prior.sample(size=10) + sample = categorical_prior.sample(size=10, random_state=self.rng) original = self.xp.asarray(sample) new = self.xp.asarray(categorical_prior.rescale( categorical_prior.cdf(sample) diff --git a/test/core/prior/base_test.py b/test/core/prior/base_test.py index 469c53ece..e61bbbf11 100644 --- a/test/core/prior/base_test.py +++ b/test/core/prior/base_test.py @@ -158,7 +158,7 @@ def conversion_func(parameters): def test_prob_integrate_to_one(self): keys = ["a", "b", "c"] n_samples = 1000000 - samples = self.priors.sample_subset(keys=keys, size=n_samples, xp=self.xp) + samples = self.priors.sample_subset(keys=keys, size=n_samples, random_state=self.rng) prob = self.priors.prob(samples, axis=0) self.assertEqual(aac.get_namespace(prob), self.xp) prob = np.asarray(prob) diff --git a/test/core/prior/conditional_test.py b/test/core/prior/conditional_test.py index 5850f5ecf..2d3a874f0 100644 --- a/test/core/prior/conditional_test.py +++ b/test/core/prior/conditional_test.py @@ -470,7 +470,7 @@ def tearDown(self): shutil.rmtree("priors") def test_samples_correct_type(self): - samples = self.priors.sample(10, xp=self.xp) + samples = self.priors.sample(10, random_state=self.rng) self.assertEqual(aac.get_namespace(samples["dirichlet_1"]), self.xp) def test_samples_sum_to_less_than_one(self): diff --git a/test/core/prior/dict_test.py b/test/core/prior/dict_test.py index 425e1ff49..73c7cddb3 100644 --- a/test/core/prior/dict_test.py +++ b/test/core/prior/dict_test.py @@ -273,7 +273,7 @@ def test_sample_subset_correct_size(self): size = 7 samples = self.prior_set_from_dict.sample_subset( keys=self.prior_set_from_dict.keys(), size=size, - xp=self.xp, + random_state=self.rng, ) self.assertEqual(len(self.prior_set_from_dict), len(samples)) for key in samples: @@ -284,16 +284,17 @@ def test_sample_subset_correct_size_when_non_priors_in_dict(self): self.prior_set_from_dict["asdf"] = "not_a_prior" samples = self.prior_set_from_dict.sample_subset( keys=self.prior_set_from_dict.keys(), - xp=self.xp, + random_state=self.rng, ) self.assertEqual(len(self.prior_set_from_dict) - 1, len(samples)) for key in samples: - self.assertIsNotNone(aac.get_namespace(samples[key]), self.xp) + if not isinstance(samples[key], (int, float)): + self.assertIsNotNone(aac.get_namespace(samples[key]), self.xp) def test_sample_subset_with_actual_subset(self): size = 3 samples = self.prior_set_from_dict.sample_subset( - keys=["length"], size=size, xp=self.xp + keys=["length"], size=size, random_state=self.rng ) expected = dict(length=self.xp.asarray([42.0, 42.0, 42.0])) self.assertTrue(np.array_equal(expected["length"], samples["length"])) @@ -303,7 +304,7 @@ def test_sample_subset_constrained_as_array(self): size = 3 keys = ["mass", "speed"] out = self.prior_set_from_dict.sample_subset_constrained_as_array( - keys, size, xp=self.xp + keys, size, random_state=self.rng ) self.assertEqual(aac.get_namespace(out), self.xp) self.assertTrue(out.shape == (len(keys), size)) @@ -326,7 +327,7 @@ def conversion_function(parameters): with patch("bilby.core.prior.logger.warning") as mock_warning: samples1 = priors1.sample_subset_constrained( - keys=list(priors1.keys()), size=N, xp=self.xp + keys=list(priors1.keys()), size=N, random_state=self.rng ) self.assertEqual(len(priors1) - 1, len(samples1)) for key in samples1: @@ -339,7 +340,7 @@ def conversion_function(parameters): with patch("bilby.core.prior.logger.warning") as mock_warning: samples2 = priors2.sample_subset_constrained( - keys=list(priors2.keys()), size=N, xp=self.xp + keys=list(priors2.keys()), size=N, random_state=self.rng ) self.assertEqual(len(priors2), len(samples2)) for key in samples2: @@ -350,10 +351,10 @@ def test_sample(self): size = 7 bilby.core.utils.random.seed(42) samples1 = self.prior_set_from_dict.sample_subset( - keys=self.prior_set_from_dict.keys(), size=size, xp=self.xp + keys=self.prior_set_from_dict.keys(), size=size, random_state=self.rng ) bilby.core.utils.random.seed(42) - samples2 = self.prior_set_from_dict.sample(size=size, xp=self.xp) + samples2 = self.prior_set_from_dict.sample(size=size, random_state=self.rng) self.assertEqual(set(samples1.keys()), set(samples2.keys())) for key in samples1: self.assertTrue(np.array_equal(samples1[key], samples2[key])) @@ -361,7 +362,7 @@ def test_sample(self): self.assertEqual(aac.get_namespace(samples2[key]), self.xp) def test_prob(self): - samples = self.prior_set_from_dict.sample_subset(keys=["mass", "speed"], xp=self.xp) + samples = self.prior_set_from_dict.sample_subset(keys=["mass", "speed"], random_state=self.rng) expected = self.first_prior.prob(samples["mass"]) * self.second_prior.prob( samples["speed"] ) @@ -369,7 +370,7 @@ def test_prob(self): self.assertEqual(aac.get_namespace(expected), self.xp) def test_ln_prob(self): - samples = self.prior_set_from_dict.sample_subset(keys=["mass", "speed"], xp=self.xp) + samples = self.prior_set_from_dict.sample_subset(keys=["mass", "speed"], random_state=self.rng) expected = self.first_prior.ln_prob( samples["mass"] ) + self.second_prior.ln_prob(samples["speed"]) @@ -398,7 +399,7 @@ def test_cdf(self): Note that the format of inputs/outputs is different between the two methods. """ - sample = self.prior_set_from_dict.sample(xp=self.xp) + sample = self.prior_set_from_dict.sample(random_state=self.rng) original = self.xp.asarray(list(sample.values())) new = self.xp.asarray(self.prior_set_from_dict.rescale( sample.keys(), diff --git a/test/core/prior/prior_test.py b/test/core/prior/prior_test.py index 67fbd2422..b03876cef 100644 --- a/test/core/prior/prior_test.py +++ b/test/core/prior/prior_test.py @@ -268,6 +268,10 @@ def condition_func(reference_params, test_param): def tearDown(self): del self.priors + def _validate_return_type(self, val): + if not isinstance(val, (int, float)): + self.assertEqual(aac.get_namespace(val), self.xp) + def test_minimum_rescaling(self): """Test the the rescaling works as expected.""" for prior in self.priors: @@ -324,15 +328,15 @@ def test_many_sample_rescaling(self): self.assertTrue( all((many_samples >= prior.minimum) & (many_samples <= prior.maximum)) ) - self.assertEqual(aac.get_namespace(many_samples), self.xp) + self._validate_return_type(many_samples) def test_least_recently_sampled(self): for prior in self.priors: - least_recently_sampled_expected = prior.sample(xp=self.xp) + least_recently_sampled_expected = prior.sample(random_state=self.rng) self.assertEqual( least_recently_sampled_expected, prior.least_recently_sampled ) - self.assertEqual(aac.get_namespace(least_recently_sampled_expected), self.xp) + self._validate_return_type(least_recently_sampled_expected) def test_sampling_single(self): """Test that sampling from the prior always returns values within its domain.""" @@ -340,11 +344,11 @@ def test_sampling_single(self): if isinstance(prior, bilby.core.prior.analytical.SymmetricLogUniform): # SymmetricLogUniform has support down to -maximum continue - single_sample = prior.sample(xp=self.xp) + single_sample = prior.sample(random_state=self.rng) self.assertTrue( (single_sample >= prior.minimum) & (single_sample <= prior.maximum) ) - self.assertEqual(aac.get_namespace(single_sample), self.xp) + self._validate_return_type(single_sample) def test_sampling_many(self): """Test that sampling from the prior always returns values within its domain.""" @@ -352,12 +356,12 @@ def test_sampling_many(self): if isinstance(prior, bilby.core.prior.analytical.SymmetricLogUniform): # SymmetricLogUniform has support down to -maximum continue - many_samples = prior.sample(5000, xp=self.xp) + many_samples = prior.sample(5000, random_state=self.rng) self.assertTrue( (all(many_samples >= prior.minimum)) & (all(many_samples <= prior.maximum)) ) - self.assertEqual(aac.get_namespace(many_samples), self.xp) + self._validate_return_type(many_samples) def test_probability_above_domain(self): """Test that the prior probability is non-negative in domain of validity and zero outside.""" @@ -390,20 +394,20 @@ def test_probability_below_domain(self): def test_least_recently_sampled_2(self): for prior in self.priors: - lrs = prior.sample(xp=self.xp) + lrs = prior.sample(random_state=self.rng) self.assertEqual(lrs, prior.least_recently_sampled) - self.assertEqual(aac.get_namespace(lrs), self.xp) + self._validate_return_type(lrs) def test_prob_and_ln_prob(self): for prior in self.priors: - sample = prior.sample(xp=self.xp) + sample = prior.sample(random_state=self.rng) if not bilby.core.prior.JointPrior in prior.__class__.__mro__: # noqa # due to the way that the Multivariate Gaussian prior must sequentially call # the prob and ln_prob functions, it must be ignored in this test. lnprob = prior.ln_prob(sample) prob = prior.prob(sample) - self.assertEqual(aac.get_namespace(lnprob), self.xp) - self.assertEqual(aac.get_namespace(prob), self.xp) + self._validate_return_type(lnprob) + self._validate_return_type(prob) # lower precision for jax running tests with float32 lnprob = np.asarray(lnprob) prob = np.asarray(prob) @@ -411,15 +415,15 @@ def test_prob_and_ln_prob(self): def test_many_prob_and_many_ln_prob(self): for prior in self.priors: - samples = prior.sample(10, xp=self.xp) + samples = prior.sample(10, random_state=self.rng) if not bilby.core.prior.JointPrior in prior.__class__.__mro__: # noqa ln_probs = prior.ln_prob(samples) probs = prior.prob(samples) for sample, logp, p in zip(samples, ln_probs, probs): self.assertAlmostEqual(prior.ln_prob(sample), logp) self.assertAlmostEqual(prior.prob(sample), p) - self.assertEqual(aac.get_namespace(ln_probs), self.xp) - self.assertEqual(aac.get_namespace(probs), self.xp) + self._validate_return_type(ln_probs) + self._validate_return_type(probs) def test_cdf_is_inverse_of_rescaling(self): domain = self.xp.linspace(0, 1, 100) @@ -441,11 +445,11 @@ def test_cdf_is_inverse_of_rescaling(self): self.assertTrue(np.array_equal(rescaled, rescaled_2)) max_difference = max(np.abs(cdf_vals - cdf_vals_2)) for arr in [rescaled, rescaled_2, cdf_vals, cdf_vals_2]: - self.assertEqual(aac.get_namespace(arr), self.xp) + self._validate_return_type(arr) else: rescaled = prior.rescale(domain) max_difference = max(np.abs(domain - prior.cdf(rescaled))) - self.assertEqual(aac.get_namespace(rescaled), self.xp) + self._validate_return_type(rescaled) self.assertLess(max_difference, threshold) def test_cdf_one_above_domain(self): @@ -632,7 +636,7 @@ def test_probability_in_domain(self): print(prior) prob = prior.prob(domain) print(min(prob)) - self.assertEqual(aac.get_namespace(prob), self.xp) + self._validate_return_type(prob) prob = np.asarray(prob) self.assertTrue(all(prob >= 0)) @@ -701,14 +705,14 @@ def test_normalized(self): elif isinstance(prior, bilby.core.prior.WeightedDiscreteValues): domain = prior.values probs = prior.prob(self.xp.asarray(domain)) - self.assertEqual(aac.get_namespace(probs), self.xp) + self._validate_return_type(probs) self.assertTrue(np.sum(np.asarray(probs)) == 1) continue else: domain = np.linspace(prior.minimum, prior.maximum, 1000) probs = prior.prob(self.xp.asarray(domain)) self.assertAlmostEqual(trapezoid(np.array(probs), domain), 1, 3) - self.assertEqual(aac.get_namespace(probs), self.xp) + self._validate_return_type(probs) def test_accuracy(self): """Test that each of the priors' functions is calculated accurately, as compared to scipy's calculations""" @@ -896,7 +900,7 @@ def test_set_maximum_setting(self): ): continue prior.maximum = (prior.maximum + prior.minimum) / 2 - self.assertTrue(max(prior.sample(10000, xp=self.xp)) < prior.maximum) + self.assertTrue(max(prior.sample(10000, random_state=self.rng)) < prior.maximum) def test_set_minimum_setting(self): for prior in self.priors: @@ -922,7 +926,7 @@ def test_set_minimum_setting(self): ): continue prior.minimum = (prior.maximum + prior.minimum) / 2 - self.assertTrue(min(prior.sample(10000, xp=self.xp)) > prior.minimum) + self.assertTrue(min(prior.sample(10000, random_state=self.rng)) > prior.minimum) if __name__ == "__main__": diff --git a/test/gw/likelihood_test.py b/test/gw/likelihood_test.py index 05201b731..128e4bfe0 100644 --- a/test/gw/likelihood_test.py +++ b/test/gw/likelihood_test.py @@ -1398,11 +1398,9 @@ def test_matches_original_likelihood( parameters = deepcopy(self.test_parameters) if add_cal_errors: parameters.update(self.calibration_parameters) + ll = likelihood.log_likelihood_ratio(parameters) llmb = likelihood_mb.log_likelihood_ratio(parameters) - self.assertLess( - abs(likelihood.log_likelihood_ratio(parameters) - llmb), - tolerance - ) + self.assertLess(abs(ll - llmb), tolerance) self.assertEqual(aac.get_namespace(llmb), self.xp) def test_large_accuracy_factor(self): diff --git a/test/gw/prior_test.py b/test/gw/prior_test.py index 277235a62..7596e5099 100644 --- a/test/gw/prior_test.py +++ b/test/gw/prior_test.py @@ -259,7 +259,7 @@ def test_bilby_to_lalinference(self): ) nsamples = 5000 - bilby_samples = bilby_prior.sample(nsamples, xp=self.xp) + bilby_samples = bilby_prior.sample(nsamples, random_state=self.rng) bilby_samples, _ = conversion.convert_to_lal_binary_black_hole_parameters( bilby_samples ) @@ -268,7 +268,7 @@ def test_bilby_to_lalinference(self): # Quicker way to generate LA prior samples (rather than specifying Constraint) lalinf_samples = [] while len(lalinf_samples) < nsamples: - s = lalinf_prior.sample() + s = lalinf_prior.sample(random_state=self.rng) if s["mass_1"] < s["mass_2"]: s["mass_1"], s["mass_2"] = s["mass_2"], s["mass_1"] if s["mass_2"] / s["mass_1"] > 0.125: @@ -580,8 +580,8 @@ def test_non_analytic_form_has_correct_statistics(self): a_prior = bilby.core.prior.TruncatedGaussian(mu=0, sigma=0.1, minimum=0, maximum=1) z_prior = bilby.core.prior.TruncatedGaussian(mu=0.4, sigma=0.2, minimum=-1, maximum=1) chi_prior = bilby.gw.prior.AlignedSpin(a_prior, z_prior) - chis = chi_prior.sample(100000, xp=self.xp) - alts = a_prior.sample(100000, xp=self.xp) * z_prior.sample(100000, xp=self.xp) + chis = chi_prior.sample(100000, random_state=self.rng) + alts = a_prior.sample(100000, random_state=self.rng) * z_prior.sample(100000, random_state=self.rng) self.assertAlmostEqual(np.mean(chis), np.mean(alts), 2) self.assertAlmostEqual(np.std(chis), np.std(alts), 2) self.assertEqual(aac.get_namespace(chis), self.xp) @@ -600,7 +600,7 @@ def test_marginalized_prior_is_uniform(self): priors["a_1"] = bilby.gw.prior.ConditionalChiUniformSpinMagnitude( minimum=0.1, maximum=priors["chi_1"].maximum, name="a_1" ) - samples = priors.sample(100000, xp=self.xp)["a_1"] + samples = priors.sample(100000, random_state=self.rng)["a_1"] ks = ks_2samp(samples, np.random.uniform(0, priors["chi_1"].maximum, 100000)) self.assertTrue(ks.pvalue > 0.001) self.assertEqual(aac.get_namespace(samples), self.xp) diff --git a/test/hyper/hyper_pe_test.py b/test/hyper/hyper_pe_test.py index 4ca58927d..83b3c0a51 100644 --- a/test/hyper/hyper_pe_test.py +++ b/test/hyper/hyper_pe_test.py @@ -105,7 +105,7 @@ def test_resample_without_max_samples(self): self.sampling_model, log_evidences=self.log_evidences, ) - resampled = like.resample_posteriors(10) + resampled = like.resample_posteriors(max_samples=10) self.assertEqual(resampled["a"].shape, (len(self.lengths), 10)) From 92f6b2b2368c1aa6f8fbd00499b742650367dbd8 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Mon, 18 May 2026 18:53:17 +0000 Subject: [PATCH 119/140] FMT: fix pre commits --- bilby/core/prior/conditional.py | 2 -- bilby/core/utils/random.py | 6 +++--- bilby/core/utils/series.py | 10 +++++----- bilby/gw/detector/psd.py | 7 +++---- bilby/gw/prior.py | 1 - bilby/gw/waveform_generator.py | 8 ++++++-- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/bilby/core/prior/conditional.py b/bilby/core/prior/conditional.py index c9f8502fe..c68f471dd 100644 --- a/bilby/core/prior/conditional.py +++ b/bilby/core/prior/conditional.py @@ -1,5 +1,3 @@ -import numpy as np - from .base import Prior, PriorException from .interpolated import Interped from .analytical import DeltaFunction, PowerLaw, Uniform, LogUniform, \ diff --git a/bilby/core/utils/random.py b/bilby/core/utils/random.py index 7dac9011c..aa1f998e4 100644 --- a/bilby/core/utils/random.py +++ b/bilby/core/utils/random.py @@ -115,14 +115,14 @@ def resolve_random_state(random_state): Parameters ========== random_state: None, int, np.random.Generator, or jax.random.KeyArray - The random state to resolve. + The random state to resolve. If None, the default random generator will be used. If an int, a new :code:`numpy.random.default_rng` object will be created with that seed. If a :code:`numpy.random.Generator`, it will be returned as is. If a :code:`jax.random.KeyArray`, a corresponding :code:`orng.ArrayRNG` generator will be created and returned. - + Returns ======= np.random.Generator or orng.ArrayRNG @@ -166,4 +166,4 @@ def random_array_module(random_state): import jax.numpy as jnp return jnp else: - return np \ No newline at end of file + return np diff --git a/bilby/core/utils/series.py b/bilby/core/utils/series.py index aaf70ad0f..06ad57d8a 100644 --- a/bilby/core/utils/series.py +++ b/bilby/core/utils/series.py @@ -156,7 +156,7 @@ def _check_legal_sampling_frequency_and_duration(sampling_frequency, duration): sampling_frequency * duration ) ) - + def safe_white_noise(number_of_samples: int, duration: float, *, random_state=None): """ @@ -170,7 +170,7 @@ def safe_white_noise(number_of_samples: int, duration: float, *, random_state=No The duration of the time series. random_state: None, int, np.random.Generator, or jax.random.KeyArray The random state to use for noise generation. - + Returns ======= white_noise: array_like @@ -182,9 +182,9 @@ def safe_white_noise(number_of_samples: int, duration: float, *, random_state=No rng = random.resolve_random_state(random_state) - norm1 = 0.5 * duration**0.5 + norm1 = 0.5 * duration**0.5 re1, im1 = rng.normal(0, norm1, (2, len(frequencies))) - + white_noise = re1 + 1j * im1 # set DC and Nyquist = 0 @@ -192,7 +192,7 @@ def safe_white_noise(number_of_samples: int, duration: float, *, random_state=No # no Nyquist frequency when N=odd if number_of_samples % 2 == 0: white_noise = xpx.at(white_noise, -1).set(0) - + # python: transpose for use with infft white_noise = white_noise.T frequencies = frequencies.T diff --git a/bilby/gw/detector/psd.py b/bilby/gw/detector/psd.py index a5167deb4..b5fa2061a 100644 --- a/bilby/gw/detector/psd.py +++ b/bilby/gw/detector/psd.py @@ -4,7 +4,6 @@ import array_api_extra as xpx import numpy as np -from ...compat.utils import xp_wrap from ...core.utils.calculus import interp1d from ...core import utils from ...core.utils import logger @@ -239,9 +238,9 @@ def _interpolate_power_spectral_density(self): for arbitrary frequency arrays. """ self._power_spectral_density_interpolated = interp1d(self.frequency_array, - self.psd_array, - bounds_error=False, - fill_value=np.inf) + self.psd_array, + bounds_error=False, + fill_value=np.inf) self._update_cache(self.frequency_array) def get_power_spectral_density_array(self, frequency_array): diff --git a/bilby/gw/prior.py b/bilby/gw/prior.py index d0dc00520..9a28df2a1 100644 --- a/bilby/gw/prior.py +++ b/bilby/gw/prior.py @@ -1608,7 +1608,6 @@ def _check_norm(array): norm = np.finfo(array.dtype).eps return array / norm - def _sample(self, size, *, xp=np, random_state=None, **kwargs): """ Overwrites the _sample method of BaseJoint Prior. Picks a pixel value according to their probabilities, then diff --git a/bilby/gw/waveform_generator.py b/bilby/gw/waveform_generator.py index cc00eb395..ce0cb821f 100644 --- a/bilby/gw/waveform_generator.py +++ b/bilby/gw/waveform_generator.py @@ -192,8 +192,12 @@ def _calculate_strain(self, model, model_data_points, transformation_function, t if model is not None: model_strain = self._strain_from_model(xp.asarray(model_data_points), model, parameters) elif transformed_model is not None: - model_strain = self._strain_from_transformed_model(xp.asarray(transformed_model_data_points), transformed_model, - transformation_function, parameters) + model_strain = self._strain_from_transformed_model( + xp.asarray(transformed_model_data_points), + transformed_model, + transformation_function, + parameters, + ) else: raise RuntimeError("No source model given") self._cache['waveform'] = model_strain From a705b6c532087e9d2a8899422de76ac509b75c31 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Mon, 18 May 2026 20:26:06 +0000 Subject: [PATCH 120/140] MAINT: make array api compatibility disabled by default --- .github/workflows/unit-tests.yml | 4 +-- bilby/compat/patches.py | 6 ++-- bilby/compat/types.py | 2 +- bilby/compat/utils.py | 22 ++++++++++++ bilby/core/prior/analytical.py | 1 - bilby/core/utils/random.py | 28 ++++++++++----- bilby/gw/compat/__init__.py | 20 ++++++----- docs/array_api.rst | 60 ++++++++++++++++++-------------- 8 files changed, 93 insertions(+), 50 deletions(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index cd50ff979..6d7cfea48 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -66,10 +66,10 @@ jobs: - name: Run jax-backend unit tests run: | python -m pip install .[jax] - SCIPY_ARRAY_API=1 pytest --array-backend jax --durations 10 + BILBY_ARRAY_API=1 SCIPY_ARRAY_API=1 pytest --array-backend jax --durations 10 - name: Run torch-backend unit tests run: | - SCIPY_ARRAY_API=1 pytest --array-backend torch --durations 10 + BILBY_ARRAY_API=1 SCIPY_ARRAY_API=1 pytest --array-backend torch --durations 10 - name: Run sampler tests run: | pytest test/integration/sampler_run_test.py --durations 10 -v diff --git a/bilby/compat/patches.py b/bilby/compat/patches.py index b5b49f1df..04e3aa667 100644 --- a/bilby/compat/patches.py +++ b/bilby/compat/patches.py @@ -1,6 +1,6 @@ import array_api_compat as aac -from .utils import BackendNotImplementedError +from .utils import BackendNotImplementedError, BILBY_ARRAY_API def multivariate_logpdf(xp, mean, cov): @@ -26,9 +26,9 @@ def multivariate_logpdf(xp, mean, cov): A callable that provides the log probaility density provided an array of points to evaluate at. """ - if aac.is_numpy_namespace(xp): - from scipy.stats import multivariate_normal + from scipy.stats import multivariate_normal + if not BILBY_ARRAY_API or aac.is_numpy_namespace(xp): logpdf = multivariate_normal(mean=mean, cov=cov).logpdf elif aac.is_jax_namespace(xp): from functools import partial diff --git a/bilby/compat/types.py b/bilby/compat/types.py index 48c74c29f..bb83cb478 100644 --- a/bilby/compat/types.py +++ b/bilby/compat/types.py @@ -1,4 +1,4 @@ import numpy as np -Real = float | int +Real = float | int | np.number ArrayLike = np.ndarray | list | tuple diff --git a/bilby/compat/utils.py b/bilby/compat/utils.py index dc89520c8..cc9f9a43b 100644 --- a/bilby/compat/utils.py +++ b/bilby/compat/utils.py @@ -1,4 +1,5 @@ import inspect +import os from collections.abc import Iterable import numpy as np @@ -8,6 +9,21 @@ __all__ = ["array_module", "promote_to_array"] +# environment variable to control whether to use the array API or not +# implemetation taken from +# https://github.com/scipy/scipy/blob/514aeea23e1c90cc4d736ef0ee8b5d762dab461a/scipy/_lib/_array_api_override.py#L27 +BILBY_ARRAY_API = os.getenv("BILBY_ARRAY_API", False) +# FIXME: add BILBY_DEVICE following SCIPY_DEVICE +# FIXME: should we also enforce SCIPY_ARRAY_API for consistency? +SCIPY_ARRAY_API = os.getenv("SCIPY_ARRAY_API", False) +if BILBY_ARRAY_API and not SCIPY_ARRAY_API: + logger.warning( + "BILBY_ARRAY_API is set but SCIPY_ARRAY_API is not set. " + "This may lead to unexpected behavior since some functions in " + "scipy will not be array API compatible. It is recommended to set " + "both environment variables to ensure consistent behavior." + ) + def array_module(arr): """ @@ -33,6 +49,9 @@ def array_module(arr): This is a best-effort function, but will not cover all possible edge cases. + If support for the general array API is not activated via :code:`BILBY_ARRAY_API=1`, + this always returns numpy. + Parameters ========== arr: array-like, tuple, dict, or other type @@ -71,6 +90,9 @@ def array_module(arr): >>> array_module(5) """ + if not BILBY_ARRAY_API: + return np + # FIXME: remove direct import of orng to avoid hard dependency import orng if isinstance(arr, orng.ArrayRNG): diff --git a/bilby/core/prior/analytical.py b/bilby/core/prior/analytical.py index 26af1f07e..19be19997 100644 --- a/bilby/core/prior/analytical.py +++ b/bilby/core/prior/analytical.py @@ -1,7 +1,6 @@ import os import numpy as np -os.environ["SCIPY_ARRAY_API"] = "1" # noqa # flag for scipy backend switching from scipy.special import ( betainc, betaincinv, diff --git a/bilby/core/utils/random.py b/bilby/core/utils/random.py index aa1f998e4..a9ab69f04 100644 --- a/bilby/core/utils/random.py +++ b/bilby/core/utils/random.py @@ -31,6 +31,8 @@ import numpy as np from numpy.random import default_rng, SeedSequence +from ...compat.utils import BILBY_ARRAY_API + def __getattr__(name): if name == "rng": @@ -128,20 +130,30 @@ def resolve_random_state(random_state): np.random.Generator or orng.ArrayRNG The resolved random number generator. """ + + def _resolve_numpy_generator(random_state): + if isinstance(random_state, np.random.Generator): + return random_state + elif random_state is None: + return Generator.rng + elif isinstance(random_state, int): + return np.random.default_rng(random_state) + else: + raise ValueError( + "Invalid random state. Must be None, int, or np.random.Generator." + ) + + if not BILBY_ARRAY_API: + return _resolve_numpy_generator(random_state) + import orng if isinstance(random_state, (np.random.Generator, orng.ArrayRNG)): return random_state - elif random_state is None: - return Generator.rng elif aac.is_jax_array(random_state): rng = orng.ArrayRNG(generator=random_state, backend="jax") return rng - elif isinstance(random_state, int): - return np.random.default_rng(random_state) else: - raise ValueError( - "Invalid random state. Must be None, int, or np.random.Generator." - ) + return _resolve_numpy_generator(random_state) def random_array_module(random_state): @@ -160,7 +172,7 @@ def random_array_module(random_state): numpy or jax.numpy The array module corresponding to the provided random state. """ - if random_state is None: + if random_state is None or not BILBY_ARRAY_API: return np elif aac.is_jax_array(random_state): import jax.numpy as jnp diff --git a/bilby/gw/compat/__init__.py b/bilby/gw/compat/__init__.py index 36f2566c4..245124a71 100644 --- a/bilby/gw/compat/__init__.py +++ b/bilby/gw/compat/__init__.py @@ -1,15 +1,17 @@ -try: - from .jax import n_leap_seconds -except ModuleNotFoundError: - pass - +from ...compat.utils import BILBY_ARRAY_API try: from .cython import gps_time_to_utc except ModuleNotFoundError: pass -try: - from .torch import n_leap_seconds -except ModuleNotFoundError: - pass \ No newline at end of file +if BILBY_ARRAY_API: + try: + from .jax import n_leap_seconds + except ModuleNotFoundError: + pass + + try: + from .torch import n_leap_seconds + except ModuleNotFoundError: + pass \ No newline at end of file diff --git a/docs/array_api.rst b/docs/array_api.rst index 8ce1cc043..10b8d7eed 100644 --- a/docs/array_api.rst +++ b/docs/array_api.rst @@ -15,6 +15,16 @@ Overview The Array API support allows you to use different array libraries with Bilby seamlessly. This can significantly improve performance, especially when using hardware accelerators like GPUs or when you need automatic differentiation capabilities. +To activate array API support you need to set the :code:`BILBY_ARRAY_API` environment variable to +:code:`1` before importing Bilby. +You will also need to set the corresponding :code:`scipy` environment variable (:code:`SCIPY_ARRAY_API`) +for most functionality. +This can be most easily done by setting the environment variable in your shell: + +.. code-block:: bash + + export BILBY_ARRAY_API=1 + export SCIPY_ARRAY_API=1 **Key principle**: In most cases, you don't need to explicitly specify which array backend to use. Bilby automatically detects the array type you're working with and uses the appropriate backend. @@ -61,22 +71,23 @@ to specify the ``xp`` parameter: val_np = np.array([0.5, 1.5, 2.5]) prob_np = prior.prob(val_np) # Returns NumPy array -Sampling with Array Backends (Explicit xp Required) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Sampling with Array Backends (Explicit RNG Required) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -When sampling from priors, you **must** explicitly specify the array backend using the ``xp`` parameter, +When sampling from priors, you **must** explicitly specify the random state for +the operation using the ``random_state`` parameter, as there's no input array to infer the backend from: .. code-block:: python import bilby - import jax.numpy as jnp + import jax prior = bilby.core.prior.Uniform(minimum=0, maximum=10) - samples = prior.sample(size=1000, xp=jnp) # Returns JAX array + samples = prior.sample(size=1000, random_state=jax.random.PRNGKey(42)) # Returns JAX array # Or with NumPy (default) - samples_np = prior.sample(size=1000) # Or explicitly: xp=np + samples_np = prior.sample(size=1000) # Or explicitly: random_state=np.random.default_rng(42) .. note:: @@ -87,11 +98,12 @@ as there's no input array to infer the backend from: Prior Dictionaries ~~~~~~~~~~~~~~~~~~ -Prior dictionaries work the same way - automatic detection for most methods, explicit ``xp`` for sampling: +Prior dictionaries work the same way - automatic detection for most methods, explicit ``random_state`` for sampling: .. code-block:: python import bilby + import jax import jax.numpy as jnp priors = bilby.core.prior.PriorDict({ @@ -99,8 +111,8 @@ Prior dictionaries work the same way - automatic detection for most methods, exp 'y': bilby.core.prior.Uniform(0, 1) }) - # Sampling requires explicit xp - samples = priors.sample(size=1000, xp=jnp) + # Sampling requires explicit random_state + samples = priors.sample(size=1000, random_state=jax.random.PRNGKey(42)) # Evaluation automatically detects backend from input theta = jnp.array([50.0, 0.5]) @@ -326,11 +338,13 @@ The ``@xp_wrap`` decorator: Missing functionality --------------------- -The most significant missing functionality is the lack of a consistent random number generation -interface across different array backends. -Currently, all random calls use :code:`numpy.random` with the seed specified as described in :doc:`rng`. -This means that functionality like prior sampling and generating noise realizations in gravitational-wave -detectors will not be :code:`JIT`-compatible. +__JAX pytrees__: Currently, Bilby types are not defined as JAX pytrees, which means they cannot be +passed as arguments to JIT-compiled functions. +This is a known limitation and we plan to add support for JAX pytrees in future releases. + +__Device management__: Bilby does not currently manage device placement for arrays. +When using JAX or PyTorch, you may need to manually ensure that your arrays are on the +correct device (CPU/GPU). We may revisit this in the future. For Bilby Developers ===================== @@ -363,14 +377,6 @@ All array-processing methods in prior classes follow this pattern: """Method that uses xp for array operations.""" return xp.some_operation(val) * self.is_in_prior_range(val) -**For methods without @xp_wrap (that use xp directly)**: - -.. code-block:: python - - def sample(self, size=None, *, xp=np): - """Method that uses xp but isn't wrapped.""" - return xp.asarray(random.rng.uniform(0, 1, size)) - Key rules: - ``xp`` is always keyword-only (after ``*``) @@ -426,7 +432,7 @@ backends using the ``array_backend`` marker: def test_sample(self): prior = MyPrior() # Sampling requires explicit xp - samples = prior.sample(size=100, xp=self.xp) + samples = prior.sample(size=100, random_state=self.rng) assert aac.get_namespace(samples) == self.xp The array_backend Marker @@ -440,6 +446,7 @@ Without the marker, tests run with the default NumPy backend only. With the mark - Tests are parametrized to run with different backends - The ``xp_class`` fixture is available, providing access to the array module via ``self.xp`` + and the random state via ``self.rng`` - Tests verify that code works correctly regardless of the array backend Running Tests with Different Backends @@ -456,9 +463,9 @@ Use the ``--array-backend`` flag to test with specific backends:: # Test with CuPy backend pytest --array-backend cupy test/core/prior/analytical_test.py -Bilby automatically sets ``SCIPY_ARRAY_API=1`` on import, so you don't need to set this -environment variable manually. The ``--array-backend`` flag controls which backend the -``xp_class`` fixture provides to your tests. +You need to set both `BILBY_ARRAY_API=1` and `SCIPY_ARRAY_API=1` environment variables +to enable array API support in testing +The ``--array-backend`` flag controls which backend the ``xp_class`` fixture provides to your tests. Migration Guide from Previous Versions -------------------------------------- @@ -470,6 +477,7 @@ Key Differences 2. **Decorator added**: Many methods now use ``@xp_wrap`` 3. **Default values differ**: Methods with ``@xp_wrap`` use ``xp=None``, others use ``xp=np`` 4. **Validation added**: Custom priors are checked for ``xp`` support +5. **Explicit random state**: Sampling methods accept a ``random_state`` argument Best Practices for Contributors -------------------------------- From aee982324f8207f19fdabcdd1411a18fce8e234c Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Mon, 18 May 2026 20:27:02 +0000 Subject: [PATCH 121/140] TYPO: typo fixes --- bilby/compat/utils.py | 3 +-- bilby/core/prior/analytical.py | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/bilby/compat/utils.py b/bilby/compat/utils.py index cc9f9a43b..69e462b62 100644 --- a/bilby/compat/utils.py +++ b/bilby/compat/utils.py @@ -9,8 +9,7 @@ __all__ = ["array_module", "promote_to_array"] -# environment variable to control whether to use the array API or not -# implemetation taken from +# environment variable to control whether to use the array API or not implementation taken from # https://github.com/scipy/scipy/blob/514aeea23e1c90cc4d736ef0ee8b5d762dab461a/scipy/_lib/_array_api_override.py#L27 BILBY_ARRAY_API = os.getenv("BILBY_ARRAY_API", False) # FIXME: add BILBY_DEVICE following SCIPY_DEVICE diff --git a/bilby/core/prior/analytical.py b/bilby/core/prior/analytical.py index 19be19997..c3bdf67e3 100644 --- a/bilby/core/prior/analytical.py +++ b/bilby/core/prior/analytical.py @@ -1,5 +1,3 @@ -import os - import numpy as np from scipy.special import ( betainc, From 8089007b1921eeb76aa1d8c9036e30b6a4493f2e Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Mon, 18 May 2026 20:41:29 +0000 Subject: [PATCH 122/140] BUG: fix nyquist frequency for white noise --- bilby/core/utils/series.py | 6 ++---- test/gw/detector/networks_test.py | 4 +++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bilby/core/utils/series.py b/bilby/core/utils/series.py index 06ad57d8a..44f80e053 100644 --- a/bilby/core/utils/series.py +++ b/bilby/core/utils/series.py @@ -189,9 +189,7 @@ def safe_white_noise(number_of_samples: int, duration: float, *, random_state=No # set DC and Nyquist = 0 white_noise = xpx.at(white_noise, 0).set(0) - # no Nyquist frequency when N=odd - if number_of_samples % 2 == 0: - white_noise = xpx.at(white_noise, -1).set(0) + white_noise = xpx.at(white_noise, -1).set(0) # python: transpose for use with infft white_noise = white_noise.T @@ -215,7 +213,7 @@ def create_white_noise(sampling_frequency, duration, random_state=None): array_like: frequency array """ number_of_samples = duration * sampling_frequency - number_of_samples = int(np.round(number_of_samples)) + number_of_samples = int(np.round(number_of_samples)) // 2 + 1 return safe_white_noise(number_of_samples, duration, random_state=random_state) diff --git a/test/gw/detector/networks_test.py b/test/gw/detector/networks_test.py index 942bd882f..061f75571 100644 --- a/test/gw/detector/networks_test.py +++ b/test/gw/detector/networks_test.py @@ -203,7 +203,9 @@ def test_set_strain_data_from_power_spectral_density(self, m): self.ifo_list.set_strain_data_from_power_spectral_densities( sampling_frequency=123, duration=6.2, start_time=3 ) - m.assert_called_with(sampling_frequency=123, duration=6.2, start_time=3) + m.assert_called_with( + sampling_frequency=123, duration=6.2, start_time=3, random_state=None + ) self.assertEqual(len(self.ifo_list), m.call_count) def test_inject_signal_pol_and_wg_none(self): From 60590d6bf5a7265f221b00c94aed1a597c84e1a9 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Mon, 18 May 2026 20:53:48 +0000 Subject: [PATCH 123/140] TEST: fix a test --- test/core/likelihood_test.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/core/likelihood_test.py b/test/core/likelihood_test.py index 05a42d3f5..2d7425bb2 100644 --- a/test/core/likelihood_test.py +++ b/test/core/likelihood_test.py @@ -7,6 +7,7 @@ import array_api_extra as xpx import bilby.core.likelihood +from bilby.compat.utils import array_module from bilby.core.likelihood import ( Likelihood, GaussianLikelihood, @@ -499,7 +500,10 @@ def test_log_likelihood_default(self): exponential_likelihood = ExponentialLikelihood( x=self.x, y=self.y, func=lambda x: self.xp.asarray([4.2]) ) - with mock.patch(f"{self.xp.__name__}.sum") as m: + # xp is not always the same as self.xp as array_api_compat uses its + # own version of numpy + xp = array_module(exponential_likelihood.func(None)) + with mock.patch(f"{xp.__name__}.sum") as m: m.return_value = 3 self.assertEqual(-3, exponential_likelihood.log_likelihood(dict())) From 781a502cd27c0cac977cc3de446d84d3c9b6242a Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Tue, 19 May 2026 13:26:36 +0000 Subject: [PATCH 124/140] TEST: add default device specification --- bilby/compat/utils.py | 21 ++++++++++++++++----- test/conftest.py | 6 ++++++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/bilby/compat/utils.py b/bilby/compat/utils.py index 69e462b62..1d3258bde 100644 --- a/bilby/compat/utils.py +++ b/bilby/compat/utils.py @@ -12,8 +12,6 @@ # environment variable to control whether to use the array API or not implementation taken from # https://github.com/scipy/scipy/blob/514aeea23e1c90cc4d736ef0ee8b5d762dab461a/scipy/_lib/_array_api_override.py#L27 BILBY_ARRAY_API = os.getenv("BILBY_ARRAY_API", False) -# FIXME: add BILBY_DEVICE following SCIPY_DEVICE -# FIXME: should we also enforce SCIPY_ARRAY_API for consistency? SCIPY_ARRAY_API = os.getenv("SCIPY_ARRAY_API", False) if BILBY_ARRAY_API and not SCIPY_ARRAY_API: logger.warning( @@ -22,6 +20,16 @@ "scipy will not be array API compatible. It is recommended to set " "both environment variables to ensure consistent behavior." ) +BILBY_DEVICE = os.getenv("BILBY_DEVICE", None) +SCIPY_DEVICE = os.getenv("SCIPY_DEVICE", None) +if BILBY_DEVICE and SCIPY_DEVICE is None: + logger.warning( + "BILBY_DEVICE is set but SCIPY_DEVICE is not set. " + "This may lead to unexpected behavior since some functions may not" + "set the default device consistently. It is recommended to set " + "both environment variables to ensure consistent behavior." + ) + def array_module(arr): @@ -150,15 +158,18 @@ def promote_to_array(args, xp, skip=None): Notes ===== This function cannot handle manual specification of devices. Arrays - are promoted to the default device of the specified array module. This - may be added in future if there is a need. + are promoted to the default device of the specified array module unless + the :code:`BILBY_DEVICE` environment variable is set, e.g., to ignore a + GPU when using :code:`pytorch`, users can specify :code:`BILBY_DEVICE=cpu`. """ if skip is None: skip = len(args) else: skip = len(args) - skip if not is_numpy_namespace(xp): - args = tuple(xp.array(arg) for arg in args[:skip]) + args[skip:] + args = tuple( + xp.asarray(arg, device=BILBY_DEVICE) for arg in args[:skip] + ) + args[skip:] return args diff --git a/test/conftest.py b/test/conftest.py index b52098a7c..09e4f9cb0 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -3,6 +3,8 @@ import array_api_compat as aac import pytest +from bilby.compat.utils import BILBY_DEVICE + def pytest_addoption(parser): parser.addoption( @@ -37,6 +39,8 @@ def pytest_collection_modifyitems(config, items): def _xp(request): + # The configuration here loosely follows scipy + # https://github.com/scipy/scipy/blob/b167cae18888a34fc43a439e729383b50f4d373e/scipy/conftest.py#L186 backend = request.config.getoption("--array-backend") match backend: case None | "numpy": @@ -45,12 +49,14 @@ def _xp(request): import jax jax.config.update("jax_enable_x64", True) + jax.config.update("jax_default_device", jax.devices(BILBY_DEVICE)[0]) xp = jax.numpy case "torch": import torch # torch starts a lot of threads, so disable this on the first import # to avoid segfaults try: + torch.set_default_device(BILBY_DEVICE) torch.set_num_threads(1) torch.set_num_interop_threads(1) torch.set_default_dtype(torch.float64) From 4a46bccd2d800d1e2e343702d5447ee94e959de2 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Tue, 19 May 2026 13:29:50 +0000 Subject: [PATCH 125/140] FMT: remove extra empty line --- bilby/compat/utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bilby/compat/utils.py b/bilby/compat/utils.py index 1d3258bde..e1a77af00 100644 --- a/bilby/compat/utils.py +++ b/bilby/compat/utils.py @@ -31,7 +31,6 @@ ) - def array_module(arr): """ Infer the array module (namespace) from the input argument. From 01d839ae539b0d743f32a1bbd2b8d8d044cc319e Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Tue, 19 May 2026 15:24:52 +0000 Subject: [PATCH 126/140] BUG: fix failures in tests --- bilby/core/prior/base.py | 3 +++ bilby/core/utils/random.py | 8 ++++++-- bilby/gw/prior.py | 11 ++++++----- bilby/gw/waveform_generator.py | 29 +++++++++++++++++++++-------- test/core/prior/dict_test.py | 22 ++++++++++++++++++---- test/gw/waveform_generator_test.py | 19 +++++++++++++++++++ 6 files changed, 73 insertions(+), 19 deletions(-) diff --git a/bilby/core/prior/base.py b/bilby/core/prior/base.py index 37beeed89..3136f67bf 100644 --- a/bilby/core/prior/base.py +++ b/bilby/core/prior/base.py @@ -155,6 +155,9 @@ def sample(self, size=None, *, random_state=None): from ..utils import random rng = random.resolve_random_state(random_state) + if isinstance(size, (int, np.integer)): + size = (size,) + unit = rng.uniform(low=0, high=1, size=size) self.least_recently_sampled = self.rescale(unit) return self.least_recently_sampled diff --git a/bilby/core/utils/random.py b/bilby/core/utils/random.py index a9ab69f04..99e2c85e5 100644 --- a/bilby/core/utils/random.py +++ b/bilby/core/utils/random.py @@ -169,13 +169,17 @@ def random_array_module(random_state): Returns ------- - numpy or jax.numpy + array module The array module corresponding to the provided random state. """ if random_state is None or not BILBY_ARRAY_API: return np - elif aac.is_jax_array(random_state): + + if aac.is_jax_array(random_state) or getattr(random_state, "backend") == "jax": import jax.numpy as jnp return jnp + elif aac.is_torch_array(random_state) or getattr(random_state, "backend") == "torch": + import torch + return torch else: return np diff --git a/bilby/gw/prior.py b/bilby/gw/prior.py index 9a28df2a1..bd2aab90f 100644 --- a/bilby/gw/prior.py +++ b/bilby/gw/prior.py @@ -1527,7 +1527,7 @@ def _check_imports(): return healpy @xp_wrap - def _rescale(self, samp, *, xp=np, **kwargs): + def _rescale(self, samp, *, xp=None, **kwargs): """ Overwrites the _rescale method of BaseJoint Prior to rescale a single value from the unitcube onto two values (ra, dec) or 3 (ra, dec, dist) if distance is included @@ -1608,7 +1608,7 @@ def _check_norm(array): norm = np.finfo(array.dtype).eps return array / norm - def _sample(self, size, *, xp=np, random_state=None, **kwargs): + def _sample(self, size, *, random_state=None, **kwargs): """ Overwrites the _sample method of BaseJoint Prior. Picks a pixel value according to their probabilities, then uniformly samples ra, and decs that are contained in chosen pixel. If the PriorDist includes distance it then @@ -1628,9 +1628,10 @@ def _sample(self, size, *, xp=np, random_state=None, **kwargs): sample of ra, and dec (and distance if 3D=True) """ rng = random.resolve_random_state(random_state) + xp = random.random_array_module(rng) sample_pix = rng.choice(self.npix, size=size, p=self.prob, replace=True) - sample = np.empty((size, self.num_vars)) + sample = xp.empty((size, self.num_vars)) for samp in range(size): theta, ra = self.hp.pix2ang(self.nside, sample_pix[samp]) dec = 0.5 * np.pi - theta @@ -1638,9 +1639,9 @@ def _sample(self, size, *, xp=np, random_state=None, **kwargs): self.update_distance(sample_pix[samp]) dist = self.draw_distance(sample_pix[samp], random_state=rng) ra_dec = self.draw_from_pixel(ra, dec, sample_pix[samp], random_state=rng) - sample[samp, :] = [ra_dec[0], ra_dec[1], dist] + sample = xpx.at(sample, samp).set(xp.asarray([ra_dec[0], ra_dec[1], dist])) else: - sample[samp, :] = self.draw_from_pixel(ra, dec, sample_pix[samp], random_state=rng) + sample = xpx.at(sample, samp).set(xp.asarray(sample[samp, :])) return xp.asarray(sample.reshape((-1, self.num_vars))) def draw_distance(self, pix, *, random_state=None): diff --git a/bilby/gw/waveform_generator.py b/bilby/gw/waveform_generator.py index ce0cb821f..a78a4d843 100644 --- a/bilby/gw/waveform_generator.py +++ b/bilby/gw/waveform_generator.py @@ -110,7 +110,7 @@ def __repr__(self): .format(self.duration, self.sampling_frequency, self.start_time, fdsm_name, tdsm_name, param_conv_name, self.waveform_arguments) - def frequency_domain_strain(self, parameters=None, *, xp=np): + def frequency_domain_strain(self, parameters=None, *, xp=None): """ Wrapper to source_model. Converts parameters with self.parameter_conversion before handing it off to the source model. @@ -140,7 +140,7 @@ def frequency_domain_strain(self, parameters=None, *, xp=np): transformed_model_data_points=self.time_array, xp=xp) - def time_domain_strain(self, parameters=None, *, xp=np): + def time_domain_strain(self, parameters=None, *, xp=None): """ Wrapper to source_model. Converts parameters with self.parameter_conversion before handing it off to the source model. @@ -153,6 +153,10 @@ def time_domain_strain(self, parameters=None, *, xp=np): Parameters to evaluate the waveform for. If not passed and the generator has been called previously, the last used parameters will be used. + xp: array module, optional + The array module to use when evaluating the source model, e.g., :code:`numpy`. + This can be used to override the :code:`time_array` stored in the generator. + If :code:`None`, the default will be used. Returns ======= @@ -172,7 +176,7 @@ def time_domain_strain(self, parameters=None, *, xp=np): xp=xp) def _calculate_strain(self, model, model_data_points, transformation_function, transformed_model, - transformed_model_data_points, parameters, *, xp=np): + transformed_model_data_points, parameters, *, xp=None): if parameters is None: parameters = self._cache.get('parameters', None) if parameters is None: @@ -190,27 +194,36 @@ def _calculate_strain(self, model, model_data_points, transformation_function, t self._cache['transformed_model'] = transformed_model parameters = self._format_parameters(parameters) if model is not None: - model_strain = self._strain_from_model(xp.asarray(model_data_points), model, parameters) + model_strain = self._strain_from_model(model_data_points, model, parameters, xp=xp) elif transformed_model is not None: model_strain = self._strain_from_transformed_model( - xp.asarray(transformed_model_data_points), + transformed_model_data_points, transformed_model, transformation_function, parameters, + xp=xp, ) else: raise RuntimeError("No source model given") self._cache['waveform'] = model_strain return model_strain - def _strain_from_model(self, model_data_points, model, parameters): + def _strain_from_model(self, model_data_points, model, parameters, *, xp=None): + if xp is not None: + model_data_points = xp.asarray(model_data_points) return model(model_data_points, **parameters) def _strain_from_transformed_model( - self, transformed_model_data_points, transformed_model, transformation_function, parameters + self, + transformed_model_data_points, + transformed_model, + transformation_function, + parameters, + *, + xp=None, ): transformed_model_strain = self._strain_from_model( - transformed_model_data_points, transformed_model, parameters + transformed_model_data_points, transformed_model, parameters, xp=xp ) if aac.is_array_api_obj(transformed_model_strain): diff --git a/test/core/prior/dict_test.py b/test/core/prior/dict_test.py index 73c7cddb3..7cde7e8d0 100644 --- a/test/core/prior/dict_test.py +++ b/test/core/prior/dict_test.py @@ -347,20 +347,34 @@ def conversion_function(parameters): self.assertEqual(N, len(samples2[key])) mock_warning.assert_not_called() - def test_sample(self): + def test_sample_with_random_seed(self): + """ + This test uses the default RNG, so don't specify random_state. + """ size = 7 bilby.core.utils.random.seed(42) samples1 = self.prior_set_from_dict.sample_subset( - keys=self.prior_set_from_dict.keys(), size=size, random_state=self.rng + keys=self.prior_set_from_dict.keys(), size=size ) bilby.core.utils.random.seed(42) - samples2 = self.prior_set_from_dict.sample(size=size, random_state=self.rng) + samples2 = self.prior_set_from_dict.sample(size=size) self.assertEqual(set(samples1.keys()), set(samples2.keys())) for key in samples1: - self.assertTrue(np.array_equal(samples1[key], samples2[key])) + np.testing.assert_array_equal(samples1[key], samples2[key]) self.assertEqual(aac.get_namespace(samples1[key]), self.xp) self.assertEqual(aac.get_namespace(samples2[key]), self.xp) + def test_sample_returns_correct_type(self): + """ + This test uses the default RNG, so don't specify random_state. + """ + size = 7 + samples = self.prior_set_from_dict.sample_subset( + keys=self.prior_set_from_dict.keys(), size=size, random_state=self.rng + ) + for key in samples: + self.assertEqual(aac.get_namespace(samples[key]), self.xp) + def test_prob(self): samples = self.prior_set_from_dict.sample_subset(keys=["mass", "speed"], random_state=self.rng) expected = self.first_prior.prob(samples["mass"]) * self.second_prior.prob( diff --git a/test/gw/waveform_generator_test.py b/test/gw/waveform_generator_test.py index df74a067f..b8bcbb533 100644 --- a/test/gw/waveform_generator_test.py +++ b/test/gw/waveform_generator_test.py @@ -544,6 +544,25 @@ def test_time_domain_source_model_call(self): self.assertEqual(aac.get_namespace(actual["plus"]), self.xp) self.assertEqual(aac.get_namespace(actual["cross"]), self.xp) + def test_time_domain_source_model_call_with_explicit_backend(self): + expected = self.waveform_generator.time_domain_source_model( + self.waveform_generator.time_array, + self.simulation_parameters["amplitude"], + self.simulation_parameters["mu"], + self.simulation_parameters["sigma"], + self.simulation_parameters["ra"], + self.simulation_parameters["dec"], + self.simulation_parameters["geocent_time"], + self.simulation_parameters["psi"], + ) + actual = self.waveform_generator.time_domain_strain( + parameters=self.simulation_parameters, xp=self.xp + ) + self.assertTrue(np.array_equal(expected["plus"], actual["plus"])) + self.assertTrue(np.array_equal(expected["cross"], actual["cross"])) + self.assertEqual(aac.get_namespace(actual["plus"]), self.xp) + self.assertEqual(aac.get_namespace(actual["cross"]), self.xp) + def test_frequency_domain_source_model_call_with_ndarray(self): self.waveform_generator.time_domain_source_model = None self.waveform_generator.frequency_domain_source_model = ( From f4c5440fdfb51a4f61e72e43b36972120bb39df9 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Tue, 19 May 2026 15:36:00 +0000 Subject: [PATCH 127/140] MAINT: move orng into jax dependencies --- jax_requirements.txt | 3 ++- requirements.txt | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/jax_requirements.txt b/jax_requirements.txt index b325586a3..013e0f955 100644 --- a/jax_requirements.txt +++ b/jax_requirements.txt @@ -1,2 +1,3 @@ interpax -jax \ No newline at end of file +jax +orng diff --git a/requirements.txt b/requirements.txt index b9fe7c056..f1a91484d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,4 +14,3 @@ tqdm h5py attrs plum-dispatch -orng From 92c8ebcd7a491bc77f868ce885a7dc79bddda969 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Tue, 19 May 2026 15:51:30 +0000 Subject: [PATCH 128/140] TEST: update waveform generator test --- test/gw/likelihood_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/gw/likelihood_test.py b/test/gw/likelihood_test.py index 128e4bfe0..a2f3c3cf1 100644 --- a/test/gw/likelihood_test.py +++ b/test/gw/likelihood_test.py @@ -40,7 +40,7 @@ def convert_nested_dict(self, data): else: raise ValueError("Input must be an array API object or a dict of such objects.") - def _strain_from_model(self, model_data_points, model, parameters): + def _strain_from_model(self, model_data_points, model, parameters, *, xp=None): model_data_points = np.asarray(model_data_points) return super()._strain_from_model(model_data_points, model, parameters) From 06241cbb71e9515a3ca8bf5e443769c30e7ed5d9 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Tue, 19 May 2026 17:59:38 +0000 Subject: [PATCH 129/140] TEST: improve visibility of prior test failures --- bilby/core/prior/joint.py | 2 +- test/core/prior/prior_test.py | 237 +++++++++++++++++++--------------- 2 files changed, 131 insertions(+), 108 deletions(-) diff --git a/bilby/core/prior/joint.py b/bilby/core/prior/joint.py index e85463c57..34fffc740 100644 --- a/bilby/core/prior/joint.py +++ b/bilby/core/prior/joint.py @@ -850,7 +850,7 @@ def sample(self, size=1, *, random_state=None, **kwargs): # reset samples self.dist.reset_sampled() self.least_recently_sampled = sample - return sample + return sample.squeeze() @xp_wrap def ln_prob(self, val, *, xp=None): diff --git a/test/core/prior/prior_test.py b/test/core/prior/prior_test.py index b03876cef..73fe1552a 100644 --- a/test/core/prior/prior_test.py +++ b/test/core/prior/prior_test.py @@ -285,14 +285,15 @@ def test_minimum_rescaling(self): if isinstance(prior, bilby.gw.prior.HealPixPrior) and aac.is_torch_namespace(self.xp): # HealPix rescaling requires interpolation continue - elif bilby.core.prior.JointPrior in prior.__class__.__mro__: - minimum_sample = prior.rescale(self.xp.asarray(0)) - if prior.dist.filled_rescale(): - self.assertAlmostEqual(np.asarray(minimum_sample[0]), prior.minimum) - self.assertAlmostEqual(np.asarray(minimum_sample[1]), prior.minimum) - else: - minimum_sample = prior.rescale(self.xp.asarray(0)) - self.assertAlmostEqual(np.asarray(minimum_sample), prior.minimum) + with self.subTest(prior=prior): + if bilby.core.prior.JointPrior in prior.__class__.__mro__: + minimum_sample = prior.rescale(self.xp.asarray(0)) + if prior.dist.filled_rescale(): + self.assertAlmostEqual(np.asarray(minimum_sample[0]), prior.minimum) + self.assertAlmostEqual(np.asarray(minimum_sample[1]), prior.minimum) + else: + minimum_sample = prior.rescale(self.xp.asarray(0)) + self.assertAlmostEqual(np.asarray(minimum_sample), prior.minimum) def test_maximum_rescaling(self): """Test the the rescaling works as expected.""" @@ -300,17 +301,18 @@ def test_maximum_rescaling(self): if isinstance(prior, bilby.gw.prior.HealPixPrior) and aac.is_torch_namespace(self.xp): # HealPix rescaling requires interpolation continue - if bilby.core.prior.JointPrior in prior.__class__.__mro__: - maximum_sample = prior.rescale(self.xp.asarray(0)) - if prior.dist.filled_rescale(): - self.assertAlmostEqual(np.asarray(maximum_sample[0]), prior.maximum) - self.assertAlmostEqual(np.asarray(maximum_sample[1]), prior.maximum) - elif isinstance(prior, bilby.gw.prior.AlignedSpin): - maximum_sample = prior.rescale(self.xp.asarray(1)) - self.assertGreater(np.asarray(maximum_sample), 0.997) - else: - maximum_sample = prior.rescale(self.xp.asarray(1)) - self.assertAlmostEqual(np.asarray(maximum_sample), prior.maximum) + with self.subTest(prior=prior): + if bilby.core.prior.JointPrior in prior.__class__.__mro__: + maximum_sample = prior.rescale(self.xp.asarray(0)) + if prior.dist.filled_rescale(): + self.assertAlmostEqual(np.asarray(maximum_sample[0]), prior.maximum) + self.assertAlmostEqual(np.asarray(maximum_sample[1]), prior.maximum) + elif isinstance(prior, bilby.gw.prior.AlignedSpin): + maximum_sample = prior.rescale(self.xp.asarray(1)) + self.assertGreater(np.asarray(maximum_sample), 0.997) + else: + maximum_sample = prior.rescale(self.xp.asarray(1)) + self.assertAlmostEqual(np.asarray(maximum_sample), prior.maximum) def test_many_sample_rescaling(self): """Test the the rescaling works as expected.""" @@ -325,18 +327,20 @@ def test_many_sample_rescaling(self): if bilby.core.prior.JointPrior in prior.__class__.__mro__: if not prior.dist.filled_rescale(): continue - self.assertTrue( - all((many_samples >= prior.minimum) & (many_samples <= prior.maximum)) - ) - self._validate_return_type(many_samples) + with self.subTest(prior=prior): + self.assertTrue( + all((many_samples >= prior.minimum) & (many_samples <= prior.maximum)) + ) + self._validate_return_type(many_samples) def test_least_recently_sampled(self): for prior in self.priors: - least_recently_sampled_expected = prior.sample(random_state=self.rng) - self.assertEqual( - least_recently_sampled_expected, prior.least_recently_sampled - ) - self._validate_return_type(least_recently_sampled_expected) + with self.subTest(prior=prior): + least_recently_sampled_expected = prior.sample(random_state=self.rng) + self.assertEqual( + least_recently_sampled_expected, prior.least_recently_sampled + ) + self._validate_return_type(least_recently_sampled_expected) def test_sampling_single(self): """Test that sampling from the prior always returns values within its domain.""" @@ -344,11 +348,11 @@ def test_sampling_single(self): if isinstance(prior, bilby.core.prior.analytical.SymmetricLogUniform): # SymmetricLogUniform has support down to -maximum continue - single_sample = prior.sample(random_state=self.rng) - self.assertTrue( - (single_sample >= prior.minimum) & (single_sample <= prior.maximum) - ) - self._validate_return_type(single_sample) + with self.subTest(prior=prior): + single_sample = prior.sample(random_state=self.rng) + self.assertGreaterEqual(single_sample, prior.minimum) + self.assertLessEqual(single_sample, prior.maximum) + self._validate_return_type(single_sample) def test_sampling_many(self): """Test that sampling from the prior always returns values within its domain.""" @@ -356,12 +360,11 @@ def test_sampling_many(self): if isinstance(prior, bilby.core.prior.analytical.SymmetricLogUniform): # SymmetricLogUniform has support down to -maximum continue - many_samples = prior.sample(5000, random_state=self.rng) - self.assertTrue( - (all(many_samples >= prior.minimum)) - & (all(many_samples <= prior.maximum)) - ) - self._validate_return_type(many_samples) + with self.subTest(prior=prior): + many_samples = prior.sample(5000, random_state=self.rng) + self.assertGreaterEqual(min(many_samples), prior.minimum) + self.assertLessEqual(max(many_samples), prior.maximum) + self._validate_return_type(many_samples) def test_probability_above_domain(self): """Test that the prior probability is non-negative in domain of validity and zero outside.""" @@ -394,16 +397,19 @@ def test_probability_below_domain(self): def test_least_recently_sampled_2(self): for prior in self.priors: - lrs = prior.sample(random_state=self.rng) - self.assertEqual(lrs, prior.least_recently_sampled) - self._validate_return_type(lrs) + with self.subTest(prior=prior): + lrs = prior.sample(random_state=self.rng) + self.assertEqual(lrs, prior.least_recently_sampled) + self._validate_return_type(lrs) def test_prob_and_ln_prob(self): for prior in self.priors: sample = prior.sample(random_state=self.rng) - if not bilby.core.prior.JointPrior in prior.__class__.__mro__: # noqa + if bilby.core.prior.JointPrior in prior.__class__.__mro__: # noqa # due to the way that the Multivariate Gaussian prior must sequentially call # the prob and ln_prob functions, it must be ignored in this test. + continue + with self.subTest(prior=prior): lnprob = prior.ln_prob(sample) prob = prior.prob(sample) self._validate_return_type(lnprob) @@ -416,7 +422,9 @@ def test_prob_and_ln_prob(self): def test_many_prob_and_many_ln_prob(self): for prior in self.priors: samples = prior.sample(10, random_state=self.rng) - if not bilby.core.prior.JointPrior in prior.__class__.__mro__: # noqa + if bilby.core.prior.JointPrior in prior.__class__.__mro__: # noqa + continue + with self.subTest(prior=prior): ln_probs = prior.ln_prob(samples) probs = prior.prob(samples) for sample, logp, p in zip(samples, ln_probs, probs): @@ -437,27 +445,30 @@ def test_cdf_is_inverse_of_rescaling(self): elif isinstance(prior, bilby.core.prior.StudentT) and "jax" in str(self.xp): # JAX implementation of StudentT prior rescale is not accurate enough continue - elif isinstance(prior, bilby.core.prior.WeightedDiscreteValues): - rescaled = prior.rescale(domain) - cdf_vals = prior.cdf(rescaled) - rescaled_2 = prior.rescale(cdf_vals) - cdf_vals_2 = prior.cdf(rescaled_2) - self.assertTrue(np.array_equal(rescaled, rescaled_2)) - max_difference = max(np.abs(cdf_vals - cdf_vals_2)) - for arr in [rescaled, rescaled_2, cdf_vals, cdf_vals_2]: - self._validate_return_type(arr) - else: - rescaled = prior.rescale(domain) - max_difference = max(np.abs(domain - prior.cdf(rescaled))) - self._validate_return_type(rescaled) - self.assertLess(max_difference, threshold) + with self.subTest(prior=prior): + if isinstance(prior, bilby.core.prior.WeightedDiscreteValues): + rescaled = prior.rescale(domain) + cdf_vals = prior.cdf(rescaled) + rescaled_2 = prior.rescale(cdf_vals) + cdf_vals_2 = prior.cdf(rescaled_2) + self.assertTrue(np.array_equal(rescaled, rescaled_2)) + max_difference = max(np.abs(cdf_vals - cdf_vals_2)) + for arr in [rescaled, rescaled_2, cdf_vals, cdf_vals_2]: + self._validate_return_type(arr) + else: + rescaled = prior.rescale(domain) + max_difference = max(np.abs(domain - prior.cdf(rescaled))) + self._validate_return_type(rescaled) + self.assertLess(max_difference, threshold) def test_cdf_one_above_domain(self): for prior in self.priors: if isinstance(prior, bilby.gw.prior.HealPixPrior) and aac.is_torch_namespace(self.xp): # HealPix rescaling requires interpolation continue - if prior.maximum != np.inf: + if prior.maximum == np.inf: + continue + with self.subTest(prior=prior): outside_domain = self.xp.linspace( prior.maximum + 1, prior.maximum + 1e4, 1000 ) @@ -476,7 +487,9 @@ def test_cdf_zero_below_domain(self): and prior.maximum == np.inf ): continue - if prior.minimum != -np.inf: + if prior.minimum == -np.inf: + continue + with self.subTest(prior=prior): outside_domain = self.xp.linspace( prior.minimum - 1e4, prior.minimum - 1, 1000 ) @@ -486,7 +499,8 @@ def test_cdf_float_with_float_input(self): for prior in self.priors: if bilby.core.prior.JointPrior in prior.__class__.__mro__: continue - self.assertIsInstance(prior.cdf(prior.sample()), float) + with self.subTest(prior=prior): + self.assertIsInstance(prior.cdf(prior.sample()), float) def test_log_normal_fail(self): with self.assertRaises(ValueError): @@ -624,21 +638,20 @@ def test_fermidirac_fail(self): def test_probability_in_domain(self): """Test that the prior probability is non-negative in domain of validity and zero outside.""" for prior in self.priors: - if prior.minimum == -np.inf: - minimum = -1e5 - else: - minimum = prior.minimum - if prior.maximum == np.inf: - maximum = 1e5 - else: - maximum = prior.maximum - domain = self.xp.linspace(minimum, maximum, 1000) - print(prior) - prob = prior.prob(domain) - print(min(prob)) - self._validate_return_type(prob) - prob = np.asarray(prob) - self.assertTrue(all(prob >= 0)) + with self.subTest(prior=prior): + if prior.minimum == -np.inf: + minimum = -1e5 + else: + minimum = prior.minimum + if prior.maximum == np.inf: + maximum = 1e5 + else: + maximum = prior.maximum + domain = self.xp.linspace(minimum, maximum, 1000) + prob = prior.prob(domain) + self._validate_return_type(prob) + prob = np.asarray(prob) + self.assertTrue(all(prob >= 0)) def test_probability_surrounding_domain(self): """Test that the prior probability is non-negative in domain of validity and zero outside.""" @@ -660,8 +673,9 @@ def test_probability_surrounding_domain(self): if bilby.core.prior.JointPrior in prior.__class__.__mro__: if not prior.dist.filled_request(): continue - self.assertTrue(all(prior.prob(surround_domain[indomain]) >= 0)) - self.assertTrue(all(prior.prob(surround_domain[outdomain]) == 0)) + with self.subTest(prior=prior): + self.assertTrue(all(prior.prob(surround_domain[indomain]) >= 0)) + self.assertTrue(all(prior.prob(surround_domain[outdomain]) == 0)) def test_normalized(self): """ @@ -704,15 +718,18 @@ def test_normalized(self): domain = np.linspace(prior.minimum, prior.maximum, 10000) elif isinstance(prior, bilby.core.prior.WeightedDiscreteValues): domain = prior.values - probs = prior.prob(self.xp.asarray(domain)) - self._validate_return_type(probs) - self.assertTrue(np.sum(np.asarray(probs)) == 1) continue else: domain = np.linspace(prior.minimum, prior.maximum, 1000) - probs = prior.prob(self.xp.asarray(domain)) - self.assertAlmostEqual(trapezoid(np.array(probs), domain), 1, 3) - self._validate_return_type(probs) + with self.subTest(prior=prior): + if isinstance(prior, bilby.core.prior.WeightedDiscreteValues): + probs = prior.prob(self.xp.asarray(domain)) + self._validate_return_type(probs) + self.assertTrue(np.sum(np.asarray(probs)) == 1) + else: + probs = prior.prob(self.xp.asarray(domain)) + self.assertAlmostEqual(trapezoid(np.array(probs), domain), 1, 3) + self._validate_return_type(probs) def test_accuracy(self): """Test that each of the priors' functions is calculated accurately, as compared to scipy's calculations""" @@ -807,30 +824,33 @@ def test_accuracy(self): bilby.core.prior.WeightedDiscreteValues, ) if isinstance(prior, (testTuple)): - np.testing.assert_almost_equal(prior.prob(self.xp.asarray(domain)), scipy_prob) - np.testing.assert_almost_equal(prior.ln_prob(self.xp.asarray(domain)), scipy_lnprob) - np.testing.assert_almost_equal(prior.cdf(self.xp.asarray(domain)), scipy_cdf) - if isinstance(prior, bilby.core.prior.StudentT) and "jax" in str(self.xp): - # JAX implementation of StudentT prior rescale is not accurate enough - continue - np.testing.assert_almost_equal( - prior.rescale(self.xp.asarray(rescale_domain)), scipy_rescale - ) + with self.subTest(prior=prior): + np.testing.assert_almost_equal(prior.prob(self.xp.asarray(domain)), scipy_prob) + np.testing.assert_almost_equal(prior.ln_prob(self.xp.asarray(domain)), scipy_lnprob) + np.testing.assert_almost_equal(prior.cdf(self.xp.asarray(domain)), scipy_cdf) + if isinstance(prior, bilby.core.prior.StudentT) and "jax" in str(self.xp): + # JAX implementation of StudentT prior rescale is not accurate enough + continue + np.testing.assert_almost_equal( + prior.rescale(self.xp.asarray(rescale_domain)), scipy_rescale + ) def test_unit_setting(self): for prior in self.priors: - if isinstance(prior, bilby.gw.prior.Cosmological): - self.assertEqual(None, prior.unit) - else: - self.assertEqual("unit", prior.unit) + with self.subTest(prior=prior): + if isinstance(prior, bilby.gw.prior.Cosmological): + self.assertEqual(None, prior.unit) + else: + self.assertEqual("unit", prior.unit) def test_eq_different_classes(self): for i in range(len(self.priors)): for j in range(len(self.priors)): - if i == j: - self.assertEqual(self.priors[i], self.priors[j]) - else: - self.assertNotEqual(self.priors[i], self.priors[j]) + with self.subTest(i=self.priors[i], j=self.priors[j]): + if i == j: + self.assertEqual(self.priors[i], self.priors[j]) + else: + self.assertNotEqual(self.priors[i], self.priors[j]) def test_eq_other_condition(self): prior_1 = bilby.core.prior.PowerLaw( @@ -874,8 +894,9 @@ def test_repr(self): else: repr_prior_string = "bilby.core.prior." + repr(prior) - repr_prior = eval(repr_prior_string, None, dict(inf=np.inf)) - self.assertEqual(prior, repr_prior) + with self.subTest(prior=prior): + repr_prior = eval(repr_prior_string, None, dict(inf=np.inf)) + self.assertEqual(prior, repr_prior) def test_set_maximum_setting(self): for prior in self.priors: @@ -899,8 +920,9 @@ def test_set_maximum_setting(self): ), ): continue - prior.maximum = (prior.maximum + prior.minimum) / 2 - self.assertTrue(max(prior.sample(10000, random_state=self.rng)) < prior.maximum) + with self.subTest(prior=prior): + prior.maximum = (prior.maximum + prior.minimum) / 2 + self.assertTrue(max(prior.sample(10000, random_state=self.rng)) < prior.maximum) def test_set_minimum_setting(self): for prior in self.priors: @@ -925,8 +947,9 @@ def test_set_minimum_setting(self): ), ): continue - prior.minimum = (prior.maximum + prior.minimum) / 2 - self.assertTrue(min(prior.sample(10000, random_state=self.rng)) > prior.minimum) + with self.subTest(prior=prior): + prior.minimum = (prior.maximum + prior.minimum) / 2 + self.assertTrue(min(prior.sample(10000, random_state=self.rng)) > prior.minimum) if __name__ == "__main__": From a41bc0d26c7c521bce2b49628906f70eaba2cce9 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Tue, 19 May 2026 18:43:23 +0000 Subject: [PATCH 130/140] DOC: update documentation --- docs/array_api.rst | 30 ++++++++++++------------------ docs/rng.rst | 4 ++++ 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/docs/array_api.rst b/docs/array_api.rst index 10b8d7eed..c6e6190c0 100644 --- a/docs/array_api.rst +++ b/docs/array_api.rst @@ -44,7 +44,7 @@ Bilby is currently tested with the following array backends: While :code:`Bilby` should be compatible with other Array API compliant libraries, these are not currently tested or officially supported. If you notice any issues when using other backends, -please report them on the `Bilby GitHub repository `. +please report them on the `Bilby GitHub repository `__. Using Different Array Backends ------------------------------- @@ -89,12 +89,6 @@ as there's no input array to infer the backend from: # Or with NumPy (default) samples_np = prior.sample(size=1000) # Or explicitly: random_state=np.random.default_rng(42) -.. note:: - - Currently, prior sampling is done by first generating uniform samples in [0, 1] - using :code:`NumPy`, then converting to the desired backend. - In future releases, this may be altered to generate samples directly in the specified backend. - Prior Dictionaries ~~~~~~~~~~~~~~~~~~ @@ -194,7 +188,7 @@ and the analysis is then performed using the JIT-compiled likelihood. duration=4, sampling_frequency=2048, frequency_domain_source_model=bilby.gw.source.lal_binary_black_hole, - waveform_arguments={"approximant": "IMRPhenomXODE"} + waveform_arguments={"approximant": "IMRPhenomXPHM"} ) ifos.inject_signal(parameters=injection_parameters, waveform_generator=injection_wfg) @@ -306,8 +300,8 @@ The ``xp`` parameter should: **Note**: Users of your custom prior won't need to pass ``xp`` explicitly for evaluation methods - it will be automatically inferred from their input arrays. They only need to specify ``xp`` when sampling. -Using the :code:`xp_wrap`` Decorator -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Using the :code:`xp_wrap` Decorator +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ For methods that perform array operations, use the ``@xp_wrap`` decorator: @@ -338,11 +332,11 @@ The ``@xp_wrap`` decorator: Missing functionality --------------------- -__JAX pytrees__: Currently, Bilby types are not defined as JAX pytrees, which means they cannot be +**JAX pytrees**: Currently, Bilby types are not defined as JAX pytrees, which means they cannot be passed as arguments to JIT-compiled functions. This is a known limitation and we plan to add support for JAX pytrees in future releases. -__Device management__: Bilby does not currently manage device placement for arrays. +**Device management**: Bilby does not currently manage device placement for arrays. When using JAX or PyTorch, you may need to manually ensure that your arrays are on the correct device (CPU/GPU). We may revisit this in the future. @@ -356,7 +350,7 @@ The Array API support in Bilby is built around several key components: 1. **The xp parameter**: A keyword-only parameter added to prior methods 2. **The @xp_wrap decorator**: Handles array module selection and injection -4. **Compatibility utilities**: Helper functions for array module detection +3. **Compatibility utilities**: Helper functions for array module detection Core Changes to Prior Base Class --------------------------------- @@ -384,8 +378,8 @@ Key rules: - Methods without ``@xp_wrap`` that use ``xp`` use ``xp=np`` as default - Methods that don't use ``xp`` have ``xp=None`` as default -The :code:`@xp_wrap`` Decorator -------------------------------- +The :code:`@xp_wrap` Decorator +------------------------------ Located in ``bilby/compat/utils.py``, this decorator: @@ -463,7 +457,7 @@ Use the ``--array-backend`` flag to test with specific backends:: # Test with CuPy backend pytest --array-backend cupy test/core/prior/analytical_test.py -You need to set both `BILBY_ARRAY_API=1` and `SCIPY_ARRAY_API=1` environment variables +You need to set both ``BILBY_ARRAY_API=1`` and ``SCIPY_ARRAY_API=1`` environment variables to enable array API support in testing The ``--array-backend`` flag controls which backend the ``xp_class`` fixture provides to your tests. @@ -492,8 +486,8 @@ When adding or modifying prior methods: 6. **Document xp parameter**: Note it in docstrings, but emphasize it's usually auto-detected 7. **Use array module functions**: Use ``xp.function()`` not ``np.function()`` in wrapped methods -Handling Array Updates with :code:`array_api_extra.at`` -------------------------------------------------------- +Handling Array Updates with :code:`array_api_extra.at` +------------------------------------------------------ One key difference between array backends is how they handle array updates. NumPy allows in-place modification of array slices, diff --git a/docs/rng.rst b/docs/rng.rst index 709657a5e..9feefc8a2 100644 --- a/docs/rng.rst +++ b/docs/rng.rst @@ -27,6 +27,10 @@ The random number generation can be seeded using the >>> from bilby.core.utils import random >>> random.seed(1234) +For more fine-grained control, every function/method that relies on random number +generation supports a :code:`random_state` argument that can be used to specify +the random number generator to use for that function/method. + ---------------- Seeding samplers ---------------- From ab67301ad0d66e770ecc618b0c15a2b650b5a89c Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Tue, 19 May 2026 18:55:45 +0000 Subject: [PATCH 131/140] TEST: fix broken tests --- bilby/core/prior/conditional.py | 5 +++++ test/core/prior/dict_test.py | 2 -- test/gw/prior_test.py | 14 ++++++++------ 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/bilby/core/prior/conditional.py b/bilby/core/prior/conditional.py index c68f471dd..b49aecc37 100644 --- a/bilby/core/prior/conditional.py +++ b/bilby/core/prior/conditional.py @@ -1,3 +1,5 @@ +import numpy as np + from .base import Prior, PriorException from .interpolated import Interped from .analytical import DeltaFunction, PowerLaw, Uniform, LogUniform, \ @@ -78,6 +80,9 @@ def sample(self, size=None, *, random_state=None, **required_variables): from ..utils import random rng = random.resolve_random_state(random_state) + if isinstance(size, int | np.integer): + size = (size,) + self.least_recently_sampled = self.rescale( rng.uniform(0, 1, size), **required_variables ) diff --git a/test/core/prior/dict_test.py b/test/core/prior/dict_test.py index 7cde7e8d0..cdd996f19 100644 --- a/test/core/prior/dict_test.py +++ b/test/core/prior/dict_test.py @@ -361,8 +361,6 @@ def test_sample_with_random_seed(self): self.assertEqual(set(samples1.keys()), set(samples2.keys())) for key in samples1: np.testing.assert_array_equal(samples1[key], samples2[key]) - self.assertEqual(aac.get_namespace(samples1[key]), self.xp) - self.assertEqual(aac.get_namespace(samples2[key]), self.xp) def test_sample_returns_correct_type(self): """ diff --git a/test/gw/prior_test.py b/test/gw/prior_test.py index 7596e5099..1c6916a47 100644 --- a/test/gw/prior_test.py +++ b/test/gw/prior_test.py @@ -223,8 +223,6 @@ def test_pickle_prior(self): self.assertEqual(priors, priors_loaded) -@pytest.mark.array_backend -@pytest.mark.usefixtures("xp_class") class TestPriorConversion(unittest.TestCase): def test_bilby_to_lalinference(self): mass_1 = [1, 20] @@ -259,7 +257,7 @@ def test_bilby_to_lalinference(self): ) nsamples = 5000 - bilby_samples = bilby_prior.sample(nsamples, random_state=self.rng) + bilby_samples = bilby_prior.sample(nsamples) bilby_samples, _ = conversion.convert_to_lal_binary_black_hole_parameters( bilby_samples ) @@ -268,7 +266,7 @@ def test_bilby_to_lalinference(self): # Quicker way to generate LA prior samples (rather than specifying Constraint) lalinf_samples = [] while len(lalinf_samples) < nsamples: - s = lalinf_prior.sample(random_state=self.rng) + s = lalinf_prior.sample() if s["mass_1"] < s["mass_2"]: s["mass_1"], s["mass_2"] = s["mass_2"], s["mass_1"] if s["mass_2"] / s["mass_1"] > 0.125: @@ -301,13 +299,17 @@ def test_bilby_to_lalinference(self): plt.show() # Check that the non-reweighted posteriors fail a KS test - ks = ks_2samp(bilby_samples["mass_ratio"], lalinf_samples["mass_ratio"]) + ks = ks_2samp( + bilby_samples["mass_ratio"].values, + lalinf_samples["mass_ratio"].values, + ) print("Non-reweighted KS test = ", ks) self.assertFalse(ks.pvalue > 0.05) # Check that the non-reweighted posteriors pass a KS test ks = ks_2samp( - result_converted.posterior["mass_ratio"], lalinf_samples["mass_ratio"] + result_converted.posterior["mass_ratio"].values, + lalinf_samples["mass_ratio"].values, ) print("Reweighted KS test = ", ks) self.assertTrue(ks.pvalue > 0.001) From 487a074cd2a39dafef6f0620cddc95a64d9925e6 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Tue, 19 May 2026 19:01:36 +0000 Subject: [PATCH 132/140] FMT: fix precommits --- test/gw/prior_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/gw/prior_test.py b/test/gw/prior_test.py index 1c6916a47..538b07904 100644 --- a/test/gw/prior_test.py +++ b/test/gw/prior_test.py @@ -300,7 +300,7 @@ def test_bilby_to_lalinference(self): # Check that the non-reweighted posteriors fail a KS test ks = ks_2samp( - bilby_samples["mass_ratio"].values, + bilby_samples["mass_ratio"].values, lalinf_samples["mass_ratio"].values, ) print("Non-reweighted KS test = ", ks) From 89d80d1a1f1878634c517498b1f97158abd82deb Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Wed, 20 May 2026 14:23:31 +0000 Subject: [PATCH 133/140] CI: split array backend tests into a separate job --- .github/workflows/unit-tests.yml | 62 ++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 6d7cfea48..2121956a6 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -63,13 +63,6 @@ jobs: - name: Run unit tests run: | python -m pytest --cov=bilby --cov-branch --durations 10 -ra --color yes --cov-report=xml --junitxml=pytest.xml - - name: Run jax-backend unit tests - run: | - python -m pip install .[jax] - BILBY_ARRAY_API=1 SCIPY_ARRAY_API=1 pytest --array-backend jax --durations 10 - - name: Run torch-backend unit tests - run: | - BILBY_ARRAY_API=1 SCIPY_ARRAY_API=1 pytest --array-backend torch --durations 10 - name: Run sampler tests run: | pytest test/integration/sampler_run_test.py --durations 10 -v @@ -88,3 +81,58 @@ jobs: with: name: pytest-${{ matrix.python.short-version }} path: pytest.xml + + array-backend: + + name: ${{ matrix.python.name }} array backend (${{ matrix.backend.name }}) + runs-on: ubuntu-latest + container: ghcr.io/bilby-dev/bilby-python${{ matrix.python.short-version }}:latest + strategy: + fail-fast: false + matrix: + python: + - name: Python 3.13 + version: 3.13 + short-version: 313 + backend: + - name: numpy: + install-args: . + - name: jax + install-args: .[jax] + - name: torch + install-args: . + env: + BILBY_ARRAY_API: 1 + SCIPY_ARRAY_API: 1 + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + fetch-tags: true + - name: Install package + run: | + # activate env so that conda list shows the correct environment + apt-get update + apt-get install -y gnupg curl + source $CONDA_PATH/bin/activate python${{ matrix.python.short-version }} + conda install -c conda-forge -y liblal!=7.7.0 python-lal!=7.7.0 + python -m pip install ${{ matrix.backend.install-args }} + conda list --show-channel-urls + shell: bash + - name: Run array-backend unit tests + run: | + pytest --array-backend ${{ matrix.backend.name }} --durations 10 --junitxml=pytest-array.xml + - name: Publish coverage to Codecov + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: coverage.xml + flags: python${{ matrix.backend.name }} + slug: bilby-dev/bilby + - name: Upload array-backend test results + if: always() + uses: actions/upload-artifact@v4 + with: + name: pytest-array-${{ matrix.python.short-version }}-${{ matrix.backend.name }} + path: pytest-array.xml From 6143c266dcd99db7f5ff81d4a5f90ba2fb1e6327 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Wed, 20 May 2026 14:26:59 +0000 Subject: [PATCH 134/140] TYPO: fix typo in ci --- .github/workflows/unit-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 2121956a6..371d93311 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -95,7 +95,7 @@ jobs: version: 3.13 short-version: 313 backend: - - name: numpy: + - name: numpy install-args: . - name: jax install-args: .[jax] From b1518764a022a5694a200731cae7a4aa50278aa6 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Wed, 20 May 2026 15:14:30 +0000 Subject: [PATCH 135/140] BUG: fix up torch tests --- .github/workflows/unit-tests.yml | 1 + bilby/compat/patches.py | 59 +++++++++++++++++++++++++++++++- bilby/core/prior/base.py | 3 +- bilby/core/utils/calculus.py | 36 ++++++++++--------- bilby/core/utils/random.py | 3 ++ test/conftest.py | 11 ++++-- test/core/prior/prior_test.py | 10 ++++-- 7 files changed, 98 insertions(+), 25 deletions(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 371d93311..c0e3becc2 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -118,6 +118,7 @@ jobs: source $CONDA_PATH/bin/activate python${{ matrix.python.short-version }} conda install -c conda-forge -y liblal!=7.7.0 python-lal!=7.7.0 python -m pip install ${{ matrix.backend.install-args }} + python -m pip install orng conda list --show-channel-urls shell: bash - name: Run array-backend unit tests diff --git a/bilby/compat/patches.py b/bilby/compat/patches.py index 04e3aa667..59962160d 100644 --- a/bilby/compat/patches.py +++ b/bilby/compat/patches.py @@ -1,6 +1,6 @@ import array_api_compat as aac -from .utils import BackendNotImplementedError, BILBY_ARRAY_API +from .utils import xp_wrap, BackendNotImplementedError, BILBY_ARRAY_API def multivariate_logpdf(xp, mean, cov): @@ -45,3 +45,60 @@ def multivariate_logpdf(xp, mean, cov): f"Unable to import multivariate_logpdf for {xp}" ) return logpdf + + +@xp_wrap +def interp(x, xs, fs, /, left=None, right=None, period=None, *, xp=None): + """ + A simple implementation of numpy-style linear interpolation + + The logic is copied from + https://github.com/pytorch/pytorch/issues/50334#issuecomment-1000917964 + + Parameters + ========== + x: array-like + The values to evaluate the interpolant at. + xs: array-like + The x-values for setting up the interpolant. + ys: array-like + The values of the function for setting up the interpolant. + left: float + The value to use for x < xs[0]. Default is fs[0] + right: float + The value to use for x > xs[-1]. Default is fs[-1]. + period: float + The period of the interpolant. + Parameters left and right are ignored if period is specified. + + Notes + ===== + To avoid overlap with the ``xp`` variable, the second and third variable + names from differ from numpy. + These arguments are enforced to be positional only. + """ + if not BILBY_ARRAY_API or hasattr(xp, "interp"): + return xp.interp(x, xs, fs, left=left, right=right, period=period) + + if period is not None: + x = x % period + if left is None: + left = fs[0] + if right is None: + right = fs[-1] + + x = xp.atleast_1d(x) + + m = (fs[1:] - fs[:-1]) / (xs[1:] - xs[:-1]) + b = fs[:-1] - (m * xs[:-1]) + + indices = xp.sum(xp.ge(x[:, None], xs[None, :]), axis=1) - 1 + indices = xp.clip(indices, 0, len(m) - 1) + + ret = m[indices] * x + b[indices] + + if period is None: + ret = xp.where(x < xs[0], xp.asarray(left), ret) + ret = xp.where(x > xs[-1], xp.asarray(right), ret) + + return ret.squeeze() diff --git a/bilby/core/prior/base.py b/bilby/core/prior/base.py index 3136f67bf..dc0c9f14e 100644 --- a/bilby/core/prior/base.py +++ b/bilby/core/prior/base.py @@ -15,6 +15,7 @@ logger, get_dict_with_properties, ) +from ...compat.patches import interp from ...compat.utils import xp_wrap @@ -205,7 +206,7 @@ def cdf(self, val, *, xp=None): x = xp.linspace(self.minimum, self.maximum, 1000) pdf = self.prob(x, xp=xp) cdf = cumulative_trapezoid(pdf, x, initial=0) - output = xp.interp(val, x, cdf / cdf[-1], left=0, right=1) + output = interp(val, x, cdf / cdf[-1], left=0, right=1) return output[()] @xp_wrap diff --git a/bilby/core/utils/calculus.py b/bilby/core/utils/calculus.py index 8794f00a4..00dbcb1d9 100644 --- a/bilby/core/utils/calculus.py +++ b/bilby/core/utils/calculus.py @@ -6,7 +6,7 @@ from scipy.special import logsumexp from .log import logger -from ...compat.utils import array_module, xp_wrap +from ...compat.utils import array_module, xp_wrap, BILBY_ARRAY_API def derivatives( @@ -193,27 +193,29 @@ def logtrapzexp(lnf, dx, *, xp=np): class interp1d(_interp1d): def __call__(self, x): - from array_api_compat import is_numpy_namespace + if not BILBY_ARRAY_API: + return super().__call__(x) + + import array_api_compat as aac xp = array_module(x) - if is_numpy_namespace(xp): + if aac.is_numpy_namespace(xp): return super().__call__(x) else: - return self._call_alt(x, xp=xp) - - def _call_alt(self, x, *, xp=np): - if isinstance(self.fill_value, tuple): - left, right = self.fill_value - else: - left = right = self.fill_value - return xp.interp( - x, - xp.asarray(self.x), - xp.asarray(self.y), - left=left, - right=right, - ) + from ...compat.patches import interp + if isinstance(self.fill_value, tuple): + left, right = self.fill_value + else: + left = right = self.fill_value + + return interp( + x, + xp.asarray(self.x), + xp.asarray(self.y), + left=left, + right=right, + ) class BoundedRectBivariateSpline(RectBivariateSpline): diff --git a/bilby/core/utils/random.py b/bilby/core/utils/random.py index 99e2c85e5..61c791b1f 100644 --- a/bilby/core/utils/random.py +++ b/bilby/core/utils/random.py @@ -152,6 +152,9 @@ def _resolve_numpy_generator(random_state): elif aac.is_jax_array(random_state): rng = orng.ArrayRNG(generator=random_state, backend="jax") return rng + elif aac.is_torch_array(random_state): + rng = orng.ArrayRNG(seed=int(random_state), backend="torch") + return rng else: return _resolve_numpy_generator(random_state) diff --git a/test/conftest.py b/test/conftest.py index 09e4f9cb0..733601d0e 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -73,14 +73,19 @@ def _xp(request): def _rng(xp): + import array_api_compat as aac from bilby.core.utils.random import resolve_random_state - if xp.__name__ == "numpy": + if aac.is_numpy_namespace(xp): return resolve_random_state(12345) - elif xp.__name__ == "jax.numpy": + elif aac.is_jax_namespace(xp): import jax.random - return resolve_random_state(jax.random.PRNGKey(12345)) + elif aac.is_torch_namespace(xp): + import torch + return resolve_random_state(torch.Tensor([12345])) + else: + raise ValueError(f"Unknown array namespace {xp} for RNG") @pytest.fixture diff --git a/test/core/prior/prior_test.py b/test/core/prior/prior_test.py index 73fe1552a..300310616 100644 --- a/test/core/prior/prior_test.py +++ b/test/core/prior/prior_test.py @@ -427,11 +427,15 @@ def test_many_prob_and_many_ln_prob(self): with self.subTest(prior=prior): ln_probs = prior.ln_prob(samples) probs = prior.prob(samples) - for sample, logp, p in zip(samples, ln_probs, probs): - self.assertAlmostEqual(prior.ln_prob(sample), logp) - self.assertAlmostEqual(prior.prob(sample), p) self._validate_return_type(ln_probs) self._validate_return_type(probs) + ln_probs = np.asarray(ln_probs) + probs = np.asarray(probs) + for sample, logp, p in zip(samples, ln_probs, probs): + new_lnprob = np.asarray(prior.ln_prob(sample)) + new_prob = np.asarray(prior.prob(sample)) + self.assertAlmostEqual(new_lnprob, logp, 6) + self.assertAlmostEqual(new_prob, p, 6) def test_cdf_is_inverse_of_rescaling(self): domain = self.xp.linspace(0, 1, 100) From 11b94b6fcab6599cbab26afc1447bc7f49b9800a Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Wed, 20 May 2026 16:58:59 +0000 Subject: [PATCH 136/140] TEST: fix testing with array api support --- bilby/compat/patches.py | 2 +- bilby/core/utils/calculus.py | 1 + bilby/core/utils/random.py | 5 +++-- bilby/gw/detector/psd.py | 5 ++++- bilby/gw/likelihood/multiband.py | 2 +- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/bilby/compat/patches.py b/bilby/compat/patches.py index 59962160d..c6c9f544b 100644 --- a/bilby/compat/patches.py +++ b/bilby/compat/patches.py @@ -86,7 +86,7 @@ def interp(x, xs, fs, /, left=None, right=None, period=None, *, xp=None): left = fs[0] if right is None: right = fs[-1] - + x = xp.atleast_1d(x) m = (fs[1:] - fs[:-1]) / (xs[1:] - xs[:-1]) diff --git a/bilby/core/utils/calculus.py b/bilby/core/utils/calculus.py index 00dbcb1d9..490195930 100644 --- a/bilby/core/utils/calculus.py +++ b/bilby/core/utils/calculus.py @@ -217,6 +217,7 @@ def __call__(self, x): right=right, ) + class BoundedRectBivariateSpline(RectBivariateSpline): def __init__(self, x, y, z, bbox=[None] * 4, kx=3, ky=3, s=0, fill_value=None): diff --git a/bilby/core/utils/random.py b/bilby/core/utils/random.py index 61c791b1f..74efd9198 100644 --- a/bilby/core/utils/random.py +++ b/bilby/core/utils/random.py @@ -177,8 +177,9 @@ def random_array_module(random_state): """ if random_state is None or not BILBY_ARRAY_API: return np - - if aac.is_jax_array(random_state) or getattr(random_state, "backend") == "jax": + elif isinstance(random_state, np.random.Generator): + return np + elif aac.is_jax_array(random_state) or getattr(random_state, "backend") == "jax": import jax.numpy as jnp return jnp elif aac.is_torch_array(random_state) or getattr(random_state, "backend") == "torch": diff --git a/bilby/gw/detector/psd.py b/bilby/gw/detector/psd.py index b5fa2061a..78e5a472b 100644 --- a/bilby/gw/detector/psd.py +++ b/bilby/gw/detector/psd.py @@ -368,7 +368,10 @@ def get_noise_realisation(self, number_of_samples, duration, *, random_state=Non with np.errstate(invalid="ignore"): frequency_domain_strain = self._power_spectral_density_interpolated(frequencies) ** 0.5 * white_noise xp = aac.array_namespace(frequency_domain_strain) - out_of_bounds = (frequencies < xp.min(self.frequency_array)) | (frequencies > xp.max(self.frequency_array)) + out_of_bounds = ( + (frequencies < self.frequency_array.min()) + | (frequencies > self.frequency_array.max()) + ) frequency_domain_strain = xpx.at(frequency_domain_strain, out_of_bounds).set(0j) return xp.nan_to_num(frequency_domain_strain), xp.asarray(frequencies) diff --git a/bilby/gw/likelihood/multiband.py b/bilby/gw/likelihood/multiband.py index 264d9d8b9..0fe5d8f47 100644 --- a/bilby/gw/likelihood/multiband.py +++ b/bilby/gw/likelihood/multiband.py @@ -757,7 +757,7 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array xp = array_module(strain) - d_inner_h = xp.conj(xp.dot(strain, self.linear_coeffs[interferometer.name])) + d_inner_h = xp.conj(xp.dot(strain, xp.asarray(self.linear_coeffs[interferometer.name]))) if self.linear_interpolation: optimal_snr_squared = xp.vdot( From 3ec7c7f92bccea7ebe877357f8e13b962e7d0988 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Wed, 20 May 2026 17:13:21 +0000 Subject: [PATCH 137/140] BUG: fix typos in healpix priors --- bilby/gw/prior.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/bilby/gw/prior.py b/bilby/gw/prior.py index bd2aab90f..9122a0c79 100644 --- a/bilby/gw/prior.py +++ b/bilby/gw/prior.py @@ -1638,10 +1638,11 @@ def _sample(self, size, *, random_state=None, **kwargs): if self.distance: self.update_distance(sample_pix[samp]) dist = self.draw_distance(sample_pix[samp], random_state=rng) - ra_dec = self.draw_from_pixel(ra, dec, sample_pix[samp], random_state=rng) - sample = xpx.at(sample, samp).set(xp.asarray([ra_dec[0], ra_dec[1], dist])) + ra, dec = self.draw_from_pixel(ra, dec, sample_pix[samp], random_state=rng) + new = [ra, dec, dist] else: - sample = xpx.at(sample, samp).set(xp.asarray(sample[samp, :])) + new = self.draw_from_pixel(ra, dec, sample_pix[samp]) + sample = xpx.at(sample, samp).set(xp.asarray(new)) return xp.asarray(sample.reshape((-1, self.num_vars))) def draw_distance(self, pix, *, random_state=None): @@ -1752,7 +1753,9 @@ def _ln_prob(self, samp, lnprob, outbounds, *, xp=None): phi, dec = samp[0] theta = 0.5 * np.pi - dec pixel = self.hp.ang2pix(self.nside, theta, phi) - xpx.at(lnprob, i).set(xp.log(xp.asarray(self.prob[pixel] / self.pixel_area))) + lnprob = xpx.at(lnprob, i).set( + xp.log(xp.asarray(self.prob[pixel] / self.pixel_area)) + ) if self.distance: self.update_distance(pixel) lnprob = xpx.at(lnprob, i).set( From 6ccaa4510346bcb0779be0fa1efa529e76a7bb8c Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Wed, 20 May 2026 17:41:01 +0000 Subject: [PATCH 138/140] TEST: loosen some numeric tests for torch --- test/gw/utils_test.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/gw/utils_test.py b/test/gw/utils_test.py index 2fc700993..9b6815264 100644 --- a/test/gw/utils_test.py +++ b/test/gw/utils_test.py @@ -64,7 +64,8 @@ def test_noise_weighted_inner_product(self): psd = PSD.power_spectral_density_interpolated(frequency) duration = 4 nwip = gwutils.noise_weighted_inner_product(aa, bb, psd, duration) - self.assertEqual(nwip, 239.87768033598326) + # torch doesn't have enough precision + self.assertAlmostEqual(float(nwip), 239.87768033598326, 10) self.assertEqual( gwutils.optimal_snr_squared(aa, psd, duration), @@ -83,7 +84,8 @@ def test_matched_filter_snr(self): mfsnr = gwutils.matched_filter_snr( signal, frequency_domain_strain, psd, duration ) - self.assertEqual(mfsnr, 25.510869054168282) + # torch doesn't have enough precision + self.assertAlmostEqual(float(mfsnr), 25.510869054168282, 10) self.assertEqual(aac.get_namespace(mfsnr), self.xp) def test_overlap(self): From 2d15cf91451908983b9c9de2e5b41e21654cdf8d Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Wed, 20 May 2026 19:27:00 +0000 Subject: [PATCH 139/140] CI: fix basic install workflow --- .github/workflows/basic-install.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/basic-install.yml b/.github/workflows/basic-install.yml index 6b7010458..673b6aa53 100644 --- a/.github/workflows/basic-install.yml +++ b/.github/workflows/basic-install.yml @@ -39,7 +39,7 @@ jobs: run: bash test/ci_test_imports.sh - name: Test entry points run: | - - if: ${{ matrix.os != "windows-latest" }} + - if: matrix.os != 'windows-latest' run: | for script in $(pip show -f bilby | grep "bin\/" | xargs -I {} basename {}); do ${script} --help; From 6ff0ef7df236fd87eadb4900d55aba1c0af4e1c1 Mon Sep 17 00:00:00 2001 From: Colm Talbot Date: Thu, 21 May 2026 07:30:43 -0400 Subject: [PATCH 140/140] CI: fix testing executables Co-authored-by: Michael J. Williams --- .github/workflows/basic-install.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/basic-install.yml b/.github/workflows/basic-install.yml index 673b6aa53..167268ee6 100644 --- a/.github/workflows/basic-install.yml +++ b/.github/workflows/basic-install.yml @@ -38,8 +38,7 @@ jobs: - name: Test imports run: bash test/ci_test_imports.sh - name: Test entry points - run: | - - if: matrix.os != 'windows-latest' + if: matrix.os != 'windows-latest' run: | for script in $(pip show -f bilby | grep "bin\/" | xargs -I {} basename {}); do ${script} --help;