diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 4c6977b..1f39526 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -14,7 +14,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.9", "3.10"] + python-version: ["3.10"] steps: - uses: actions/checkout@v4 diff --git a/mlpp_lib/__init__.py b/mlpp_lib/__init__.py index ef2d419..3213f00 100644 --- a/mlpp_lib/__init__.py +++ b/mlpp_lib/__init__.py @@ -2,4 +2,4 @@ import os -os.environ["TF_USE_LEGACY_KERAS"] = "1" +os.environ["KERAS_BACKEND"] = "torch" diff --git a/mlpp_lib/callbacks.py b/mlpp_lib/callbacks.py index 7c5ed33..07c8e9e 100644 --- a/mlpp_lib/callbacks.py +++ b/mlpp_lib/callbacks.py @@ -2,7 +2,7 @@ import numpy as np import properscoring as ps -from tensorflow.keras import callbacks +from keras import callbacks class EnsembleMetrics(callbacks.Callback): @@ -16,7 +16,7 @@ def add_validation_data(self, validation_data) -> None: def on_epoch_end(self, epoch, logs): """Compute a range of probabilistic scores at the end of each epoch.""" - y_pred = self.model(self.X_val).sample(self.n_samples) + y_pred = self.model(self.X_val).sample((self.n_samples,)) y_pred = y_pred.numpy()[:, :, 0].T y_val = np.squeeze(self.y_val) diff --git a/mlpp_lib/custom_distributions.py b/mlpp_lib/custom_distributions.py new file mode 100644 index 0000000..30d3d30 --- /dev/null +++ b/mlpp_lib/custom_distributions.py @@ -0,0 +1,178 @@ +import torch +from torch.distributions import Distribution, constraints, Normal + +class TruncatedNormalDistribution(Distribution): + """ + Implementation of a truncated normal distribution in [a, b] with + differentiable sampling. + + Source: The Truncated Normal Distribution, John Burkardt 2023 + """ + + def __init__(self, mu_bar: torch.Tensor, sigma_bar: torch.Tensor, a: torch.Tensor,b: torch.Tensor): + """_summary_ + + Args: + mu_bar (torch.Tensor): The mean of the underlying Normal. It is not the true mean. + sigma_bar (torch.Tensor): The std of the underlying Normal. It is not the true std. + a (torch.Tensor): The left boundary + b (torch.Tensor): The right boundary + """ + self._n = Normal(mu_bar, sigma_bar) + self.mu_bar = mu_bar + self.sigma_bar = sigma_bar + super().__init__() + + self.a = a + self.b = b + + + def icdf(self, p): + # inverse cdf + p_ = self._n.cdf(self.a) + p * (self._n.cdf(self.b) - self._n.cdf(self.a)) + return self._n.icdf(p_) + + def mean(self) -> torch.Tensor: + """ + Returns: + torch.Tensor: Returns the true mean of the distribution. + """ + alpha = (self.a - self.mu_bar) / self.sigma_bar + beta = (self.b - self.mu_bar) / self.sigma_bar + + sn = torch.distributions.Normal(torch.zeros_like(self.mu_bar), torch.ones_like(self.mu_bar)) + + scale = (torch.exp(sn.log_prob(beta)) - torch.exp(sn.log_prob(alpha)))/(sn.cdf(beta) - sn.cdf(alpha)) + + return self.mu_bar - self.sigma_bar * scale + + def variance(self) -> torch.Tensor: + """ + Returns: + torch.Tensor: Returns the true variance of the distribution. + """ + alpha = (self.a - self.mu_bar) / self.sigma_bar + beta = (self.b - self.mu_bar) / self.sigma_bar + + sn = torch.distributions.Normal(torch.zeros_like(self.mu_bar), torch.ones_like(self.mu_bar)) + + pdf_a = torch.exp(sn.log_prob(alpha)) + pdf_b = torch.exp(sn.log_prob(beta)) + CDF_a = sn.cdf(alpha) + CDF_b = sn.cdf(beta) + + return self.sigma_bar**2 * (1.0 - (beta*pdf_b - alpha*pdf_a)/(CDF_b - CDF_a) - ((pdf_b - pdf_a)/(CDF_b - CDF_a))**2) + + + def moment(self, k): + # Source: A Recursive Formula for the Moments of a Truncated Univariate Normal Distribution (Eric Orjebin) + if k == -1: + return torch.zeros_like(self.mu_bar) + if k == 0: + return torch.ones_like(self.mu_bar) + + alpha = (self.a - self.mu_bar) / self.sigma_bar + beta = (self.b - self.mu_bar) / self.sigma_bar + sn = torch.distributions.Normal(torch.zeros_like(self.mu_bar), torch.ones_like(self.mu_bar)) + + scale = ((self.b**(k-1) * torch.exp(sn.log_prob(beta)) - self.a**(k-1) * torch.exp(sn.log_prob(alpha))) / (sn.cdf(beta) - sn.cdf(alpha))) + + return (k-1)* self.sigma_bar ** 2 * self.moment(k-2) + self.mu_bar * self.moment(k-1) - self.sigma_bar * scale + + def sample(self, shape): + return self.rsample(shape) + + def rsample(self, shape): + # get some random probability [0,1] + p = torch.distributions.Uniform(0,1).sample(shape) + # apply the inverse cdf on p + return self.icdf(p) + + @property + def arg_constraints(self): + return { + 'mu_bar': constraints.real, + 'sigma_bar': constraints.positive, + } + + @property + def has_rsample(self): + return True + +class CensoredNormalDistribution(Distribution): + r"""Implements a censored Normal distribution. + Values of the underlying normal that lie outside the range [a,b] + are assigned to a and b respectively. + + .. math:: + f_Y(y) = + \begin{cases} + a, & \text{if } y \leq a \\ + \sim N(\bar{\mu}, \bar{\sigma}) & \text{if } a < y < b \\ + b, & \text{if } y \geq b \\ + \end{cases} + + + """ + + def __init__(self, mu_bar: torch.Tensor, sigma_bar: torch.Tensor, a: torch.Tensor,b: torch.Tensor): + """ + Args: + mu_bar (torch.Tensor): The mean of the latent normal distribution + sigma_bar (torch.Tensor): The std of the latend normal distribution + a (torch.Tensor): The lower bound of the distribution. + b (torch.Tensor): The upper bound of the distribution. + """ + + + self._n = Normal(mu_bar, sigma_bar) + self.mu_bar = mu_bar + self.sigma_bar = sigma_bar + super().__init__() + + self.a = a + self.b = b + + + def mean(self): + alpha = (self.a - self.mu_bar) / self.sigma_bar + beta = (self.b - self.mu_bar) / self.sigma_bar + + sn = torch.distributions.Normal(torch.zeros_like(self.mu_bar), torch.ones_like(self.mu_bar)) + E_z = TruncatedNormalDistribution(self.mu_bar, self.sigma_bar, self.a, self.b).mean() + return ( + self.b * (1-sn.cdf(beta)) + + self.a * sn.cdf(alpha) + + E_z * (sn.cdf(beta) - sn.cdf(alpha)) + ) + + + def variance(self): + # Variance := Var(Y) = E(Y^2) - E(Y)^2 + alpha = (self.a - self.mu_bar) / self.sigma_bar + beta = (self.b - self.mu_bar) / self.sigma_bar + sn = torch.distributions.Normal(torch.zeros_like(self.mu_bar), torch.ones_like(self.sigma_bar)) + tn = TruncatedNormalDistribution(mu_bar=self.mu_bar, sigma_bar=self.sigma_bar, a=self.a, b=self.b) + + # Law of total expectation: + # E(Y^2) = E(Y^2|X>b)*P(X>b) + E(Y^2|Xb) + a^2 * P(X str: return out -class DataLoader(tf.keras.utils.Sequence): +class DataLoader(keras.utils.Sequence): """A dataloader for mlpp. Parameters @@ -598,16 +599,16 @@ def __init__( self.shuffle = shuffle self.block_size = block_size self.num_samples = len(self.dataset.x) - self.num_batches = int(np.ceil(self.num_samples / batch_size)) - self._indices = tf.range(self.num_samples) + self.num_batches_ = int(np.ceil(self.num_samples / batch_size)) + self._indices = keras.ops.arange(self.num_samples) self._seed = 0 self._reset() def __len__(self) -> int: - return self.num_batches + return self.num_batches_ - def __getitem__(self, index) -> tuple[tf.Tensor, ...]: - if index >= self.num_batches: + def __getitem__(self, index) -> tuple[KerasTensor, ...]: + if index >= self.num_batches_: self._reset() raise IndexError start = index * self.batch_size @@ -626,38 +627,37 @@ def _shuffle_indices(self) -> None: each block stay in their original order, but the blocks themselves are shuffled. """ if self.block_size == 1: - self._indices = tf.random.shuffle(self._indices, seed=self._seed) + self._indices = keras.random.shuffle(self._indices, seed=self._seed) return num_blocks = self._indices.shape[0] // self.block_size - reshaped_indices = tf.reshape( + reshaped_indices = keras.ops.reshape( self._indices[: num_blocks * self.block_size], (num_blocks, self.block_size) ) - shuffled_blocks = tf.random.shuffle(reshaped_indices, seed=self._seed) - shuffled_indices = tf.reshape(shuffled_blocks, [-1]) + shuffled_blocks = keras.random.shuffle(reshaped_indices, seed=self._seed) + shuffled_indices = keras.reshape(shuffled_blocks, [-1]) # Append any remaining elements if the number of indices isn't a multiple of the block size if shuffled_indices.shape[0] % self.block_size: remainder = self._indices[num_blocks * self.block_size :] - shuffled_indices = tf.concat([shuffled_indices, remainder], axis=0) + shuffled_indices = keras.ops.concatenate([shuffled_indices, remainder], axis=0) self._indices = shuffled_indices - def _reset(self) -> None: """Reset iterator and shuffles data if needed""" self.index = 0 if self.shuffle: self._shuffle_indices() - self.dataset.x = tf.gather(self.dataset.x, self._indices) - self.dataset.y = tf.gather(self.dataset.y, self._indices) + self.dataset.x = keras.ops.take(self.dataset.x, self._indices, axis=0) + self.dataset.y = keras.ops.take(self.dataset.y, self._indices, axis=0) if self.dataset.w is not None: - self.dataset.w = tf.gather(self.dataset.w, self._indices) + self.dataset.w = keras.ops.take(self.dataset.w, self._indices, axis=0) self._seed += 1 def _to_device(self, device) -> None: """Transfer data to a device""" - with tf.device(device): - self.dataset.x = tf.constant(self.dataset.x) - self.dataset.y = tf.constant(self.dataset.y) + with keras.device(device): + self.dataset.x = keras.ops.array(self.dataset.x) + self.dataset.y = keras.ops.array(self.dataset.y) if self.dataset.w is not None: - self.dataset.w = tf.constant(self.dataset.w) + self.dataset.w = keras.ops.array(self.dataset.w) class DataFilter: diff --git a/mlpp_lib/exceptions.py b/mlpp_lib/exceptions.py new file mode 100644 index 0000000..e90be88 --- /dev/null +++ b/mlpp_lib/exceptions.py @@ -0,0 +1,3 @@ +class MissingReparameterizationError(Exception): + """Raised when a sampling function without 'rsample' is used in a context requiring reparameterization.""" + pass \ No newline at end of file diff --git a/mlpp_lib/layers.py b/mlpp_lib/layers.py new file mode 100644 index 0000000..56db108 --- /dev/null +++ b/mlpp_lib/layers.py @@ -0,0 +1,200 @@ +from keras.src.layers import Layer +from typing import Optional, Union, Literal +import keras +import keras.ops as ops +import torch +from keras.src.layers import ( + Add, + Dense, + Dropout, + BatchNormalization, + Activation, +) + +@keras.saving.register_keras_serializable() +class MonteCarloDropout(Dropout): + def call(self, inputs): + return super().call(inputs, training=True) + +@keras.saving.register_keras_serializable() +class FullyConnectedLayer(Layer): + """ A fully connected layer composed of a sequence + of linear layers interleaved by optional + batch norms, and dropouts/MC dropouts. + """ + def __init__(self, + hidden_layers: list, + batchnorm: bool = False, + activations: Optional[Union[str, list[str]]] = "relu", + dropout: Optional[Union[float, list[float]]] = None, + mc_dropout: bool = False, + skip_connection: bool = False, + skip_connection_act: str = 'linear', + indx=0): + super().__init__() + + if isinstance(activations, list): + assert len(activations) == len(hidden_layers) + elif isinstance(activations, str): + activations = [activations] * len(hidden_layers) + + if isinstance(dropout, list): + assert len(dropout) == len(hidden_layers) + elif isinstance(dropout, float): + dropout = [dropout] * (len(hidden_layers)) + else: + dropout = [] + + + self.skip_conn = skip_connection + self.layers = [] + self.hidden_layers = hidden_layers + + for i,units in enumerate(hidden_layers): + self.layers.append(Dense(units, name=f"dense_{indx}:{i}")) + if batchnorm: + self.layers.append(BatchNormalization()) + self.layers.append(Activation(activations[i])) + if i < len(dropout) and 0.0 < dropout[i] < 1.0: + if mc_dropout: + self.layers.append(MonteCarloDropout(dropout[i], name=f"mc_dropout_{indx}:{i}")) + else: + self.layers.append(Dropout(dropout[i], name=f"dropout_{indx}:{i}")) + + if skip_connection: + self.skip_enc = Dense(hidden_layers[-1], name=f"skip_dense") + self.skip_add = Add(name=f"skip_add") + self.skip_act = Activation(activation=skip_connection_act, name=f"skip_activation") + + + def compute_output_shape(self, input_shape): + return (input_shape[0], self.hidden_layers[-1]) + + + def call(self, inputs): + # iterate layers + out = inputs + for l in self.layers: + out = l(out) + # optional skip connection + if self.skip_conn: + inputs = self.skip_enc(inputs) + out = self.skip_add([out, inputs]) + out = self.skip_act(out) + return out + + + +class MultibranchLayer(Layer): + def __init__(self, branches: list[Layer], aggregation: Literal['sum', 'concat']='concat'): + super().__init__() + + self.branches = branches + self.aggr = keras.layers.Concatenate(axis=1) if aggregation == 'concat' else keras.layers.Add() + + + def call(self, inputs): + branch_outputs = [branch(inputs) for branch in self.branches] + return self.aggr(branch_outputs) + +class CrossNetLayer(keras.layers.Layer): + def __init__(self, hidden_size, depth=1): + super().__init__() + + self.ws = [ + self.add_weight( + shape=(hidden_size, 1), + initializer="random_normal", + trainable=True) + for _ in range(depth) + ] + self.bs = [ + self.add_weight( + shape=(hidden_size, 1), + initializer="random_normal", + trainable=True) + for _ in range(depth) + ] + + self.hidden_size = hidden_size + self.encoder = Dense(self.hidden_size) + + def build(self, input_shape): + super().build(input_shape) + + def call(self, x): + x = self.encoder(x) + # x_{l+1} = x_0*x_l^T*w_l + b_l + x_l = f(x_l, w_l, b_l) + x_l + # where f learns the residual of x_{l+1} - x+l + x0 = x + x_l = x + for l in range(len(self.ws)): + outer_prod = x0.unsqueeze(2) * x_l.unsqueeze(1) + residual = ops.matmul(outer_prod, self.ws[l]) + self.bs[l] + residual = residual.squeeze() + + x_l = residual + x_l + + return x_l + + def compute_output_shape(self, input_shape, *args, **kwargs): + return (input_shape[0], self.hidden_size) + + +class ParallelConcatenateLayer(Layer): + """Feeds the same input to all given layers + and concatenates their outputs along the last dimension. + """ + def __init__(self, layers: list[Layer]): + super().__init__() + + self.layers = layers + + def call(self, inputs): + + return keras.layers.Concatenate(axis=-1)([l(inputs) for l in self.layers]) + + +class MeanAndTriLCovLayer(Layer): + """ Layer that learns to output the mean of a distribution and + a lower triangular matrix which could be interpreted + as L such that LL^T=Cov(x) by a downstream distribution. + The layer does not apply any constraints on the outputs. + """ + + def __init__(self, d1, bias_init='zeros'): + super().__init__() + d2 = d1 * (d1 + 1) // 2 + + self.mean_layer = Dense(d1, bias_initializer=bias_init, name='mean_layer') + self.tril_cov_layer = Dense(d2, name='tril_cov_layer') + self.d1 = d1 + + def call(self, inputs): + mean = self.mean_layer(inputs) + flat_cov = self.tril_cov_layer(inputs) + + tril_cov = self._build_lower_triangular(flat=flat_cov) + + return mean, tril_cov + + + def _build_lower_triangular(self, flat): + """ + Convert the flat tensor into a lower triangular matrix. + Does not apply any constraints. + Args: + - flat: Tensor of shape (batch_size, dim * (dim + 1) // 2) + + Returns: + - L: Lower triangular matrix of shape (batch_size, dim, dim) + """ + batch_size = flat.size(0) + + L = torch.zeros(batch_size, self.d1, self.d1, device=flat.device, dtype=flat.dtype) + + tril_indices = torch.tril_indices(row=self.d1, col=self.d1, offset=0) + + L[:, tril_indices[0], tril_indices[1]] = flat + + return L \ No newline at end of file diff --git a/mlpp_lib/losses.py b/mlpp_lib/losses.py index c61d833..c8c5e34 100644 --- a/mlpp_lib/losses.py +++ b/mlpp_lib/losses.py @@ -1,680 +1,878 @@ -from typing import Literal, Optional, Union +import os + +from mlpp_lib.exceptions import MissingReparameterizationError +from mlpp_lib.probabilistic_layers import WrappingTorchDist +os.environ["KERAS_BACKEND"] = "torch" +import torch +import keras +import scoringrules as sr +import typing as tp +from keras.src import ops +from keras import tree +from keras.src.losses.loss import reduce_weighted_values +from keras.src.losses.losses import LossFunctionWrapper +from keras.src import backend +import warnings + +class DistributionLoss(keras.Loss): + """Loss base class allowing for non-tensor (distributions) inputs. + + We simply override the `__call__` method in order to avoid calling `conver_to_tensor` + on `Distribution` objects, which would raise an error. -import numpy as np -import tensorflow as tf -import tensorflow_probability as tfp -from tensorflow_probability import distributions as tfd - -from mlpp_lib.decorators import with_attrs - - -@with_attrs(loss_type="probabilistic") -def crps_energy_ensemble( - obs: Union[tf.Tensor, np.ndarray], - fct_ens: Union[tf.Tensor, np.ndarray], -) -> tf.Tensor: - """ - Energy form of the Continuous Ranked Probability Score from Gneiting and Raftery (2007), - where the expectations terms are approximated from an ensemble. - - .. math:: - CRPS(F, y) = E_F|X - y| - 1/2 * E_F|X - X'| - - Parameters - ---------- - fct_ens: array-like - Ensemble forecasts, with ensemble members along the first dimension. - obs: array-like - Observations. - - Return - ------ - crps: tf.Tensor - The CRPS for each sample. - """ - - # first term - E_1 = tf.abs(fct_ens - obs[None, :]) - E_1 = tf.reduce_mean(E_1, axis=0) - - # second term - E_2 = tf.abs(fct_ens[None, :] - fct_ens[:, None]) - E_2 = tf.reduce_mean(E_2, axis=(0, 1)) - crps = E_1 - E_2 / 2 - - return crps - - -@with_attrs(loss_type="probabilistic") -def crps_energy( - obs: Union[tf.Tensor, np.ndarray], - fct_dist: tfp.distributions.Distribution, -) -> tf.Tensor: - """ - Energy form of the Continuous Ranked Probability Score from Gneiting and Raftery (2007), - where the expectation terms are approximated from the distribution using monte-carlo methods. - - .. math:: - CRPS(F, y) = E_F|X - y| - 1/2 * E_F|X - X'| - - Parameters - ---------- - obs: array-like - Array of observations. - fct_dist: tensorflow-probability Distribution - The predicted distribution. - - Return - ------ - crps: tf.Tensor - The CRPS for each sample. - """ - - n_samples = 1000 - - obs = tf.debugging.check_numerics(obs, "Target values") - - use_reparameterization = ( - fct_dist.reparameterization_type == tfd.FULLY_REPARAMETERIZED - ) - - samples_1 = fct_dist.sample(n_samples) - samples_2 = fct_dist.sample(n_samples) - - # first term - E_1 = tfp.monte_carlo.expectation( - f=lambda x: tf.norm(x - obs[None, :], ord=1, axis=-1), - samples=samples_1, - log_prob=fct_dist.log_prob, - use_reparameterization=use_reparameterization, - ) - - # second term - E_2 = tfp.monte_carlo.expectation( - f=lambda x: tf.norm(x - samples_2, ord=1, axis=-1), - samples=samples_1, - log_prob=fct_dist.log_prob, - use_reparameterization=use_reparameterization, - ) - crps = E_1 - E_2 / 2 - # Avoid negative loss when E_2 >> E_1 caused by large values in `sample_2` - crps = tf.abs(crps) - - return crps[..., None] - - -class WeightedCRPSEnergy(tf.keras.losses.Loss): - """ - Compute threshold-weighted CRPS using its kernel score representation. - - Parameters - ---------- - threshold : float - The threshold for the weight function within the threshold-weighted CRPS. - Specifically, the weight function w(x) is 1{x > threshold}. - n_samples : int, optional - The number of Monte Carlo samples to be used for estimating expectations. Must be greater than 1. - Used only if `y_pred` is of type `tfp.Distribution`. - correct_crps : bool, optional - If True, applies a bias correction to the CRPS, as detailed in Eq. 4 of Fricker et al. (2013). - **kwargs : dict, optional - Additional keyword arguments to pass to the parent `Loss` class. - - Methods - ------- - call(y_true, y_pred, scale=None): - Compute the CRPS for predictions and ground truth data. Optionally, compute the - CRPS over maximum value for blocks of size `scale`. - - Notes - ----- - - The implemented weight function is w(x) = 1{x > threshold}. - - For computational stability, a small offset is added to the final CRPS value. """ - - def __init__( - self, - threshold: float, - n_samples: int = 1000, - correct_crps: bool = True, - **kwargs, - ) -> None: - super(WeightedCRPSEnergy, self).__init__(**kwargs) - - self.threshold = float(threshold) - self.n_samples = int(n_samples) - if self.n_samples < 2: - raise ValueError("n_samples must be > 1") - self.correct_crps = bool(correct_crps) - self.bias_correction = n_samples / (n_samples - 1) if correct_crps else 1.0 - - def get_config(self) -> dict: - custom_config = { - "threshold": self.threshold, - "n_samples": self.n_samples, - "correct_crps": self.correct_crps, - } - config = super().get_config() - config.update(custom_config) - return config - - def call( - self, - y_true: Union[tf.Tensor, np.ndarray], - y_pred: Union[tf.Tensor, np.ndarray, tfp.distributions.Distribution], - scale: Optional[int] = None, - ) -> tf.Tensor: - """ - Compute the loss. - - Parameters - ---------- - y_true: array-like - Values representing the ground truth. - y_pred: array_like or tfp.Distribution - Predicted values or distributions. - scale : int, optional - If provided, the CRPS is computed over the maximum values within blocks of - size `scale` in both `y_true` and `y_pred`. The input tensors' first dimension - (typically batch size) should be divisible by this scale. - """ - - threshold = tf.constant(self.threshold, dtype=y_true.dtype) - n_samples = self.n_samples - y_true = tf.debugging.check_numerics(y_true, "Target values") - v_obs = tf.math.maximum(y_true, threshold) - - if tf.is_tensor(y_pred) or isinstance(y_pred, np.ndarray): - - n_samples = y_pred.shape[0] - - v_ens = tf.math.maximum(y_pred, threshold) - - if scale is not None and scale > 1: - # Reshape the tensors and compute the block maxima - reshaped_true = tf.reshape(v_obs, (-1, scale, 1)) - v_obs = tf.reduce_max(reshaped_true, axis=1) - - reshaped_pred = tf.reshape(v_ens, (n_samples, -1, scale, 1)) - v_ens = tf.reduce_max(reshaped_pred, axis=2) - - # first term - E_1 = tf.abs(v_ens - v_obs[None, :]) - E_1 = tf.reduce_mean(E_1, axis=0) - - # second term - E_2 = tf.abs(v_ens[None, :] - v_ens[:, None]) - E_2 = tf.reduce_mean(E_2, axis=(0, 1)) - - else: - - use_reparameterization = ( - y_pred.reparameterization_type == tfd.FULLY_REPARAMETERIZED + def __call__(self, y_true, y_pred, sample_weight=None): + in_mask = backend.get_keras_mask(y_pred) + + with ops.name_scope(self.name): + # added to avoid convert_to_tensor when y_pred is a distribution + def _maybe_convert_to_tensor(x): + if isinstance(x, torch.distributions.Distribution) or isinstance(x, WrappingTorchDist): + return x + return ops.convert_to_tensor(x, dtype=self.dtype) + + y_pred = tree.map_structure(_maybe_convert_to_tensor, y_pred) + # ---- + y_true = tree.map_structure( + lambda x: ops.convert_to_tensor(x, dtype=self.dtype), y_true ) - samples_1 = tf.math.maximum(y_pred.sample(n_samples), threshold) - samples_2 = tf.math.maximum(y_pred.sample(n_samples), threshold) - - if scale is not None and scale > 1: - # Reshape the tensors and compute the block maxima - reshaped_true = tf.reshape(v_obs, (-1, scale, 1)) - v_obs = tf.reduce_max(reshaped_true, axis=1) - - reshaped_pred = tf.reshape(samples_1, (n_samples, -1, scale, 1)) - samples_1 = tf.reduce_max(reshaped_pred, axis=2) - - reshaped_pred = tf.reshape(samples_2, (n_samples, -1, scale, 1)) - samples_2 = tf.reduce_max(reshaped_pred, axis=2) - - # first term - E_1 = tfp.monte_carlo.expectation( - f=lambda x: tf.norm(x - v_obs[None, :], ord=1, axis=-1), - samples=samples_1, - log_prob=y_pred.log_prob, - use_reparameterization=use_reparameterization, - )[..., None] - - # second term - E_2 = tfp.monte_carlo.expectation( - f=lambda x: tf.norm(x - samples_2, ord=1, axis=-1), - samples=samples_1, - log_prob=y_pred.log_prob, - use_reparameterization=use_reparameterization, - )[..., None] - - twcrps = E_1 - self.bias_correction * E_2 / 2 - - # Avoid negative loss when E_2 >> E_1 caused by large values in `sample_2` - twcrps = tf.abs(twcrps) - # Add a small offset to ensure stability - twcrps += 1e-7 - - return twcrps - - -class MultiScaleCRPSEnergy(WeightedCRPSEnergy): - """ - Compute threshold-weighted CRPS over multiple scales of data. - - Parameters - ---------- - scales: list[int] - List of scales (block sizes) over which CRPS is computed. The batch size used for - training must be divisible by all scales. - threshold: float - The threshold to be used within the weight function of the threshold-weighted CRPS. - n_samples: int, optional (default=1000) - Number of samples used to compute the Monte Carlo expectations. - correct_crps: bool, optional (default=True) - Whether to bias correct the CRPS following Eq. 4 in Fricker et al. (2013). - **kwargs: - (Optional) Additional keyword arguments to be passed to the parent `WeightedCRPSEnergy` class. - - Notes - ----- - The CRPS is computed over the maximum value in each block (scale) of `y_true` - and the samples sampled from `y_pred`. - """ - - def __init__(self, scales: list[int], **kwargs) -> None: - """Initialize the MultiScaleCRPSEnergy with scales and other parameters.""" - super(MultiScaleCRPSEnergy, self).__init__(**kwargs) - self.scales = scales + losses = self.call(y_true, y_pred) + out_mask = backend.get_keras_mask(losses) - def call( - self, - y_true: tf.Tensor, - y_pred: Union[tf.Tensor, tfp.distributions.Distribution], - ) -> tf.Tensor: - """ - Compute the threshold-weighted CRPS over multiple scales of data. - - Parameters - ---------- - y_true: tf.Tensor - Ground truth tensor. - y_pred: Union[tf.Tensor, tfp.distributions.Distribution] - Predicted tensor or distribution. - - Returns - ------- - tf.Tensor - Loss tensor, computed as the average CRPS over the provided scales. - """ - - all_losses = [] - - for scale in self.scales: - - if scale > 1: - tf.debugging.assert_equal( - tf.shape(y_true)[0] % scale, - 0, - message=f"Input tensor length ({tf.shape(y_true)[0]}) is not divisible by scale {scale}.", - ) - - scale_loss = super(MultiScaleCRPSEnergy, self).call( - y_true, y_pred, scale=scale + if in_mask is not None and out_mask is not None: + mask = in_mask & out_mask + elif in_mask is not None: + mask = in_mask + elif out_mask is not None: + mask = out_mask + else: + mask = None + + return reduce_weighted_values( + losses, + sample_weight=sample_weight, + mask=mask, + reduction=self.reduction, + dtype=self.dtype, ) - # Repeat loss to match the original shape for CRPS computation - if scale > 1: - scale_loss = tf.repeat(scale_loss, scale, axis=0) - - all_losses.append(scale_loss) - - # Average the losses over all scales - total_loss = tf.reduce_mean(tf.stack(all_losses, axis=0), axis=0) - - return total_loss - - -class EnergyScore(tf.keras.losses.Loss): - """ - Compute Energy Score. - - Parameters - ---------- - threshold: float - The threshold to be used within the weight function of the Energy Score. - n_samples: int - Number of samples used to compute the Monte Carlo expectations. - **kwargs: - (Optional) Additional keyword arguments to be passed to the parent `Loss` class. - """ - - def __init__( - self, - n_samples: int = 1000, - **kwargs, - ) -> None: - super(EnergyScore, self).__init__(**kwargs) - - self.n_samples = int(n_samples) - - def get_config(self) -> None: - custom_config = { - "n_samples": self.n_samples, - } - config = super().get_config() - config.update(custom_config) - return config - - def call( - self, - y_true: Union[tf.Tensor, np.ndarray], - y_pred: tfp.distributions.Distribution, - ) -> tf.Tensor: +# class CRPSNormal(DistributionLossWrapper): +# def __init__(self): +# super(scoringrule...) + +class DistributionLossWrapper(DistributionLoss, LossFunctionWrapper): + ''' + Wraps a scoringrules score function with analytical fomulation into a keras loss function, + such that it can be used with y_true being a tensor and y_pred + being a torch.distributions.Distribution. This means that the loss value is computed + directly from the parameters of the distribution rather than samples. + ''' + def __init__(self, fn: tp.Callable[[torch.Tensor], torch.Tensor],**kwargs): + """_summary_ + + Args: + fn (tp.Callable[[torch.Tensor], torch.Tensor]): the scoringrules function """ - Compute the loss. - - Parameters - ---------- - y_true: array-like - Values representing the ground truth. - y_pred: tfp.Distribution - Predicted distributions. + kwargs = {'backend': backend.backend(), **kwargs} + + def _extract_wrapper(y_true, y_pred: torch.distributions.Distribution | WrappingTorchDist, **kwargs): + if isinstance(y_pred, torch.distributions.Distribution): + params = [getattr(y_pred, p) for p in self._sr_param_order(y_pred)] + else: + params = [getattr(y_pred._distribution, p) for p in self._sr_param_order(y_pred)] + params = self._sr_reparametrization(y_pred)(*params) + + return fn(y_true, *params, **kwargs) + + super().__init__(_extract_wrapper, **kwargs) + + def call(self, y_true, y_pred): + losses = self.fn(y_true, y_pred, **self._fn_kwargs) + if losses.numel() != y_true.shape[0]: + warnings.warn( + f"The number of elements in the losses tensor (shape {losses.shape}) is not as expected. There probably is an error.", + UserWarning,) + return losses + + + + @staticmethod + def _sr_param_order(dist: WrappingTorchDist) -> list[str]: + """Given a distribution, returns a list + of strings representing the attribute names of the + distribution in the order expected by scoringrules. + + Args: + dist (torch.distributions.Distribution): the distribution + + Raises: + ValueError: when the order is not specified in the SR_PARAM_ORDER + mapping. + + Returns: + list[str]: the attributes in the expected order. """ - - y_true = tf.debugging.check_numerics(y_true, "Target values") - - use_reparameterization = ( - y_pred.reparameterization_type == tfd.FULLY_REPARAMETERIZED - ) - - samples_1 = y_pred.sample(self.n_samples) - samples_2 = y_pred.sample(self.n_samples) - - # first term - E_1 = tfp.monte_carlo.expectation( - f=lambda x: tf.norm(x - y_true[None, ...], ord=1, axis=-1), - samples=samples_1, - log_prob=y_pred.log_prob, - use_reparameterization=use_reparameterization, - ) - - E_2 = tfp.monte_carlo.expectation( - f=lambda x: tf.norm(x - samples_2, ord=1, axis=-1), - samples=samples_1, - log_prob=y_pred.log_prob, - use_reparameterization=use_reparameterization, - ) - - energy_score = E_1 - E_2 / 2 - - # Avoid negative loss when E_2 >> E_1 caused by large values in `sample_2` - energy_score = tf.abs(energy_score) - - return energy_score - - -class MultivariateLoss(tf.keras.losses.Loss): - """ - Compute losses for multivariate data. - - Facilitates computing losses for multivariate targets - that may have different units. Allows rescaling the inputs - and applying weights to each target variables. - - Parameters - ---------- - metric: {"mse", "mae", "crps_energy"} - The function used to compute the loss. - scaling: {"minmax", "standard"} - (Optional) A scaling to apply to the data, in order to address the differences - in magnitude due to different units (if unit is not of the variables. Default is `None`. - weights: array-like - (Optional) Weights assigned to each variable in the computation of the loss. Default is `None`. - **kwargs: - (Optional) Additional keyword arguments to be passed to the parent `Loss` class. - - - """ - - def mse_metric(y_true, y_pred): - return tf.reduce_mean(tf.square(y_true - y_pred), axis=0) - - def mae_metric(y_true, y_pred): - return tf.reduce_mean(tf.abs(y_true - y_pred), axis=0) - - avail_metrics = { - "mse": mse_metric, - "mae": mae_metric, - "crps_energy": crps_energy, - } - - avail_scaling = { - "standard": "_standard_scaling", - "minmax": "_minmax_scaling", - None: "none", - } - - def __init__( - self, - metric: Literal["mse", "mae"], - scaling: Literal["minmax", "standard"] = None, - weights: Union[list, np.ndarray] = None, - **kwargs, - ) -> None: - super(MultivariateLoss, self).__init__(**kwargs) - - try: - self.metric = self.avail_metrics[metric] - except KeyError as err: - raise NotImplementedError( - f"`metric` argument must be one of {list(self.avail_metrics.keys())}" - ) from err - try: - if getattr(self.metric, "loss_type", None) == "probabilistic": - method_name = self.avail_scaling[scaling] + "_probabilistic" - else: - method_name = self.avail_scaling[scaling] - self.scaling = getattr(self, method_name, None) - except KeyError as err: - raise NotImplementedError( - f"`scaling` argument must be one of {list(self.avail_scaling.keys())}" - ) from err - - if weights: - self.weights = tf.constant(weights) - else: - self.weights = None - - def get_config(self) -> None: - config = { - "metric": self.metric, - "scaling": self.scaling, - "weights": self.weights, - } - base_config = super().get_config() - return dict(list(base_config.items()) + list(config.items())) - - def call( - self, - y_true: Union[tf.Tensor, np.ndarray], - y_pred: Union[tf.Tensor, np.ndarray, tfp.distributions.Distribution], - ) -> tf.Tensor: - """ - Compute the loss. - - Parameters - ---------- - y_true: array-like - Values representing the ground truth. - y_pred: array_like or tfp.Distribution - Predicted values or distributions. - + return SR_PARAM_ORDER[dist.name] + except KeyError: + raise ValueError( + "The order of the distribution parameters passed to scoringrules" + f"must be specified. Not found for {dist.name}") + + @staticmethod + def _sr_reparametrization(dist: WrappingTorchDist)-> tp.Callable[..., tp.Tuple[torch.Tensor, ...]]: + """Given a distribution, returns a function to be + applied to every parameter of the distribution to match + the expected parametrization of scoringrules. + + Args: + dist (torch.distributions.Distribution): the distribution + + Raises: + ValueError: when the reparametrization function is not specified in + the SR_REPARAM mapping. + + Returns: + tp.Callable[..., tp.Tuple[torch.Tensor, ...]]: a tuple with the reparametrized parameters. """ - - assert y_true.shape[1:] == y_pred.shape[1:] - if self.weights is not None: - assert ( - len(self.weights) == y_true.shape[-1] - ), "Number weights must match the number of target variables." - - if self.scaling: - y_true, y_pred = self.scaling(y_true, y_pred) - - loss = self.metric(y_true, y_pred) - - if self.weights is not None: - loss = tf.multiply(loss, self.weights) - - return loss - - def _minmax_scaling( - self, y_true: Union[tf.Tensor, np.ndarray], y_pred: Union[tf.Tensor, np.ndarray] - ) -> tuple[tf.Tensor]: - - y_true_min = tf.reduce_min(y_true, axis=0) - y_true_max = tf.reduce_max(y_true, axis=0) - y_true = (y_true - y_true_min) / (y_true_max - y_true_min) - y_pred = (y_pred - y_true_min) / (y_true_max - y_true_min) - - return y_true, y_pred - - def _minmax_scaling_probabilistic( - self, - y_true: Union[tf.Tensor, np.ndarray], - y_pred: tfp.distributions.Distribution, - ) -> tuple[tf.Tensor]: - - y_true_min = tf.reduce_min(y_true, axis=0) - y_true_max = tf.reduce_max(y_true, axis=0) - - scale = tfp.bijectors.Scale(scale=1 / (y_true_max - y_true_min)) - shift = tfp.bijectors.Shift(shift=-y_true_min) - y_true = (y_true - y_true_min) / (y_true_max - y_true_min) - y_pred = scale(shift(y_pred)) - - y_pred.shape = (*y_pred.batch_shape, *y_pred.event_shape) - - return y_true, y_pred - - def _standard_scaling( - self, y_true: Union[tf.Tensor, np.ndarray], y_pred: Union[tf.Tensor, np.ndarray] - ) -> tuple[tf.Tensor]: - - y_true_mean = tf.math.reduce_mean(y_true, axis=0) - y_true_std = tf.math.reduce_std(y_true, axis=0) - y_true = (y_true - y_true_mean) / y_true_std - y_pred = (y_pred - y_true_mean) / y_true_std - - return y_true, y_pred - - def _standard_scaling_probabilistic( - self, - y_true: Union[tf.Tensor, np.ndarray], - y_pred: tfp.distributions.Distribution, - ) -> tuple[tf.Tensor]: - - y_true_mean = tf.math.reduce_mean(y_true, axis=0) - y_true_std = tf.math.reduce_std(y_true, axis=0) - - scale = tfp.bijectors.Scale(scale=1 / y_true_std) - shift = tfp.bijectors.Shift(shift=-y_true_mean) - y_true = (y_true - y_true_mean) / y_true_std - y_pred = scale(shift(y_pred)) - - y_pred.shape = (*y_pred.batch_shape, *y_pred.event_shape) - - return y_true, y_pred - - -class BinaryClassifierLoss(tf.keras.losses.Loss): + try: + return SR_REPARAM[dist.name] + except KeyError: + raise ValueError( + f"The reparametrization function for the distribution parameters \ + passed to scoringrules must be specified. None found for \ + {dist.name}. If the parameters are the same,\ + use the identity function.") + +# Mapping between distribution cls and parameter +# order expected by scoringrules +SR_PARAM_ORDER = { + "Normal": ["loc", "scale"], + "Exponential": ["rate"], + "Weibull": ["concentration", "scale"] +} + +# Mapping between distribution cls and a reparametrization, +# in case torch and scoringrules use different parameters format +# such as 1/\beta vs \beta +SR_REPARAM = { + "Normal": lambda loc, scale: (loc, scale), + "Exponential": lambda x: (x,), + "Weibull": lambda x: (x,) +} + + + +class SampleLossWrapper(DistributionLoss, LossFunctionWrapper): """ - Compute binary classification loss from continuous predictions based on a threshold. - - Parameters - ---------- - threshold: float - loss_type: {"binary_crossentropy", "focal"} - The type of loss to be used. - n_samples: int, optional - **kwargs: - (Optional) Additional keyword arguments to be passed to the parent `Loss` class. - + Wraps a scoringrules ensamble-based estimation of a score function into a keras loss function, + such that it can be used with with y_true being a tensor and y_pred + being a torch.distributions.Distribution. Internally, num_samples samples will be sampled from + the predicted distribution and the loss value is computed with a MC approach. + For gradient-based optimization, this only makes sense if the underlying + torch.distributions.Distribution implements rsample(), ie the reparametrization of the sampling function. """ - - def __init__( - self, - threshold: float, - loss_type: Literal["binary_crossentropy", "focal"] = "binary_crossentropy", - n_samples: int = 1000, - **kwargs, - ) -> None: - super(BinaryClassifierLoss, self).__init__(**kwargs) - - self.threshold = float(threshold) - self.n_samples = int(n_samples) - if self.n_samples < 2: - raise ValueError("n_samples must be > 1") - self.loss_type = loss_type - - def get_config(self) -> dict: - custom_config = { - "threshold": self.threshold, - "loss_type": self.loss_type, - "n_samples": self.n_samples, - } - config = super().get_config() - config.update(custom_config) - return config - - def call( - self, - y_true: Union[tf.Tensor, np.ndarray], - y_pred: Union[tf.Tensor, np.ndarray, tfp.distributions.Distribution], - ) -> tf.Tensor: - """ - Compute the loss. - - Parameters - ---------- - y_true: array-like - Values representing the ground truth. - y_pred: array_like or tfp.Distribution - Predicted values or distributions. - """ - threshold = tf.constant(self.threshold, dtype=y_true.dtype) - n_samples = self.n_samples - y_true = tf.debugging.check_numerics(y_true, "Target values") - - if isinstance(y_pred, tfp.distributions.Distribution): - y_pred_samples = y_pred.sample(n_samples) - else: - y_pred_samples = y_pred - - y_pred_samples = tf.debugging.check_numerics(y_pred_samples, "Predicted values") - - y_true_bool = tf.cast(y_true > threshold, dtype=y_true.dtype) - y_pred_bool = tf.cast(y_pred_samples > threshold, dtype=y_true.dtype) - y_pred_prob = tf.reduce_mean(y_pred_bool, axis=0) - - loss = tf.keras.losses.binary_crossentropy(y_true_bool, y_pred_prob, axis=1) - if self.loss_type == "focal": - loss = tf.pow(1 - tf.exp(-loss), 2) - - return loss - - -class CombinedLoss(tf.keras.losses.Loss): - def __init__(self, losses): - # Local import to avoid circular dependency with mlpp_lib.utils - from mlpp_lib.utils import get_loss - - super(CombinedLoss, self).__init__() - self.losses = [] - self.weights = [] - - # Initialize losses based on the input config dictionaries - for loss_config in losses: - self.weights.append(loss_config.get("weight", 1.0)) - self.losses.append(get_loss(loss_config)) - + def __init__(self, fn: tp.Callable[[torch.Tensor], torch.Tensor], num_samples: int=21, estimator: str = 'pwm', **kwargs): + + kwargs = {'backend': backend.backend(), **kwargs} + + def _extract_wrapper(y_true: torch.Tensor, y_pred: torch.Tensor, **kwargs): + if not y_pred.has_rsample: + raise MissingReparameterizationError(f"Gradient-based optimization will not work. {y_pred.__name__} does not implement rsample().") + # obtain num_samples samples from the distribution y_pred + y_pred_samples = y_pred.rsample(self.num_samples) # [Samples, Batch, Dim] + y_pred_samples = y_pred_samples.permute(1,0,2) # [Batch, Samples, Dim] + return fn(y_true, y_pred_samples, axis=1, estimator=estimator, **kwargs) + + super().__init__(_extract_wrapper, **kwargs) + self.num_samples = (num_samples,) + def call(self, y_true, y_pred): - total_loss = 0 - for loss, weight in zip(self.losses, self.weights): - total_loss += weight * loss(y_true, y_pred) - return total_loss + losses = self.fn(y_true, y_pred, **self._fn_kwargs) + if losses.numel() != y_true.shape[0]: + warnings.warn( + f"The number of elements in the losses tensor (shape {losses.shape}) is not as expected. There probably is an error.", + UserWarning,) + return losses + +# from typing import Literal, Optional, Union + +# import numpy as np +# import tensorflow as tf +# import tensorflow_probability as tfp +# from tensorflow_probability import distributions as tfd + +# from mlpp_lib.decorators import with_attrs + + +# @with_attrs(loss_type="probabilistic") +# def crps_energy_ensemble( +# obs: Union[tf.Tensor, np.ndarray], +# fct_ens: Union[tf.Tensor, np.ndarray], +# ) -> tf.Tensor: +# """ +# Energy form of the Continuous Ranked Probability Score from Gneiting and Raftery (2007), +# where the expectations terms are approximated from an ensemble. + +# .. math:: +# CRPS(F, y) = E_F|X - y| - 1/2 * E_F|X - X'| + +# Parameters +# ---------- +# fct_ens: array-like +# Ensemble forecasts, with ensemble members along the first dimension. +# obs: array-like +# Observations. + +# Return +# ------ +# crps: tf.Tensor +# The CRPS for each sample. +# """ + +# # first term +# E_1 = tf.abs(fct_ens - obs[None, :]) +# E_1 = tf.reduce_mean(E_1, axis=0) + +# # second term +# E_2 = tf.abs(fct_ens[None, :] - fct_ens[:, None]) +# E_2 = tf.reduce_mean(E_2, axis=(0, 1)) +# crps = E_1 - E_2 / 2 + +# return crps + + +# @with_attrs(loss_type="probabilistic") +# def crps_energy( +# obs: Union[tf.Tensor, np.ndarray], +# fct_dist: tfp.distributions.Distribution, +# ) -> tf.Tensor: +# """ +# Energy form of the Continuous Ranked Probability Score from Gneiting and Raftery (2007), +# where the expectation terms are approximated from the distribution using monte-carlo methods. + +# .. math:: +# CRPS(F, y) = E_F|X - y| - 1/2 * E_F|X - X'| + +# Parameters +# ---------- +# obs: array-like +# Array of observations. +# fct_dist: tensorflow-probability Distribution +# The predicted distribution. + +# Return +# ------ +# crps: tf.Tensor +# The CRPS for each sample. +# """ + +# n_samples = 1000 + +# obs = tf.debugging.check_numerics(obs, "Target values") + +# use_reparameterization = ( +# fct_dist.reparameterization_type == tfd.FULLY_REPARAMETERIZED +# ) + +# samples_1 = fct_dist.sample(n_samples) +# samples_2 = fct_dist.sample(n_samples) + +# # first term +# E_1 = tfp.monte_carlo.expectation( +# f=lambda x: tf.norm(x - obs[None, :], ord=1, axis=-1), +# samples=samples_1, +# log_prob=fct_dist.log_prob, +# use_reparameterization=use_reparameterization, +# ) + +# # second term +# E_2 = tfp.monte_carlo.expectation( +# f=lambda x: tf.norm(x - samples_2, ord=1, axis=-1), +# samples=samples_1, +# log_prob=fct_dist.log_prob, +# use_reparameterization=use_reparameterization, +# ) +# crps = E_1 - E_2 / 2 +# # Avoid negative loss when E_2 >> E_1 caused by large values in `sample_2` +# crps = tf.abs(crps) + +# return crps[..., None] + + +# class WeightedCRPSEnergy(tf.keras.losses.Loss): +# """ +# Compute threshold-weighted CRPS using its kernel score representation. + +# Parameters +# ---------- +# threshold : float +# The threshold for the weight function within the threshold-weighted CRPS. +# Specifically, the weight function w(x) is 1{x > threshold}. +# n_samples : int, optional +# The number of Monte Carlo samples to be used for estimating expectations. Must be greater than 1. +# Used only if `y_pred` is of type `tfp.Distribution`. +# correct_crps : bool, optional +# If True, applies a bias correction to the CRPS, as detailed in Eq. 4 of Fricker et al. (2013). +# **kwargs : dict, optional +# Additional keyword arguments to pass to the parent `Loss` class. + +# Methods +# ------- +# call(y_true, y_pred, scale=None): +# Compute the CRPS for predictions and ground truth data. Optionally, compute the +# CRPS over maximum value for blocks of size `scale`. + +# Notes +# ----- +# - The implemented weight function is w(x) = 1{x > threshold}. +# - For computational stability, a small offset is added to the final CRPS value. +# """ + +# def __init__( +# self, +# threshold: float, +# n_samples: int = 1000, +# correct_crps: bool = True, +# **kwargs, +# ) -> None: +# super(WeightedCRPSEnergy, self).__init__(**kwargs) + +# self.threshold = float(threshold) +# self.n_samples = int(n_samples) +# if self.n_samples < 2: +# raise ValueError("n_samples must be > 1") +# self.correct_crps = bool(correct_crps) +# self.bias_correction = n_samples / (n_samples - 1) if correct_crps else 1.0 + +# def get_config(self) -> dict: +# custom_config = { +# "threshold": self.threshold, +# "n_samples": self.n_samples, +# "correct_crps": self.correct_crps, +# } +# config = super().get_config() +# config.update(custom_config) +# return config + +# def call( +# self, +# y_true: Union[tf.Tensor, np.ndarray], +# y_pred: Union[tf.Tensor, np.ndarray, tfp.distributions.Distribution], +# scale: Optional[int] = None, +# ) -> tf.Tensor: +# """ +# Compute the loss. + +# Parameters +# ---------- +# y_true: array-like +# Values representing the ground truth. +# y_pred: array_like or tfp.Distribution +# Predicted values or distributions. +# scale : int, optional +# If provided, the CRPS is computed over the maximum values within blocks of +# size `scale` in both `y_true` and `y_pred`. The input tensors' first dimension +# (typically batch size) should be divisible by this scale. +# """ + +# threshold = tf.constant(self.threshold, dtype=y_true.dtype) +# n_samples = self.n_samples +# y_true = tf.debugging.check_numerics(y_true, "Target values") +# v_obs = tf.math.maximum(y_true, threshold) + +# if tf.is_tensor(y_pred) or isinstance(y_pred, np.ndarray): + +# n_samples = y_pred.shape[0] + +# v_ens = tf.math.maximum(y_pred, threshold) + +# if scale is not None and scale > 1: +# # Reshape the tensors and compute the block maxima +# reshaped_true = tf.reshape(v_obs, (-1, scale, 1)) +# v_obs = tf.reduce_max(reshaped_true, axis=1) + +# reshaped_pred = tf.reshape(v_ens, (n_samples, -1, scale, 1)) +# v_ens = tf.reduce_max(reshaped_pred, axis=2) + +# # first term +# E_1 = tf.abs(v_ens - v_obs[None, :]) +# E_1 = tf.reduce_mean(E_1, axis=0) + +# # second term +# E_2 = tf.abs(v_ens[None, :] - v_ens[:, None]) +# E_2 = tf.reduce_mean(E_2, axis=(0, 1)) + +# else: + +# use_reparameterization = ( +# y_pred.reparameterization_type == tfd.FULLY_REPARAMETERIZED +# ) + +# samples_1 = tf.math.maximum(y_pred.sample(n_samples), threshold) +# samples_2 = tf.math.maximum(y_pred.sample(n_samples), threshold) + +# if scale is not None and scale > 1: +# # Reshape the tensors and compute the block maxima +# reshaped_true = tf.reshape(v_obs, (-1, scale, 1)) +# v_obs = tf.reduce_max(reshaped_true, axis=1) + +# reshaped_pred = tf.reshape(samples_1, (n_samples, -1, scale, 1)) +# samples_1 = tf.reduce_max(reshaped_pred, axis=2) + +# reshaped_pred = tf.reshape(samples_2, (n_samples, -1, scale, 1)) +# samples_2 = tf.reduce_max(reshaped_pred, axis=2) + +# # first term +# E_1 = tfp.monte_carlo.expectation( +# f=lambda x: tf.norm(x - v_obs[None, :], ord=1, axis=-1), +# samples=samples_1, +# log_prob=y_pred.log_prob, +# use_reparameterization=use_reparameterization, +# )[..., None] + +# # second term +# E_2 = tfp.monte_carlo.expectation( +# f=lambda x: tf.norm(x - samples_2, ord=1, axis=-1), +# samples=samples_1, +# log_prob=y_pred.log_prob, +# use_reparameterization=use_reparameterization, +# )[..., None] + +# twcrps = E_1 - self.bias_correction * E_2 / 2 + +# # Avoid negative loss when E_2 >> E_1 caused by large values in `sample_2` +# twcrps = tf.abs(twcrps) +# # Add a small offset to ensure stability +# twcrps += 1e-7 + +# return twcrps + + +# class MultiScaleCRPSEnergy(WeightedCRPSEnergy): +# """ +# Compute threshold-weighted CRPS over multiple scales of data. + +# Parameters +# ---------- +# scales: list[int] +# List of scales (block sizes) over which CRPS is computed. The batch size used for +# training must be divisible by all scales. +# threshold: float +# The threshold to be used within the weight function of the threshold-weighted CRPS. +# n_samples: int, optional (default=1000) +# Number of samples used to compute the Monte Carlo expectations. +# correct_crps: bool, optional (default=True) +# Whether to bias correct the CRPS following Eq. 4 in Fricker et al. (2013). +# **kwargs: +# (Optional) Additional keyword arguments to be passed to the parent `WeightedCRPSEnergy` class. + +# Notes +# ----- +# The CRPS is computed over the maximum value in each block (scale) of `y_true` +# and the samples sampled from `y_pred`. +# """ + +# def __init__(self, scales: list[int], **kwargs) -> None: +# """Initialize the MultiScaleCRPSEnergy with scales and other parameters.""" +# super(MultiScaleCRPSEnergy, self).__init__(**kwargs) +# self.scales = scales + +# def call( +# self, +# y_true: tf.Tensor, +# y_pred: Union[tf.Tensor, tfp.distributions.Distribution], +# ) -> tf.Tensor: +# """ +# Compute the threshold-weighted CRPS over multiple scales of data. + +# Parameters +# ---------- +# y_true: tf.Tensor +# Ground truth tensor. +# y_pred: Union[tf.Tensor, tfp.distributions.Distribution] +# Predicted tensor or distribution. + +# Returns +# ------- +# tf.Tensor +# Loss tensor, computed as the average CRPS over the provided scales. +# """ + +# all_losses = [] + +# for scale in self.scales: + +# if scale > 1: +# tf.debugging.assert_equal( +# tf.shape(y_true)[0] % scale, +# 0, +# message=f"Input tensor length ({tf.shape(y_true)[0]}) is not divisible by scale {scale}.", +# ) + +# scale_loss = super(MultiScaleCRPSEnergy, self).call( +# y_true, y_pred, scale=scale +# ) + +# # Repeat loss to match the original shape for CRPS computation +# if scale > 1: +# scale_loss = tf.repeat(scale_loss, scale, axis=0) + +# all_losses.append(scale_loss) + +# # Average the losses over all scales +# total_loss = tf.reduce_mean(tf.stack(all_losses, axis=0), axis=0) + +# return total_loss + + +# class EnergyScore(tf.keras.losses.Loss): +# """ +# Compute Energy Score. + +# Parameters +# ---------- +# threshold: float +# The threshold to be used within the weight function of the Energy Score. +# n_samples: int +# Number of samples used to compute the Monte Carlo expectations. +# **kwargs: +# (Optional) Additional keyword arguments to be passed to the parent `Loss` class. +# """ + +# def __init__( +# self, +# n_samples: int = 1000, +# **kwargs, +# ) -> None: +# super(EnergyScore, self).__init__(**kwargs) + +# self.n_samples = int(n_samples) + +# def get_config(self) -> None: +# custom_config = { +# "n_samples": self.n_samples, +# } +# config = super().get_config() +# config.update(custom_config) +# return config + +# def call( +# self, +# y_true: Union[tf.Tensor, np.ndarray], +# y_pred: tfp.distributions.Distribution, +# ) -> tf.Tensor: +# """ +# Compute the loss. + +# Parameters +# ---------- +# y_true: array-like +# Values representing the ground truth. +# y_pred: tfp.Distribution +# Predicted distributions. +# """ + +# y_true = tf.debugging.check_numerics(y_true, "Target values") + +# use_reparameterization = ( +# y_pred.reparameterization_type == tfd.FULLY_REPARAMETERIZED +# ) + +# samples_1 = y_pred.sample(self.n_samples) +# samples_2 = y_pred.sample(self.n_samples) + +# # first term +# E_1 = tfp.monte_carlo.expectation( +# f=lambda x: tf.norm(x - y_true[None, ...], ord=1, axis=-1), +# samples=samples_1, +# log_prob=y_pred.log_prob, +# use_reparameterization=use_reparameterization, +# ) + +# E_2 = tfp.monte_carlo.expectation( +# f=lambda x: tf.norm(x - samples_2, ord=1, axis=-1), +# samples=samples_1, +# log_prob=y_pred.log_prob, +# use_reparameterization=use_reparameterization, +# ) + +# energy_score = E_1 - E_2 / 2 + +# # Avoid negative loss when E_2 >> E_1 caused by large values in `sample_2` +# energy_score = tf.abs(energy_score) + +# return energy_score + + +# class MultivariateLoss(tf.keras.losses.Loss): +# """ +# Compute losses for multivariate data. + +# Facilitates computing losses for multivariate targets +# that may have different units. Allows rescaling the inputs +# and applying weights to each target variables. + +# Parameters +# ---------- +# metric: {"mse", "mae", "crps_energy"} +# The function used to compute the loss. +# scaling: {"minmax", "standard"} +# (Optional) A scaling to apply to the data, in order to address the differences +# in magnitude due to different units (if unit is not of the variables. Default is `None`. +# weights: array-like +# (Optional) Weights assigned to each variable in the computation of the loss. Default is `None`. +# **kwargs: +# (Optional) Additional keyword arguments to be passed to the parent `Loss` class. + + +# """ + +# def mse_metric(y_true, y_pred): +# return tf.reduce_mean(tf.square(y_true - y_pred), axis=0) + +# def mae_metric(y_true, y_pred): +# return tf.reduce_mean(tf.abs(y_true - y_pred), axis=0) + +# avail_metrics = { +# "mse": mse_metric, +# "mae": mae_metric, +# "crps_energy": crps_energy, +# } + +# avail_scaling = { +# "standard": "_standard_scaling", +# "minmax": "_minmax_scaling", +# None: "none", +# } + +# def __init__( +# self, +# metric: Literal["mse", "mae"], +# scaling: Literal["minmax", "standard"] = None, +# weights: Union[list, np.ndarray] = None, +# **kwargs, +# ) -> None: +# super(MultivariateLoss, self).__init__(**kwargs) + +# try: +# self.metric = self.avail_metrics[metric] +# except KeyError as err: +# raise NotImplementedError( +# f"`metric` argument must be one of {list(self.avail_metrics.keys())}" +# ) from err + +# try: +# if getattr(self.metric, "loss_type", None) == "probabilistic": +# method_name = self.avail_scaling[scaling] + "_probabilistic" +# else: +# method_name = self.avail_scaling[scaling] +# self.scaling = getattr(self, method_name, None) +# except KeyError as err: +# raise NotImplementedError( +# f"`scaling` argument must be one of {list(self.avail_scaling.keys())}" +# ) from err + +# if weights: +# self.weights = tf.constant(weights) +# else: +# self.weights = None + +# def get_config(self) -> None: +# config = { +# "metric": self.metric, +# "scaling": self.scaling, +# "weights": self.weights, +# } +# base_config = super().get_config() +# return dict(list(base_config.items()) + list(config.items())) + +# def call( +# self, +# y_true: Union[tf.Tensor, np.ndarray], +# y_pred: Union[tf.Tensor, np.ndarray, tfp.distributions.Distribution], +# ) -> tf.Tensor: +# """ +# Compute the loss. + +# Parameters +# ---------- +# y_true: array-like +# Values representing the ground truth. +# y_pred: array_like or tfp.Distribution +# Predicted values or distributions. + +# """ + +# assert y_true.shape[1:] == y_pred.shape[1:] +# if self.weights is not None: +# assert ( +# len(self.weights) == y_true.shape[-1] +# ), "Number weights must match the number of target variables." + +# if self.scaling: +# y_true, y_pred = self.scaling(y_true, y_pred) + +# loss = self.metric(y_true, y_pred) + +# if self.weights is not None: +# loss = tf.multiply(loss, self.weights) + +# return loss + +# def _minmax_scaling( +# self, y_true: Union[tf.Tensor, np.ndarray], y_pred: Union[tf.Tensor, np.ndarray] +# ) -> tuple[tf.Tensor]: + +# y_true_min = tf.reduce_min(y_true, axis=0) +# y_true_max = tf.reduce_max(y_true, axis=0) +# y_true = (y_true - y_true_min) / (y_true_max - y_true_min) +# y_pred = (y_pred - y_true_min) / (y_true_max - y_true_min) + +# return y_true, y_pred + +# def _minmax_scaling_probabilistic( +# self, +# y_true: Union[tf.Tensor, np.ndarray], +# y_pred: tfp.distributions.Distribution, +# ) -> tuple[tf.Tensor]: + +# y_true_min = tf.reduce_min(y_true, axis=0) +# y_true_max = tf.reduce_max(y_true, axis=0) + +# scale = tfp.bijectors.Scale(scale=1 / (y_true_max - y_true_min)) +# shift = tfp.bijectors.Shift(shift=-y_true_min) +# y_true = (y_true - y_true_min) / (y_true_max - y_true_min) +# y_pred = scale(shift(y_pred)) + +# y_pred.shape = (*y_pred.batch_shape, *y_pred.event_shape) + +# return y_true, y_pred + +# def _standard_scaling( +# self, y_true: Union[tf.Tensor, np.ndarray], y_pred: Union[tf.Tensor, np.ndarray] +# ) -> tuple[tf.Tensor]: + +# y_true_mean = tf.math.reduce_mean(y_true, axis=0) +# y_true_std = tf.math.reduce_std(y_true, axis=0) +# y_true = (y_true - y_true_mean) / y_true_std +# y_pred = (y_pred - y_true_mean) / y_true_std + +# return y_true, y_pred + +# def _standard_scaling_probabilistic( +# self, +# y_true: Union[tf.Tensor, np.ndarray], +# y_pred: tfp.distributions.Distribution, +# ) -> tuple[tf.Tensor]: + +# y_true_mean = tf.math.reduce_mean(y_true, axis=0) +# y_true_std = tf.math.reduce_std(y_true, axis=0) + +# scale = tfp.bijectors.Scale(scale=1 / y_true_std) +# shift = tfp.bijectors.Shift(shift=-y_true_mean) +# y_true = (y_true - y_true_mean) / y_true_std +# y_pred = scale(shift(y_pred)) + +# y_pred.shape = (*y_pred.batch_shape, *y_pred.event_shape) + +# return y_true, y_pred + + +# class BinaryClassifierLoss(tf.keras.losses.Loss): +# """ +# Compute binary classification loss from continuous predictions based on a threshold. + +# Parameters +# ---------- +# threshold: float +# loss_type: {"binary_crossentropy", "focal"} +# The type of loss to be used. +# n_samples: int, optional +# **kwargs: +# (Optional) Additional keyword arguments to be passed to the parent `Loss` class. + +# """ + +# def __init__( +# self, +# threshold: float, +# loss_type: Literal["binary_crossentropy", "focal"] = "binary_crossentropy", +# n_samples: int = 1000, +# **kwargs, +# ) -> None: +# super(BinaryClassifierLoss, self).__init__(**kwargs) + +# self.threshold = float(threshold) +# self.n_samples = int(n_samples) +# if self.n_samples < 2: +# raise ValueError("n_samples must be > 1") +# self.loss_type = loss_type + +# def get_config(self) -> dict: +# custom_config = { +# "threshold": self.threshold, +# "loss_type": self.loss_type, +# "n_samples": self.n_samples, +# } +# config = super().get_config() +# config.update(custom_config) +# return config + +# def call( +# self, +# y_true: Union[tf.Tensor, np.ndarray], +# y_pred: Union[tf.Tensor, np.ndarray, tfp.distributions.Distribution], +# ) -> tf.Tensor: +# """ +# Compute the loss. + +# Parameters +# ---------- +# y_true: array-like +# Values representing the ground truth. +# y_pred: array_like or tfp.Distribution +# Predicted values or distributions. +# """ +# threshold = tf.constant(self.threshold, dtype=y_true.dtype) +# n_samples = self.n_samples +# y_true = tf.debugging.check_numerics(y_true, "Target values") + +# if isinstance(y_pred, tfp.distributions.Distribution): +# y_pred_samples = y_pred.sample(n_samples) +# else: +# y_pred_samples = y_pred + +# y_pred_samples = tf.debugging.check_numerics(y_pred_samples, "Predicted values") + +# y_true_bool = tf.cast(y_true > threshold, dtype=y_true.dtype) +# y_pred_bool = tf.cast(y_pred_samples > threshold, dtype=y_true.dtype) +# y_pred_prob = tf.reduce_mean(y_pred_bool, axis=0) + +# loss = tf.keras.losses.binary_crossentropy(y_true_bool, y_pred_prob, axis=1) +# if self.loss_type == "focal": +# loss = tf.pow(1 - tf.exp(-loss), 2) + +# return loss + + +# class CombinedLoss(tf.keras.losses.Loss): +# def __init__(self, losses): +# # Local import to avoid circular dependency with mlpp_lib.utils +# from mlpp_lib.utils import get_loss + +# super(CombinedLoss, self).__init__() +# self.losses = [] +# self.weights = [] + +# # Initialize losses based on the input config dictionaries +# for loss_config in losses: +# self.weights.append(loss_config.get("weight", 1.0)) +# self.losses.append(get_loss(loss_config)) + +# def call(self, y_true, y_pred): +# total_loss = 0 +# for loss, weight in zip(self.losses, self.weights): +# total_loss += weight * loss(y_true, y_pred) +# return total_loss diff --git a/mlpp_lib/metrics.py b/mlpp_lib/metrics.py index 79beb85..fa67ac5 100644 --- a/mlpp_lib/metrics.py +++ b/mlpp_lib/metrics.py @@ -1,12 +1,13 @@ -import tensorflow as tf - +# import tensorflow as tf +import keras +import keras.ops as ops def bias(y_true, y_pred): return tf.reduce_mean(y_pred - y_true, axis=-1) -class MAEBusts(tf.keras.metrics.Metric): - """Compute frequency of occurrence of absolute errors > thr""" +class MAEBusts(keras.metrics.Metric): + """Compute frequency of occurrence of absolute errors > threshold.""" def __init__(self, threshold, name="mae_busts", **kwargs): super().__init__(name=name, **kwargs) @@ -15,16 +16,16 @@ def __init__(self, threshold, name="mae_busts", **kwargs): self.n_samples = self.add_weight(name="ns", initializer="zeros") def update_state(self, y_true, y_pred, sample_weight=None): - values = tf.cast(tf.abs(y_pred - y_true) > self.threshold, tf.float32) + values = ops.cast(ops.abs(y_pred - y_true) > self.threshold, self.dtype) if sample_weight is not None: - sample_weight = tf.cast(sample_weight, self.dtype) - values = tf.multiply(values, sample_weight) - self.n_samples.assign_add(tf.reduce_sum(sample_weight)) + sample_weight = ops.cast(sample_weight, self.dtype) + values = ops.multiply(values, sample_weight) + self.n_samples.assign_add(ops.sum(sample_weight)) else: - self.n_samples.assign_add(tf.cast(tf.size(values), tf.float32)) + self.n_samples.assign_add(ops.cast(ops.size(values), self.dtype)) - self.n_busts.assign_add(tf.reduce_sum(values)) + self.n_busts.assign_add(ops.sum(values)) def result(self): return self.n_busts / self.n_samples diff --git a/mlpp_lib/models.py b/mlpp_lib/models.py index 592c6da..b7c4cbb 100644 --- a/mlpp_lib/models.py +++ b/mlpp_lib/models.py @@ -1,19 +1,23 @@ import logging -from typing import Optional, Union, Any, Callable +from typing import Optional, Union, Any, Literal import numpy as np -import tensorflow as tf -from tensorflow.keras.layers import ( +# import tensorflow as tf +import keras +from keras.src.layers import ( Add, Dense, Dropout, BatchNormalization, Activation, + Concatenate ) -from tensorflow.keras import Model, initializers +from keras import Model, initializers from mlpp_lib.physical_layers import * -from mlpp_lib import probabilistic_layers +# from mlpp_lib import probabilistic_layers +from mlpp_lib.probabilistic_layers import BaseDistributionLayer, BaseParametricDistributionModule, distribution_to_layer +from mlpp_lib.layers import FullyConnectedLayer, MultibranchLayer, CrossNetLayer, ParallelConcatenateLayer try: import tcn # type: ignore @@ -25,38 +29,74 @@ _LOGGER = logging.getLogger(__name__) - -@tf.keras.saving.register_keras_serializable() +class ProbabilisticModel(keras.Model): + """ A probabilistic model composed of an encoder layer + and a probabilistic layer predicting the output's distribution. + """ + def __init__(self, encoder_layer: keras.Layer, probabilistic_layer: BaseDistributionLayer, default_output_type: Literal["distribution", "samples"] = "distribution"): + """_summary_ + + Args: + encoder_layer (keras.Layer): The encoder layer, transforming the inputs into + some latent dimension. + probabilistic_layer (BaseDistributionLayer): the output layer predicting the distribution. + default_output_type (Literal[distribution, samples], optional): Defines the defult behaviour of self.call(), where the model can either output a parametric + distribution, or samples obtained from it. This is important to when fitting the model, as the type of output defines what loss functions are suitable. + Defaults to "distribution". + """ + super().__init__() + + self.encoder_layer = encoder_layer + self.probabilistic_layer = probabilistic_layer + self.default_output_type = default_output_type + + + def call(self, inputs, output_type: Optional[Literal["distribution", "samples"]] = None): + if output_type is None: + output_type = self.default_output_type + + enc = self.encoder_layer(inputs) + return self.probabilistic_layer(enc, output_type=output_type) + + + + +@keras.saving.register_keras_serializable() class MonteCarloDropout(Dropout): def call(self, inputs): return super().call(inputs, training=True) - -def get_probabilistic_layer( - output_size, - probabilistic_layer: Union[str, dict] -) -> Callable: - """Get the probabilistic layer.""" - - if isinstance(probabilistic_layer, dict): - probabilistic_layer_name = list(probabilistic_layer.keys())[0] - probabilistic_layer_options = probabilistic_layer[probabilistic_layer_name] - else: - probabilistic_layer_name = probabilistic_layer - probabilistic_layer_options = {} - - if hasattr(probabilistic_layers, probabilistic_layer_name): - _LOGGER.info(f"Using custom probabilistic layer: {probabilistic_layer_name}") - probabilistic_layer_obj = getattr(probabilistic_layers, probabilistic_layer_name) - n_params = getattr(probabilistic_layers, probabilistic_layer_name).params_size(output_size) - probabilistic_layer = ( - probabilistic_layer_obj(output_size, name="output", **probabilistic_layer_options) if isinstance(probabilistic_layer_obj, type) - else probabilistic_layer_obj(output_size, name="output") - ) - else: - raise KeyError(f"The probabilistic layer {probabilistic_layer_name} is not available.") - - return probabilistic_layer, n_params +def get_probabilistic_layer(distribution: str, bias_init, distribution_kwargs={},num_samples=21): + probabilistic_layer = distribution_to_layer[distribution](**distribution_kwargs) + return BaseDistributionLayer(distribution=probabilistic_layer, + num_samples=num_samples, + bias_init=bias_init) + +# def get_probabilistic_layer( +# output_size, +# probabilistic_layer: Union[str, dict] +# ) -> Callable: +# """Get the probabilistic layer.""" + +# if isinstance(probabilistic_layer, dict): +# probabilistic_layer_name = list(probabilistic_layer.keys())[0] +# probabilistic_layer_options = probabilistic_layer[probabilistic_layer_name] +# else: +# probabilistic_layer_name = probabilistic_layer +# probabilistic_layer_options = {} + +# if hasattr(probabilistic_layers, probabilistic_layer_name): +# _LOGGER.info(f"Using custom probabilistic layer: {probabilistic_layer_name}") +# probabilistic_layer_obj = getattr(probabilistic_layers, probabilistic_layer_name) +# n_params = getattr(probabilistic_layers, probabilistic_layer_name).params_size(output_size) +# probabilistic_layer = ( +# probabilistic_layer_obj(output_size, name="output", **probabilistic_layer_options) if isinstance(probabilistic_layer_obj, type) +# else probabilistic_layer_obj(output_size, name="output") +# ) +# else: +# raise KeyError(f"The probabilistic layer {probabilistic_layer_name} is not available.") + +# return probabilistic_layer, n_params def _build_fcn_block( @@ -91,31 +131,17 @@ def _build_fcn_block( return x -def _build_fcn_output(x, output_size, probabilistic_layer, out_bias_init): - # probabilistic prediction - if probabilistic_layer: - probabilistic_layer, n_params = get_probabilistic_layer(output_size, probabilistic_layer) - if isinstance(out_bias_init, np.ndarray): - out_bias_init = np.hstack( - [out_bias_init, [0.0] * (n_params - out_bias_init.shape[0])] - ) - out_bias_init = initializers.Constant(out_bias_init) - - x = Dense(n_params, bias_initializer=out_bias_init, name="dist_params")(x) - outputs = probabilistic_layer(x) - - # deterministic prediction - else: +def _build_fcn_output(output_size, out_bias_init, probabilistic_layer=None, **distribution_kwargs): + if probabilistic_layer is None: if isinstance(out_bias_init, np.ndarray): out_bias_init = initializers.Constant(out_bias_init) - - outputs = Dense(output_size, bias_initializer=out_bias_init, name="output")(x) - - return outputs - - + return Dense(output_size, name='output', bias_initializer=out_bias_init) + + + prob_layer = get_probabilistic_layer(distribution=probabilistic_layer, bias_init=out_bias_init,distribution_kwargs=distribution_kwargs) + return prob_layer + def fully_connected_network( - input_shape: tuple[int], output_size: int, hidden_layers: list, batchnorm: bool = False, @@ -125,14 +151,13 @@ def fully_connected_network( out_bias_init: Optional[Union[str, np.ndarray[Any, float]]] = "zeros", probabilistic_layer: Optional[str] = None, skip_connection: bool = False, + prob_layer_kwargs: dict = {} ) -> Model: """ - Build a Fully Connected Neural Network. + Get an unbuilt Fully Connected Neural Network. Parameters ---------- - input_shape: tuple[int] - Shape of the input samples (not including batch size) output_size: int Number of target predictants. hidden_layers: list[int] @@ -165,46 +190,30 @@ def fully_connected_network( model: keras model The built (but not yet compiled) model. """ + + ffnn = FullyConnectedLayer(hidden_layers=hidden_layers, + batchnorm=batchnorm, + activations=activations, + dropout=dropout, + mc_dropout=mc_dropout, + skip_connection=skip_connection) + + output_layer = _build_fcn_output(out_bias_init=out_bias_init, + output_size=output_size, + probabilistic_layer=probabilistic_layer, **prob_layer_kwargs) + + if probabilistic_layer is None: + return keras.models.Sequential([ffnn, output_layer]) + + return ProbabilisticModel(encoder_layer=ffnn, + probabilistic_layer=output_layer) - if isinstance(dropout, list): - assert len(dropout) == len(hidden_layers) - elif isinstance(dropout, float): - dropout = [dropout] * (len(hidden_layers) - 1) - else: - dropout = [] - - if isinstance(activations, list): - assert len(activations) == len(hidden_layers) - elif isinstance(activations, str): - activations = [activations] * len(hidden_layers) - - if isinstance(out_bias_init, np.ndarray): - out_bias_init_shape = out_bias_init.shape[-1] - assert out_bias_init.shape[-1] == output_size, ( - f"Bias initialization array is shape {out_bias_init_shape}" - f"but output size is {output_size}" - ) - - inputs = tf.keras.Input(shape=input_shape) - x = _build_fcn_block( - inputs, - hidden_layers, - batchnorm, - activations, - dropout, - mc_dropout, - skip_connection, - ) - outputs = _build_fcn_output(x, output_size, probabilistic_layer, out_bias_init) - model = Model(inputs=inputs, outputs=outputs) - - return model def fully_connected_multibranch_network( - input_shape: tuple[int], output_size: int, hidden_layers: list, + n_branches, batchnorm: bool = False, activations: Optional[Union[str, list[str]]] = "relu", dropout: Optional[Union[float, list[float]]] = None, @@ -212,9 +221,11 @@ def fully_connected_multibranch_network( out_bias_init: Optional[Union[str, np.ndarray[Any, float]]] = "zeros", probabilistic_layer: Optional[str] = None, skip_connection: bool = False, + aggregation: Literal['sum', 'concat']='concat', + prob_layer_kwargs: dict = {} ) -> Model: """ - Build a multi-branch Fully Connected Neural Network. + Returns an unbuilt a multi-branch Fully Connected Neural Network. Parameters ---------- @@ -225,6 +236,8 @@ def fully_connected_multibranch_network( hidden_layers: list[int] List that is used to define the fully connected block. Each element creates a Dense layer with the corresponding units. + n_branches: int + The number of branches. batchnorm: bool Use batch normalization. Default is False. activations: str or list[str] @@ -246,74 +259,53 @@ def fully_connected_multibranch_network( used as output layer of the keras `Model`. Default is None. skip_connection: bool Include a skip connection to the MLP architecture. Default is False. + aggregation: Literal['sum', 'concat'] + The aggregation strategy to combine the branches' outputs. Return ------ model: keras model - The built (but not yet compiled) model. + The unbuilt and uncompiled model. """ - - if isinstance(dropout, list): - assert len(dropout) == len(hidden_layers) - elif isinstance(dropout, float): - dropout = [dropout] * (len(hidden_layers) - 1) - else: - dropout = [] - - if isinstance(activations, list): - assert len(activations) == len(hidden_layers) - elif isinstance(activations, str): - activations = [activations] * len(hidden_layers) - - if isinstance(out_bias_init, np.ndarray): - out_bias_init_shape = out_bias_init.shape[-1] - assert out_bias_init.shape[-1] == output_size, ( - f"Bias initialization array is shape {out_bias_init_shape}" - f"but output size is {output_size}" - ) - - if probabilistic_layer: - _, n_params = get_probabilistic_layer(output_size, probabilistic_layer) - n_branches = n_params - else: - n_branches = output_size - - inputs = tf.keras.Input(shape=input_shape) - all_branch_outputs = [] + + branch_layers = [] for idx in range(n_branches): - x = _build_fcn_block( - inputs, - hidden_layers, - batchnorm, - activations, - dropout, - mc_dropout, - skip_connection, - idx, - ) - all_branch_outputs.append(x) - - concatenated_x = tf.keras.layers.Concatenate()(all_branch_outputs) - outputs = _build_fcn_output( - concatenated_x, output_size, probabilistic_layer, out_bias_init - ) - model = Model(inputs=inputs, outputs=outputs) - - return model + branch_layers.append(FullyConnectedLayer( + hidden_layers=hidden_layers, + batchnorm=batchnorm, + activations=activations, + dropout=dropout, + mc_dropout=mc_dropout, + skip_connection=skip_connection, + indx=idx + )) + + mb_ffnn = MultibranchLayer(branches=branch_layers, aggregation=aggregation) + + output_layer = _build_fcn_output(out_bias_init=out_bias_init, + output_size=output_size, + probabilistic_layer=probabilistic_layer, **prob_layer_kwargs) + + if probabilistic_layer is None: + return keras.models.Sequential([mb_ffnn, output_layer]) + + return ProbabilisticModel(encoder_layer=mb_ffnn, + probabilistic_layer=output_layer) def deep_cross_network( - input_shape: tuple[int], output_size: int, hidden_layers: list, + n_cross_layers: int, + cross_layers_hiddensize: int, batchnorm: bool = True, activations: Optional[Union[str, list[str]]] = "relu", dropout: Optional[Union[float, list[float]]] = None, mc_dropout: bool = False, out_bias_init: Optional[Union[str, np.ndarray[Any, float]]] = "zeros", probabilistic_layer: Optional[str] = None, - skip_connection: bool = False, + prob_layer_kwargs: dict = {} ): """ Build a Deep and Cross Network (see https://arxiv.org/abs/1708.05123). @@ -327,6 +319,10 @@ def deep_cross_network( hidden_layers: list[int] List that is used to define the fully connected block. Each element creates a Dense layer with the corresponding units. + n_cross_layers: int + The number of cross layers + cross_layers_hiddensize: int + The hidden size to be used in the cross layers batchnorm: bool Use batch normalization. Default is True. activations: str or list[str] @@ -346,86 +342,38 @@ def deep_cross_network( probabilistic_layer: str (Optional) Name of a probabilistic layer defined in `mlpp_lib.probabilistic_layers`, which is used as output layer of the keras `Model`. Default is None. - skip_connection: bool - Include a skip connection to the MLP architecture. Default is False. Return ------ model: keras model The built (but not yet compiled) model. """ - if isinstance(dropout, list): - assert len(dropout) == len(hidden_layers) - elif isinstance(dropout, float): - dropout = [dropout] * (len(hidden_layers)) - else: - dropout = [] - - if isinstance(activations, list): - assert len(activations) == len(hidden_layers) - elif isinstance(activations, str): - activations = [activations] * len(hidden_layers) - if isinstance(out_bias_init, np.ndarray): - out_bias_init_shape = out_bias_init.shape[-1] - assert out_bias_init.shape[-1] == output_size, ( - f"Bias initialization array is shape {out_bias_init_shape}" - f"but output size is {output_size}" - ) # cross part - inputs = tf.keras.layers.Input(shape=input_shape) - cross = inputs - for _ in hidden_layers: - units_ = cross.shape[-1] - x = Dense(units_)(cross) - cross = inputs * x + cross - cross = BatchNormalization()(cross) - # cross = tf.keras.Model(inputs=inputs, outputs=cross, name="crossblock") - + cross_layer = CrossNetLayer(hidden_size=cross_layers_hiddensize, + depth=n_cross_layers) + # deep part - deep = inputs - deep = _build_fcn_block( - deep, - hidden_layers, - batchnorm, - activations, - dropout, - mc_dropout, - skip_connection=False, - ) + + deep_layer = FullyConnectedLayer(hidden_layers=hidden_layers, + batchnorm=batchnorm, + activations=activations, + dropout=dropout, + mc_dropout=mc_dropout) - # merge - merge = tf.keras.layers.Concatenate()([cross, deep]) + + encoder = ParallelConcatenateLayer([cross_layer, deep_layer]) - if skip_connection: - merge = Dense(input_shape[0])(merge) - merge = Add()([merge, inputs]) - merge = Activation(activation=activations[-1])(merge) - - # probabilistic prediction - if probabilistic_layer: - probabilistic_layer, n_params = get_probabilistic_layer(output_size, probabilistic_layer) - if isinstance(out_bias_init, np.ndarray): - out_bias_init = np.hstack( - [out_bias_init, [0.0] * (n_params - out_bias_init.shape[0])] - ) - out_bias_init = initializers.Constant(out_bias_init) + output_layer = _build_fcn_output(out_bias_init=out_bias_init, + output_size=output_size, + probabilistic_layer=probabilistic_layer, **prob_layer_kwargs) - x = Dense(n_params, bias_initializer=out_bias_init, name="dist_params")(merge) - outputs = probabilistic_layer(x) - - # deterministic prediction - else: - if isinstance(out_bias_init, np.ndarray): - out_bias_init = initializers.Constant(out_bias_init) - - outputs = Dense(output_size, bias_initializer=out_bias_init, name="output")( - merge - ) - - model = Model(inputs=inputs, outputs=outputs, name="deep_cross_network") - return model + if probabilistic_layer is None: + return keras.models.Sequential([encoder, output_layer]) + + return ProbabilisticModel(encoder_layer=encoder, + probabilistic_layer=output_layer) def temporal_convolutional_network( @@ -450,7 +398,7 @@ def temporal_convolutional_network( if isinstance(out_bias_init, np.ndarray): out_bias_init = initializers.Constant(out_bias_init) - inputs = tf.keras.Input(shape=input_shape, name="input") + inputs = keras.Input(shape=input_shape, name="input") x_tcn = tcn.TCN( nb_filters=nb_filters, kernel_size=kernel_size, diff --git a/mlpp_lib/physical_layers.py b/mlpp_lib/physical_layers.py index 2f4e8b5..c792a06 100644 --- a/mlpp_lib/physical_layers.py +++ b/mlpp_lib/physical_layers.py @@ -1,5 +1,5 @@ -import tensorflow as tf -from tensorflow.keras.layers import Layer +from keras.layers import Layer +import keras.backend as K class ThermodynamicLayer(Layer): @@ -23,7 +23,7 @@ def __init__(self, **kwargs) -> None: self.D_idx = 1 # dew_point_deficit self.P_idx = 2 # surface_air_pressure - self.EPSILON = tf.constant(622.0) + self.EPSILON = K.constant(622.0) def build(self, input_shape: tuple[int]) -> None: super().build(input_shape) @@ -38,11 +38,11 @@ def call(self, inputs): dew_point_deficit = inputs[..., self.D_idx] surface_air_pressure = inputs[..., self.P_idx] - dew_point_temperature = air_temperature - tf.nn.relu(dew_point_deficit) - water_vapor_saturation_pressure = 6.112 * tf.exp( + dew_point_temperature = air_temperature - K.relu(dew_point_deficit) + water_vapor_saturation_pressure = 6.112 * K.exp( (17.67 * air_temperature) / (air_temperature + 243.5) ) - water_vapor_pressure = 6.112 * tf.exp( + water_vapor_pressure = 6.112 * K.exp( (17.67 * dew_point_temperature) / (dew_point_temperature + 243.5) ) relative_humidity = ( @@ -52,7 +52,7 @@ def call(self, inputs): water_vapor_pressure / (surface_air_pressure - water_vapor_pressure) ) - out = tf.concat( + out = K.concat( [ air_temperature[..., None], dew_point_temperature[..., None], diff --git a/mlpp_lib/probabilistic_layers.py b/mlpp_lib/probabilistic_layers.py index 838a5af..5cc15b3 100644 --- a/mlpp_lib/probabilistic_layers.py +++ b/mlpp_lib/probabilistic_layers.py @@ -1,1462 +1,1894 @@ -"""In this module, any custom built keras layers are included.""" - +import torch +import torch.nn as nn +import keras +from keras.layers import TorchModuleWrapper, Dense +from keras import Layer +from abc import ABC, abstractmethod import numpy as np -import tensorflow as tf -import tensorflow_probability.python.layers as tfpl -from tensorflow_probability.python import bijectors as tfb -from tensorflow_probability.python import distributions as tfd -from tensorflow_probability.python.layers.distribution_layer import ( - _event_size, - _get_convert_to_tensor_fn, - _serialize, - dist_util, - independent_lib, -) - - -# these almost work out of the box -from tensorflow_probability.python.layers import ( - IndependentNormal, - IndependentLogistic, - IndependentBernoulli, - IndependentPoisson, -) - - -@tf.keras.saving.register_keras_serializable() -class IndependentNormal(IndependentNormal): - @property - def output(self): # this is necessary to use the layer within shap - return super().output[0] +from keras import initializers +from typing import Literal +from inspect import getmembers, isclass +import sys +from mlpp_lib.custom_distributions import TruncatedNormalDistribution, CensoredNormalDistribution +from mlpp_lib.exceptions import MissingReparameterizationError +from mlpp_lib.layers import MeanAndTriLCovLayer -@tf.keras.saving.register_keras_serializable() -class IndependentLogistic(IndependentLogistic): +class BaseParametricDistributionModule(nn.Module, ABC): + """ Base class for parametric distributions layers + """ @property - def output(self): - return super().output[0] - - -@tf.keras.saving.register_keras_serializable() -class IndependentBernoulli(IndependentBernoulli): + @abstractmethod + def num_parameters(self): + '''The number of parameters that describe the distribution''' + pass + @property - def output(self): - return super().output[0] - - -@tf.keras.saving.register_keras_serializable() -class IndependentPoisson(IndependentPoisson): + @abstractmethod + def name(self): + pass + @property - def output(self): - return super().output[0] - - -@tf.keras.saving.register_keras_serializable() -class IndependentBeta(tfpl.DistributionLambda): - """An independent 2-parameter Beta Keras layer""" - - def __init__( - self, - event_shape=(), - convert_to_tensor_fn=tfd.Distribution.mean, - validate_args=False, - **kwargs - ): - """Initialize the `IndependentBeta` layer. - Args: - event_shape: integer vector `Tensor` representing the shape of single - draw from this distribution. - convert_to_tensor_fn: Python `callable` that takes a `tfd.Distribution` - instance and returns a `tf.Tensor`-like object. - Default value: `tfd.Distribution.mean`. - validate_args: Python `bool`, default `False`. When `True` distribution - parameters are checked for validity despite possibly degrading runtime - performance. When `False` invalid inputs may silently render incorrect - outputs. - Default value: `False`. - **kwargs: Additional keyword arguments passed to `tf.keras.Layer`. - """ - convert_to_tensor_fn = _get_convert_to_tensor_fn(convert_to_tensor_fn) - - # If there is a 'make_distribution_fn' keyword argument (e.g., because we - # are being called from a `from_config` method), remove it. We pass the - # distribution function to `DistributionLambda.__init__` below as the first - # positional argument. - kwargs.pop("make_distribution_fn", None) - - def new_from_t(t): - return IndependentBeta.new(t, event_shape, validate_args) - - super(IndependentBeta, self).__init__( - new_from_t, convert_to_tensor_fn, **kwargs - ) - - self._event_shape = event_shape - self._convert_to_tensor_fn = convert_to_tensor_fn - self._validate_args = validate_args - - @staticmethod - def new(params, event_shape=(), validate_args=False, name=None): - """Create the distribution instance from a `params` vector.""" - with tf.name_scope(name or "IndependentBeta"): - params = tf.convert_to_tensor(params, name="params") - event_shape = dist_util.expand_to_vector( - tf.convert_to_tensor( - event_shape, name="event_shape", dtype_hint=tf.int32 - ), - tensor_name="event_shape", - ) - output_shape = tf.concat( - [ - tf.shape(params)[:-1], - event_shape, - ], - axis=0, - ) - alpha, beta = tf.split(params, 2, axis=-1) - - alpha = tf.math.softplus(tf.reshape(alpha, output_shape)) + 1e-3 - beta = tf.math.softplus(tf.reshape(beta, output_shape)) + 1e-3 - betad = tfd.Beta(alpha, beta, validate_args=validate_args) - - return independent_lib.Independent( - betad, - reinterpreted_batch_ndims=tf.size(event_shape), - validate_args=validate_args, - ) - - @staticmethod - def params_size(event_shape=(), name=None): - """The number of `params` needed to create a single distribution.""" - with tf.name_scope(name or "IndependentBeta_params_size"): - event_shape = tf.convert_to_tensor( - event_shape, name="event_shape", dtype_hint=tf.int32 - ) - return np.int32(2) * _event_size( - event_shape, name=name or "IndependentBeta_params_size" - ) - - def get_config(self): - """Returns the config of this layer. - NOTE: At the moment, this configuration can only be serialized if the - Layer's `convert_to_tensor_fn` is a serializable Keras object (i.e., - implements `get_config`) or one of the standard values: - - `Distribution.sample` (or `"sample"`) - - `Distribution.mean` (or `"mean"`) - - `Distribution.mode` (or `"mode"`) - - `Distribution.stddev` (or `"stddev"`) - - `Distribution.variance` (or `"variance"`) + def has_rsample(self): + return self._distribution.has_rsample + + @abstractmethod + def process_params(self, **kwargs) -> torch.distributions.Distribution: """ - config = { - "event_shape": self._event_shape, - "convert_to_tensor_fn": _serialize(self._convert_to_tensor_fn), - "validate_args": self._validate_args, - } - base_config = super(IndependentBeta, self).get_config() - return dict(list(base_config.items()) + list(config.items())) - - @property - def output(self): - """This allows the use of this layer with the shap package.""" - return super(IndependentBeta, self).output[0] - - -@tf.keras.saving.register_keras_serializable() -class Independent4ParamsBeta(tfpl.DistributionLambda): - """An independent 4-parameter Beta Keras layer allowing control over scale as well as a 'shift' parameter.""" - - def __init__( - self, - event_shape=(), - convert_to_tensor_fn=tfd.Distribution.mean, - validate_args=False, - **kwargs - ): - """Initialize the `Independent4ParamsBeta` layer. - Args: - event_shape: integer vector `Tensor` representing the shape of single - draw from this distribution. - convert_to_tensor_fn: Python `callable` that takes a `tfd.Distribution` - instance and returns a `tf.Tensor`-like object. - Default value: `tfd.Distribution.mean`. - validate_args: Python `bool`, default `False`. When `True` distribution - parameters are checked for validity despite possibly degrading runtime - performance. When `False` invalid inputs may silently render incorrect - outputs. - Default value: `False`. - **kwargs: Additional keyword arguments passed to `tf.keras.Layer`. + Given the distribution's parameters predicted by a previous layer, + ensures the constraints are met and the parametric distribution is returned. """ - convert_to_tensor_fn = _get_convert_to_tensor_fn(convert_to_tensor_fn) - - # If there is a 'make_distribution_fn' keyword argument (e.g., because we - # are being called from a `from_config` method), remove it. We pass the - # distribution function to `DistributionLambda.__init__` below as the first - # positional argument. - kwargs.pop("make_distribution_fn", None) - - def new_from_t(t): - return Independent4ParamsBeta.new(t, event_shape, validate_args) - - super(Independent4ParamsBeta, self).__init__( - new_from_t, convert_to_tensor_fn, **kwargs - ) - - self._event_shape = event_shape - self._convert_to_tensor_fn = convert_to_tensor_fn - self._validate_args = validate_args - - @staticmethod - def new(params, event_shape=(), validate_args=False, name=None): - """Create the distribution instance from a `params` vector.""" - with tf.name_scope(name or "Independent4ParamsBeta"): - params = tf.convert_to_tensor(params, name="params") - event_shape = dist_util.expand_to_vector( - tf.convert_to_tensor( - event_shape, name="event_shape", dtype_hint=tf.int32 - ), - tensor_name="event_shape", - ) - output_shape = tf.concat( - [ - tf.shape(params)[:-1], - event_shape, - ], - axis=0, - ) - alpha, beta, shift, scale = tf.split(params, 4, axis=-1) - # alpha > 2 and beta > 2 produce a concave downward Beta - alpha = tf.math.softplus(tf.reshape(alpha, output_shape)) + 1e-3 - beta = tf.math.softplus(tf.reshape(beta, output_shape)) + 1e-3 - shift = tf.math.softplus(tf.reshape(shift, output_shape)) - scale = tf.math.softplus(tf.reshape(scale, output_shape)) + 1e-3 - betad = tfd.Beta(alpha, beta, validate_args=validate_args) - transf_betad = tfd.TransformedDistribution( - distribution=betad, bijector=tfb.Shift(shift)(tfb.Scale(scale)) - ) - return independent_lib.Independent( - transf_betad, - reinterpreted_batch_ndims=tf.size(event_shape), - validate_args=validate_args, - ) - - @staticmethod - def params_size(event_shape=(), name=None): - """The number of `params` needed to create a single distribution.""" - with tf.name_scope(name or "Independent4ParamsBeta_params_size"): - event_shape = tf.convert_to_tensor( - event_shape, name="event_shape", dtype_hint=tf.int32 - ) - return np.int32(4) * _event_size( - event_shape, name=name or "Independent4ParamsBeta_params_size" - ) + pass + + def forward(self, predicted_parameters, num_samples=1, return_dist=True, pattern: Literal['sbd', 'bsd'] = 'sbd', reparametrized=False): + parametric_dist = self.process_params(predicted_parameters) + + dist = WrappingTorchDist(distribution=parametric_dist) + + if return_dist: + return dist + + if not reparametrized: + return dist.sample(num_samples, pattern=pattern) + return dist.rsample(num_samples, pattern) - def get_config(self): - """Returns the config of this layer. - NOTE: At the moment, this configuration can only be serialized if the - Layer's `convert_to_tensor_fn` is a serializable Keras object (i.e., - implements `get_config`) or one of the standard values: - - `Distribution.sample` (or `"sample"`) - - `Distribution.mean` (or `"mean"`) - - `Distribution.mode` (or `"mode"`) - - `Distribution.stddev` (or `"stddev"`) - - `Distribution.variance` (or `"variance"`) - """ - config = { - "event_shape": self._event_shape, - "convert_to_tensor_fn": _serialize(self._convert_to_tensor_fn), - "validate_args": self._validate_args, + +class WrappingTorchDist(): + """ + Wraps a torch.distributions.Distribution instance. + Unifies sample(torch.Size) and sample_n(int) in a single function and + allows to specify a pattern for the samples between [Batch, Samples, Dim] + and [Samples, Batch, Dim]. + """ + def __init__(self, distribution: torch.distributions.Distribution): + self._distribution = distribution + + def _get_samples(self, sampling_fn, n: int, pattern: Literal['sbd', 'bsd'] = 'sbd'): + patterns_to_perm = { + "bsd": (1,0,2), + "sbd": (0,1,2) } - base_config = super(Independent4ParamsBeta, self).get_config() - return dict(list(base_config.items()) + list(config.items())) - + samples = sampling_fn((n,)) if isinstance(n, int) else sampling_fn(n) + return samples.permute(*patterns_to_perm[pattern]) + + def sample(self, n: int|tuple, pattern: Literal['sbd', 'bsd'] = 'sbd'): + return self._get_samples(sampling_fn=self._distribution.sample, n=n, pattern=pattern) + + def rsample(self, n: int|tuple, pattern: Literal['sbd', 'bsd'] = 'sbd'): + if not self._distribution.has_rsample: + raise MissingReparameterizationError(f"{self._distribution.__class__} does not implement rsample.") + + return self._get_samples(sampling_fn=self._distribution.rsample, n=n, pattern=pattern) + + def __str__(self): + return f"Wrapper for {self._distribution.__class__.__name__} distribution." + @property - def output(self): - """This allows the use of this layer with the shap package.""" - return super(Independent4ParamsBeta, self).output[0] - - -@tf.keras.saving.register_keras_serializable() -class IndependentDoublyCensoredNormal(tfpl.DistributionLambda): - """An independent censored normal Keras layer.""" - - def __init__( - self, - event_shape=(), - convert_to_tensor_fn=tfd.Distribution.mean, - validate_args=False, - **kwargs - ): - """Initialize the `IndependentDoublyCensoredNormal` layer. - Args: - event_shape: integer vector `Tensor` representing the shape of single - draw from this distribution. - convert_to_tensor_fn: Python `callable` that takes a `tfd.Distribution` - instance and returns a `tf.Tensor`-like object. - Default value: `tfd.Distribution.mean`. - validate_args: Python `bool`, default `False`. When `True` distribution - parameters are checked for validity despite possibly degrading runtime - performance. When `False` invalid inputs may silently render incorrect - outputs. - Default value: `False`. - **kwargs: Additional keyword arguments passed to `tf.keras.Layer`. - """ - convert_to_tensor_fn = _get_convert_to_tensor_fn(convert_to_tensor_fn) - - # If there is a 'make_distribution_fn' keyword argument (e.g., because we - # are being called from a `from_config` method), remove it. We pass the - # distribution function to `DistributionLambda.__init__` below as the first - # positional argument. - kwargs.pop("make_distribution_fn", None) - # get the clipping parameters and pop them - _clip_low = kwargs.pop("clip_low", 0.0) - _clip_high = kwargs.pop("clip_high", 1.0) - - def new_from_t(t): - return IndependentDoublyCensoredNormal.new( - t, - event_shape, - validate_args, - clip_low=_clip_low, - clip_high=_clip_high, - ) - - super(IndependentDoublyCensoredNormal, self).__init__( - new_from_t, convert_to_tensor_fn, **kwargs - ) - - self._event_shape = event_shape - self._convert_to_tensor_fn = convert_to_tensor_fn - self._validate_args = validate_args - - @staticmethod - def new( - params, - event_shape=(), - validate_args=False, - name=None, - clip_low=0.0, - clip_high=1.0, - ): - """Create the distribution instance from a `params` vector.""" - with tf.name_scope(name or "IndependentDoublyCensoredNormal"): - params = tf.convert_to_tensor(params, name="params") - event_shape = dist_util.expand_to_vector( - tf.convert_to_tensor( - event_shape, name="event_shape", dtype_hint=tf.int32 - ), - tensor_name="event_shape", - ) - output_shape = tf.concat( - [ - tf.shape(params)[:-1], - event_shape, - ], - axis=0, - ) - loc, scale = tf.split(params, 2, axis=-1) - loc = tf.reshape(loc, output_shape) - scale = tf.math.softplus(tf.reshape(scale, output_shape)) + 1e-3 - normal_dist = tfd.Normal(loc=loc, scale=scale, validate_args=validate_args) - - class CustomCensored(tfd.Distribution): - def __init__(self, normal, clip_low=0.0, clip_high=1.0): - self.normal = normal - super(CustomCensored, self).__init__( - dtype=normal.dtype, - reparameterization_type=tfd.FULLY_REPARAMETERIZED, - validate_args=validate_args, - allow_nan_stats=True, - ) - self.clip_low = clip_low - self.clip_high = clip_high - - def _sample_n(self, n, seed=None): - - # Sample from normal distribution - samples = self.normal.sample(sample_shape=(n,), seed=seed) - - # Clip values between 0 and 1 - chosen_samples = tf.clip_by_value( - samples, self.clip_low, self.clip_high - ) - - return chosen_samples - - def _mean(self): - """ - Original: X ~ N(mu, sigma) - Censored: Y = X if clip_low <= X <= clip_high else clip_low if X < clip_low else clip_high - Phi / phi: CDF / PDF of standard normal distribution - - Law of total expectations: - E[Y] = E[Y | X > c_h] * P(X > c_h) + E[Y | X < c_l] * P(X < c_l) + E[Y | c_l <= X <= c_h] * P(c_l <= X <= c_h) - = c_h * P(X > c_h) + P(X < c_l) * c_l + E[Y | c_l <= X <= c_h] * P(c_l <= X <= c_h) - = c_h * P(X > c_h) + P(X < c_l) * c_l + E[Z ~ TruncNormal(mu, sigma, c_l, c_h)] * (Phi((c_h - mu) / sigma) - Phi(c_l - mu / sigma)) - = c_h * (1 - Phi((c_h - mu) / sigma)) - + c_l * Phi((c_l - mu) / sigma) - + mu * (Phi((c_h - mu) / sigma) - Phi(c_l - mu / sigma)) - + sigma * (phi(c_l - mu / sigma) - phi((c_h - mu) / sigma)) - Ref for TruncatedNormal mean: https://en.wikipedia.org/wiki/Truncated_normal_distribution - """ - mu, sigma = self.normal.mean(), self.normal.stddev() - low_bound_standard = (self.clip_low - mu) / sigma - high_bound_standard = (self.clip_high - mu) / sigma - - cdf = lambda x: tfd.Normal(0, 1).cdf(x) - pdf = lambda x: tfd.Normal(0, 1).prob(x) - - return ( - self.clip_high * (1 - cdf(high_bound_standard)) - + self.clip_low * cdf(low_bound_standard) - + mu * (cdf(high_bound_standard) - cdf(low_bound_standard)) - + sigma * (pdf(low_bound_standard) - pdf(high_bound_standard)) - ) - - def _log_prob(self, value): - - mu, sigma = self.normal.mean(), self.normal.stddev() - cdf = lambda x: tfd.Normal(0, 1).cdf(x) - pdf = lambda x: tfd.Normal(0, 1).prob(x) - - logprob_left = lambda x: tf.math.log( - cdf(self.clip_low - mu / sigma) + 1e-3 - ) - logprob_middle = lambda x: self.normal.log_prob(x) - logprob_right = lambda x: tf.math.log( - 1 - cdf((self.clip_high - mu) / sigma) + 1e-3 - ) - - return ( - logprob_left(value) - + logprob_middle(value) - + logprob_right(value) - ) - - return independent_lib.Independent( - CustomCensored(normal_dist, clip_low=clip_low, clip_high=clip_high), - reinterpreted_batch_ndims=tf.size(event_shape), - validate_args=validate_args, - ) - - @staticmethod - def params_size(event_shape=(), name=None): - """The number of `params` needed to create a single distribution.""" - with tf.name_scope(name or "IndependentDoublyCensoredNormal_params_size"): - event_shape = tf.convert_to_tensor( - event_shape, name="event_shape", dtype_hint=tf.int32 - ) - return np.int32(2) * _event_size( - event_shape, name=name or "IndependentDoublyCensoredNormal_params_size" - ) - - def get_config(self): - """Returns the config of this layer. - NOTE: At the moment, this configuration can only be serialized if the - Layer's `convert_to_tensor_fn` is a serializable Keras object (i.e., - implements `get_config`) or one of the standard values: - - `Distribution.sample` (or `"sample"`) - - `Distribution.mean` (or `"mean"`) - - `Distribution.mode` (or `"mode"`) - - `Distribution.stddev` (or `"stddev"`) - - `Distribution.variance` (or `"variance"`) - """ - config = { - "event_shape": self._event_shape, - "convert_to_tensor_fn": _serialize(self._convert_to_tensor_fn), - "validate_args": self._validate_args, - } - base_config = super(IndependentDoublyCensoredNormal, self).get_config() - return dict(list(base_config.items()) + list(config.items())) - + def name(self): + return self._distribution.__class__.__name__ + @property - def output(self): - """This allows the use of this layer with the shap package.""" - return super(IndependentDoublyCensoredNormal, self).output[0] - - -@tf.keras.saving.register_keras_serializable() -class IndependentConcaveBeta(tfpl.DistributionLambda): - """An independent 4-parameter Beta Keras layer with enforced concavity""" - - # INdependent - def __init__( - self, - event_shape=(), - convert_to_tensor_fn=tfd.Distribution.mean, - validate_args=False, - **kwargs - ): - """Initialize the `IndependentConcaveBeta` layer. - Args: - event_shape: integer vector `Tensor` representing the shape of single - draw from this distribution. - convert_to_tensor_fn: Python `callable` that takes a `tfd.Distribution` - instance and returns a `tf.Tensor`-like object. - Default value: `tfd.Distribution.mean`. - validate_args: Python `bool`, default `False`. When `True` distribution - parameters are checked for validity despite possibly degrading runtime - performance. When `False` invalid inputs may silently render incorrect - outputs. - Default value: `False`. - **kwargs: Additional keyword arguments passed to `tf.keras.Layer`. - """ - convert_to_tensor_fn = _get_convert_to_tensor_fn(convert_to_tensor_fn) - - # If there is a 'make_distribution_fn' keyword argument (e.g., because we - # are being called from a `from_config` method), remove it. We pass the - # distribution function to `DistributionLambda.__init__` below as the first - # positional argument. - kwargs.pop("make_distribution_fn", None) - - def new_from_t(t): - return IndependentConcaveBeta.new(t, event_shape, validate_args) - - super(IndependentConcaveBeta, self).__init__( - new_from_t, convert_to_tensor_fn, **kwargs - ) - - self._event_shape = event_shape - self._convert_to_tensor_fn = convert_to_tensor_fn - self._validate_args = validate_args - - @staticmethod - def new(params, event_shape=(), validate_args=False, name=None): - """Create the distribution instance from a `params` vector.""" - with tf.name_scope(name or "IndependentConcaveBeta"): - params = tf.convert_to_tensor(params, name="params") - event_shape = dist_util.expand_to_vector( - tf.convert_to_tensor( - event_shape, name="event_shape", dtype_hint=tf.int32 - ), - tensor_name="event_shape", - ) - output_shape = tf.concat( - [ - tf.shape(params)[:-1], - event_shape, - ], - axis=0, - ) - alpha, beta, shift, scale = tf.split(params, 4, axis=-1) - # alpha > 2 and beta > 2 produce a concave downward Beta - alpha = tf.math.softplus(tf.reshape(alpha, output_shape)) + 2.0 - beta = tf.math.softplus(tf.reshape(beta, output_shape)) + 2.0 - shift = tf.math.softplus(tf.reshape(shift, output_shape)) - scale = tf.math.softplus(tf.reshape(scale, output_shape)) + 1e-3 - betad = tfd.Beta(alpha, beta, validate_args=validate_args) - transf_betad = tfd.TransformedDistribution( - distribution=betad, bijector=tfb.Shift(shift)(tfb.Scale(scale)) - ) - return independent_lib.Independent( - transf_betad, - reinterpreted_batch_ndims=tf.size(event_shape), - validate_args=validate_args, - ) - - @staticmethod - def params_size(event_shape=(), name=None): - """The number of `params` needed to create a single distribution.""" - with tf.name_scope(name or "IndependentConcaveBeta_params_size"): - event_shape = tf.convert_to_tensor( - event_shape, name="event_shape", dtype_hint=tf.int32 - ) - return np.int32(4) * _event_size( - event_shape, name=name or "IndependentConcaveBeta_params_size" - ) - - def get_config(self): - """Returns the config of this layer. - NOTE: At the moment, this configuration can only be serialized if the - Layer's `convert_to_tensor_fn` is a serializable Keras object (i.e., - implements `get_config`) or one of the standard values: - - `Distribution.sample` (or `"sample"`) - - `Distribution.mean` (or `"mean"`) - - `Distribution.mode` (or `"mode"`) - - `Distribution.stddev` (or `"stddev"`) - - `Distribution.variance` (or `"variance"`) - """ - config = { - "event_shape": self._event_shape, - "convert_to_tensor_fn": _serialize(self._convert_to_tensor_fn), - "validate_args": self._validate_args, - } - base_config = super(IndependentConcaveBeta, self).get_config() - return dict(list(base_config.items()) + list(config.items())) - + def has_rsample(self): + return self._distribution.has_rsample + +class UniveriateGaussianModule(BaseParametricDistributionModule): + ''' + Torch implementation of a Gaussian sampling layer given mean and covariance + values of shape [None, 2]. This layer uses the reparametrization trick + to allow the flow of gradients. + ''' + _name = 'IndependentNormal' + _distribution = torch.distributions.Normal # WrappingTorchDist(base_dist=torch.distributions.Normal).sample(2) + def __init__(self, **kwargs): + super(UniveriateGaussianModule, self).__init__() + self.get_positive_std = torch.nn.Softplus() + + def process_params(self, moments): + + # Create a copy of `moments` to avoid issues when using Softplus + # on tensor selections + new_moments = moments.clone() + new_moments[:, 1] = self.get_positive_std(moments[:, 1]) + + normal_dist = self._distribution(new_moments[:,0:1], new_moments[:,1:2]) + return normal_dist + + @property - def output(self): - """This allows the use of this layer with the shap package.""" - return super(IndependentConcaveBeta, self).output[0] - - -@tf.keras.saving.register_keras_serializable() -class IndependentGamma(tfpl.DistributionLambda): - """An independent gamma Keras layer.""" - - def __init__( - self, - event_shape=(), - convert_to_tensor_fn=tfd.Distribution.mean, - validate_args=False, - **kwargs - ): - """Initialize the `IndependentGamma` layer. - Args: - event_shape: integer vector `Tensor` representing the shape of single - draw from this distribution. - convert_to_tensor_fn: Python `callable` that takes a `tfd.Distribution` - instance and returns a `tf.Tensor`-like object. - Default value: `tfd.Distribution.mean`. - validate_args: Python `bool`, default `False`. When `True` distribution - parameters are checked for validity despite possibly degrading runtime - performance. When `False` invalid inputs may silently render incorrect - outputs. - Default value: `False`. - **kwargs: Additional keyword arguments passed to `tf.keras.Layer`. - """ - convert_to_tensor_fn = _get_convert_to_tensor_fn(convert_to_tensor_fn) - - # If there is a 'make_distribution_fn' keyword argument (e.g., because we - # are being called from a `from_config` method), remove it. We pass the - # distribution function to `DistributionLambda.__init__` below as the first - # positional argument. - kwargs.pop("make_distribution_fn", None) - - def new_from_t(t): - return IndependentGamma.new(t, event_shape, validate_args) - - super(IndependentGamma, self).__init__( - new_from_t, convert_to_tensor_fn, **kwargs - ) - - self._event_shape = event_shape - self._convert_to_tensor_fn = convert_to_tensor_fn - self._validate_args = validate_args - - @staticmethod - def new(params, event_shape=(), validate_args=False, name=None): - """Create the distribution instance from a `params` vector.""" - with tf.name_scope(name or "IndependentGamma"): - params = tf.convert_to_tensor(params, name="params") - event_shape = dist_util.expand_to_vector( - tf.convert_to_tensor( - event_shape, name="event_shape", dtype_hint=tf.int32 - ), - tensor_name="event_shape", - ) - output_shape = tf.concat( - [ - tf.shape(params)[:-1], - event_shape, - ], - axis=0, - ) - concentration, rate = tf.split(params, 2, axis=-1) - return independent_lib.Independent( - tfd.Gamma( - concentration=tf.math.softplus( - tf.reshape(concentration, output_shape) - ), - rate=tf.math.softplus(tf.reshape(rate, output_shape)), - validate_args=validate_args, - ), - reinterpreted_batch_ndims=tf.size(event_shape), - validate_args=validate_args, - ) - - @staticmethod - def params_size(event_shape=(), name=None): - """The number of `params` needed to create a single distribution.""" - with tf.name_scope(name or "IndependentGamma_params_size"): - event_shape = tf.convert_to_tensor( - event_shape, name="event_shape", dtype_hint=tf.int32 - ) - return np.int32(2) * _event_size( - event_shape, name=name or "IndependentGamma_params_size" - ) - - def get_config(self): - """Returns the config of this layer. - NOTE: At the moment, this configuration can only be serialized if the - Layer's `convert_to_tensor_fn` is a serializable Keras object (i.e., - implements `get_config`) or one of the standard values: - - `Distribution.sample` (or `"sample"`) - - `Distribution.mean` (or `"mean"`) - - `Distribution.mode` (or `"mode"`) - - `Distribution.stddev` (or `"stddev"`) - - `Distribution.variance` (or `"variance"`) - """ - config = { - "event_shape": self._event_shape, - "convert_to_tensor_fn": _serialize(self._convert_to_tensor_fn), - "validate_args": self._validate_args, - } - base_config = super(IndependentGamma, self).get_config() - return dict(list(base_config.items()) + list(config.items())) - + def num_parameters(self): + return 2 + @property - def output(self): - """This allows the use of this layer with the shap package.""" - return super(IndependentGamma, self).output[0] - - -@tf.keras.saving.register_keras_serializable() -class IndependentLogNormal(tfpl.DistributionLambda): - """An independent LogNormal Keras layer.""" - - def __init__( - self, - event_shape=(), - convert_to_tensor_fn=tfd.Distribution.mean, - validate_args=False, - **kwargs - ): - """Initialize the `IndependentLogNormal` layer. - Args: - event_shape: integer vector `Tensor` representing the shape of single - draw from this distribution. - convert_to_tensor_fn: Python `callable` that takes a `tfd.Distribution` - instance and returns a `tf.Tensor`-like object. - Default value: `tfd.Distribution.mean`. - validate_args: Python `bool`, default `False`. When `True` distribution - parameters are checked for validity despite possibly degrading runtime - performance. When `False` invalid inputs may silently render incorrect - outputs. - Default value: `False`. - **kwargs: Additional keyword arguments passed to `tf.keras.Layer`. - """ - convert_to_tensor_fn = _get_convert_to_tensor_fn(convert_to_tensor_fn) - - # If there is a 'make_distribution_fn' keyword argument (e.g., because we - # are being called from a `from_config` method), remove it. We pass the - # distribution function to `DistributionLambda.__init__` below as the first - # positional argument. - kwargs.pop("make_distribution_fn", None) - - def new_from_t(t): - return IndependentLogNormal.new(t, event_shape, validate_args) - - super(IndependentLogNormal, self).__init__( - new_from_t, convert_to_tensor_fn, **kwargs - ) - - self._event_shape = event_shape - self._convert_to_tensor_fn = convert_to_tensor_fn - self._validate_args = validate_args - - @staticmethod - def new(params, event_shape=(), validate_args=False, name=None): - """Create the distribution instance from a `params` vector.""" - with tf.name_scope(name or "IndependentLogNormal"): - params = tf.convert_to_tensor(params, name="params") - event_shape = dist_util.expand_to_vector( - tf.convert_to_tensor( - event_shape, name="event_shape", dtype_hint=tf.int32 - ), - tensor_name="event_shape", - ) - output_shape = tf.concat( - [ - tf.shape(params)[:-1], - event_shape, - ], - axis=0, - ) - loc, scale = tf.split(params, 2, axis=-1) - return independent_lib.Independent( - tfd.LogNormal( - loc=tf.reshape(loc, output_shape), - scale=tf.math.softplus(tf.reshape(scale, output_shape)) + 1e-3, - validate_args=validate_args, - ), - reinterpreted_batch_ndims=tf.size(event_shape), - validate_args=validate_args, - ) - - @staticmethod - def params_size(event_shape=(), name=None): - """The number of `params` needed to create a single distribution.""" - with tf.name_scope(name or "IndependentLogNormal_params_size"): - event_shape = tf.convert_to_tensor( - event_shape, name="event_shape", dtype_hint=tf.int32 - ) - return np.int32(2) * _event_size( - event_shape, name=name or "IndependentLogNormal_params_size" - ) + def name(self): + return self._name + +class MultivariateGaussianTriLModule(BaseParametricDistributionModule): + """Multivariate Gaussian ~N(mu, L) where mu = E[x] is the mean vector, and L is a lower triangular + matrix such that LL^T = Cov(x). Matrix L is only required to be a lower triagular square matrix. + Internally, the values on the diagonal will be ensured positive with a softplus. + """ + _name = 'multivariate_tril_gaussian' + _distribution = torch.distributions.MultivariateNormal + + def __init__(self, dim, **kwargs): + super(MultivariateGaussianTriLModule, self).__init__() + self.dim = dim + + def process_params(self, mean_and_tril_cov): + mean = mean_and_tril_cov[0] + tril_cov = mean_and_tril_cov[1] + + tril_cov = self._ensure_lower_cholesky(tril_cov) + + multivariate_normal = self._distribution(loc=mean, scale_tril=tril_cov) + return multivariate_normal + + + def _ensure_lower_cholesky(self, x): + """Ensures positive values on the diagonal. + The input is expected to be a lower triangular matrix. - def get_config(self): - """Returns the config of this layer. - NOTE: At the moment, this configuration can only be serialized if the - Layer's `convert_to_tensor_fn` is a serializable Keras object (i.e., - implements `get_config`) or one of the standard values: - - `Distribution.sample` (or `"sample"`) - - `Distribution.mean` (or `"mean"`) - - `Distribution.mode` (or `"mode"`) - - `Distribution.stddev` (or `"stddev"`) - - `Distribution.variance` (or `"variance"`) """ - config = { - "event_shape": self._event_shape, - "convert_to_tensor_fn": _serialize(self._convert_to_tensor_fn), - "validate_args": self._validate_args, - } - base_config = super(IndependentLogNormal, self).get_config() - return dict(list(base_config.items()) + list(config.items())) - + diag = torch.diagonal(x, dim1=-2, dim2=-1) # get diagonals + diag_fixed = torch.nn.functional.softplus(diag) # make them positive + # remove old diagonals and replace the new ones + return x - torch.diag_embed(diag) + torch.diag_embed(diag_fixed) + @property - def output(self): - """This allows the use of this layer with the shap package.""" - return super(IndependentLogNormal, self).output[0] - - -@tf.keras.saving.register_keras_serializable() -class IndependentLogitNormal(tfpl.DistributionLambda): - """An independent Logit-Normal Keras layer.""" - - def __init__( - self, - event_shape=(), - convert_to_tensor_fn=tfd.Distribution.sample, - validate_args=False, - **kwargs - ): - """Initialize the `IndependentLogitNormal` layer. - Args: - event_shape: integer vector `Tensor` representing the shape of single - draw from this distribution. - convert_to_tensor_fn: Python `callable` that takes a `tfd.Distribution` - instance and returns a `tf.Tensor`-like object. - Default value: `tfd.Distribution.mean`. - validate_args: Python `bool`, default `False`. When `True` distribution - parameters are checked for validity despite possibly degrading runtime - performance. When `False` invalid inputs may silently render incorrect - outputs. - Default value: `False`. - **kwargs: Additional keyword arguments passed to `tf.keras.Layer`. - """ - convert_to_tensor_fn = _get_convert_to_tensor_fn(convert_to_tensor_fn) - - # If there is a 'make_distribution_fn' keyword argument (e.g., because we - # are being called from a `from_config` method), remove it. We pass the - # distribution function to `DistributionLambda.__init__` below as the first - # positional argument. - kwargs.pop("make_distribution_fn", None) - - def new_from_t(t): - return IndependentLogitNormal.new(t, event_shape, validate_args) - - super(IndependentLogitNormal, self).__init__( - new_from_t, convert_to_tensor_fn, **kwargs - ) - - self._event_shape = event_shape - self._convert_to_tensor_fn = convert_to_tensor_fn - self._validate_args = validate_args - - @staticmethod - def new(params, event_shape=(), validate_args=False, name=None): - """Create the distribution instance from a `params` vector.""" - with tf.name_scope(name or "IndependentLogitNormal"): - params = tf.convert_to_tensor(params, name="params") - event_shape = dist_util.expand_to_vector( - tf.convert_to_tensor( - event_shape, name="event_shape", dtype_hint=tf.int32 - ), - tensor_name="event_shape", - ) - output_shape = tf.concat( - [ - tf.shape(params)[:-1], - event_shape, - ], - axis=0, - ) - loc, scale = tf.split(params, 2, axis=-1) - return independent_lib.Independent( - tfd.LogitNormal( - loc=tf.reshape(loc, output_shape), - scale=tf.math.softplus(tf.reshape(scale, output_shape)) + 1e-3, - validate_args=validate_args, - ), - reinterpreted_batch_ndims=tf.size(event_shape), - validate_args=validate_args, - ) - - @staticmethod - def params_size(event_shape=(), name=None): - """The number of `params` needed to create a single distribution.""" - with tf.name_scope(name or "IndependentLogitNormal_params_size"): - event_shape = tf.convert_to_tensor( - event_shape, name="event_shape", dtype_hint=tf.int32 - ) - return np.int32(2) * _event_size( - event_shape, name=name or "IndependentLogitNormal_params_size" - ) - - def get_config(self): - """Returns the config of this layer. - NOTE: At the moment, this configuration can only be serialized if the - Layer's `convert_to_tensor_fn` is a serializable Keras object (i.e., - implements `get_config`) or one of the standard values: - - `Distribution.sample` (or `"sample"`) - - `Distribution.mean` (or `"mean"`) - - `Distribution.mode` (or `"mode"`) - - `Distribution.stddev` (or `"stddev"`) - - `Distribution.variance` (or `"variance"`) - """ - config = { - "event_shape": self._event_shape, - "convert_to_tensor_fn": _serialize(self._convert_to_tensor_fn), - "validate_args": self._validate_args, - } - base_config = super(IndependentLogitNormal, self).get_config() - return dict(list(base_config.items()) + list(config.items())) - + def num_parameters(self): + return (self.dim, self.dim * (self.dim + 1) // 2) + @property - def output(self): - """This allows the use of this layer with the shap package.""" - return super(IndependentLogitNormal, self).output[0] - - -@tf.keras.saving.register_keras_serializable() -class IndependentMixtureNormal(tfpl.DistributionLambda): - """A mixture of two normal distributions Keras layer. - 5-parameters distribution: loc1, scale1, loc2, scale2, weight - """ - - def __init__( - self, - event_shape=(), - convert_to_tensor_fn=tfd.Distribution.mean, - validate_args=False, - **kwargs - ): - """Initialize the `IndependentMixtureNormal` layer. - Args: - event_shape: integer vector `Tensor` representing the shape of single - draw from this distribution. - convert_to_tensor_fn: Python `callable` that takes a `tfd.Distribution` - instance and returns a `tf.Tensor`-like object. - Default value: `tfd.Distribution.mean`. - validate_args: Python `bool`, default `False`. When `True` distribution - parameters are checked for validity despite possibly degrading runtime - performance. When `False` invalid inputs may silently render incorrect - outputs. - Default value: `False`. - **kwargs: Additional keyword arguments passed to `tf.keras.Layer`. - """ - - convert_to_tensor_fn = _get_convert_to_tensor_fn(convert_to_tensor_fn) - - # If there is a 'make_distribution_fn' keyword argument (e.g., because we - # are being called from a `from_config` method), remove it. We pass the - # distribution function to `DistributionLambda.__init__` below as the first - # positional argument. - kwargs.pop("make_distribution_fn", None) - - def new_from_t(t): - return IndependentMixtureNormal.new(t, event_shape, validate_args) - - super(IndependentMixtureNormal, self).__init__( - new_from_t, convert_to_tensor_fn, **kwargs - ) - - self._event_shape = event_shape - self._convert_to_tensor_fn = convert_to_tensor_fn - self._validate_args = validate_args - - @staticmethod - def new(params, event_shape=(), validate_args=False, name=None): - """Create the distribution instance from a `params` vector.""" - with tf.name_scope(name or "IndependentMixtureNormal"): - params = tf.convert_to_tensor(params, name="params") - - event_shape = dist_util.expand_to_vector( - tf.convert_to_tensor( - event_shape, name="event_shape", dtype_hint=tf.int32 - ), - tensor_name="event_shape", - ) - - output_shape = tf.concat( - [ - tf.shape(params)[:-1], - event_shape, - ], - axis=0, - ) - - loc1, scale1, loc2, scale2, weight = tf.split(params, 5, axis=-1) - loc1 = tf.reshape(loc1, output_shape) - scale1 = tf.math.softplus(tf.reshape(scale1, output_shape)) + 1e-3 - loc2 = tf.reshape(loc2, output_shape) - scale2 = tf.math.softplus(tf.reshape(scale2, output_shape)) + 1e-3 - weight = tf.math.sigmoid(tf.reshape(weight, output_shape)) - - # Create the component distributions - normald1 = tfd.Normal(loc=loc1, scale=scale1) - normald2 = tfd.Normal(loc=loc2, scale=scale2) - - # Create a categorical distribution for the weights - cat = tfd.Categorical( - probs=tf.concat( - [tf.expand_dims(weight, -1), tf.expand_dims(1 - weight, -1)], - axis=-1, - ) - ) - - class CustomMixture(tfd.Distribution): - def __init__(self, cat, normald1, normald2): - self.cat = cat - self.normald1 = normald1 - self.normald2 = normald2 - super(CustomMixture, self).__init__( - dtype=normald1.dtype, - reparameterization_type=tfd.FULLY_REPARAMETERIZED, - validate_args=validate_args, - allow_nan_stats=True, - ) - - def _sample_n(self, n, seed=None): - indices = self.cat.sample(sample_shape=(n,), seed=seed) - - # Sample from both truncated normal distributions - samples1 = self.normald1.sample(sample_shape=(n,), seed=seed) - samples2 = self.normald2.sample(sample_shape=(n,), seed=seed) - - # Stack the samples along a new axis - samples = tf.stack([samples1, samples2], axis=-1) - - # Gather samples according to indices from the categorical distribution - chosen_samples = tf.gather( - samples, - indices, - batch_dims=tf.get_static_value(tf.rank(indices)), - ) - - return chosen_samples - - def _log_prob(self, value): - log_prob1 = self.normald1.log_prob(value) - log_prob2 = self.normald2.log_prob(value) - log_probs = tf.stack([log_prob1, log_prob2], axis=-1) - weighted_log_probs = log_probs + tf.math.log( - tf.concat([weight, 1 - weight], axis=-1) - ) - return tf.reduce_logsumexp(weighted_log_probs, axis=-1) - - def _mean(self): - return ( - weight * self.normald1.mean() - + (1 - weight) * self.normald2.mean() - ) - - mixtured = CustomMixture(cat, normald1, normald2) - - return independent_lib.Independent( - mixtured, - reinterpreted_batch_ndims=tf.size(event_shape), - validate_args=validate_args, - ) - - @staticmethod - def params_size(event_shape=(), name=None): - """The number of `params` needed to create a single distribution.""" - with tf.name_scope(name or "IndependentMixtureNormal_params_size"): - event_shape = tf.convert_to_tensor( - event_shape, name="event_shape", dtype_hint=tf.int32 - ) - return np.int32(5) * _event_size( - event_shape, name=name or "IndependentMixtureNormal_params_size" - ) - - def get_config(self): - """Returns the config of this layer. - NOTE: At the moment, this configuration can only be serialized if the - Layer's `convert_to_tensor_fn` is a serializable Keras object (i.e., - implements `get_config`) or one of the standard values: - - `Distribution.sample` (or `"sample"`) - - `Distribution.mean` (or `"mean"`) - - `Distribution.mode` (or `"mode"`) - - `Distribution.stddev` (or `"stddev"`) - - `Distribution.variance` (or `"variance"`) - """ - config = { - "event_shape": self._event_shape, - "convert_to_tensor_fn": _serialize(self._convert_to_tensor_fn), - "validate_args": self._validate_args, - } - base_config = super(IndependentMixtureNormal, self).get_config() - return dict(list(base_config.items()) + list(config.items())) + def name(self): + return self._name + +class UnivariateTruncatedGaussianModule(BaseParametricDistributionModule): + _name = 'truncated_gaussian' + _distribution = TruncatedNormalDistribution + + def __init__(self, a, b, **kwargs): + super(UnivariateTruncatedGaussianModule, self).__init__() + self.get_positive_std = torch.nn.Softplus() + if type(a) != torch.Tensor: + a = torch.tensor(a) + if type(b) != torch.Tensor: + b = torch.tensor(b) + + self.a, self.b = a, b + + + def process_params(self, moments): + + # Create a copy of `moments` to avoid issues when using Softplus + # on tensor selections + new_moments = moments.clone() + new_moments[:, 1] = self.get_positive_std(moments[:, 1]) + + + trunc_normal_dist = self._distribution(mu_bar=new_moments[:,0:1], sigma_bar=new_moments[:,1:2], a=self.a, b=self.b) + return trunc_normal_dist @property - def output(self): - """This allows the use of this layer with the shap package.""" - return super(IndependentMixtureNormal, self).output[0] - - -@tf.keras.saving.register_keras_serializable() -class IndependentTruncatedNormal(tfpl.DistributionLambda): - """An independent TruncatedNormal Keras layer.""" - - def __init__( - self, - event_shape=(), - convert_to_tensor_fn=tfd.Distribution.mean, - validate_args=False, - **kwargs - ): - """Initialize the `IndependentTruncatedNormal` layer. - Args: - event_shape: integer vector `Tensor` representing the shape of single - draw from this distribution. - convert_to_tensor_fn: Python `callable` that takes a `tfd.Distribution` - instance and returns a `tf.Tensor`-like object. - Default value: `tfd.Distribution.mean`. - validate_args: Python `bool`, default `False`. When `True` distribution - parameters are checked for validity despite possibly degrading runtime - performance. When `False` invalid inputs may silently render incorrect - outputs. - Default value: `False`. - **kwargs: Additional keyword arguments passed to `tf.keras.Layer`. - """ - convert_to_tensor_fn = _get_convert_to_tensor_fn(convert_to_tensor_fn) - - # If there is a 'make_distribution_fn' keyword argument (e.g., because we - # are being called from a `from_config` method), remove it. We pass the - # distribution function to `DistributionLambda.__init__` below as the first - # positional argument. - kwargs.pop("make_distribution_fn", None) - - def new_from_t(t): - return IndependentTruncatedNormal.new(t, event_shape, validate_args) - - super(IndependentTruncatedNormal, self).__init__( - new_from_t, convert_to_tensor_fn, **kwargs - ) - - self._event_shape = event_shape - self._convert_to_tensor_fn = convert_to_tensor_fn - self._validate_args = validate_args - - @staticmethod - def new(params, event_shape=(), validate_args=False, name=None): - """Create the distribution instance from a `params` vector.""" - with tf.name_scope(name or "IndependentTruncatedNormal"): - params = tf.convert_to_tensor(params, name="params") - event_shape = dist_util.expand_to_vector( - tf.convert_to_tensor( - event_shape, name="event_shape", dtype_hint=tf.int32 - ), - tensor_name="event_shape", - ) - output_shape = tf.concat( - [ - tf.shape(params)[:-1], - event_shape, - ], - axis=0, - ) - loc, scale = tf.split(params, 2, axis=-1) - return independent_lib.Independent( - tfd.TruncatedNormal( - loc=tf.reshape(loc, output_shape), - scale=tf.math.softplus(tf.reshape(scale, output_shape)) + 1e-3, - low=0, - high=np.inf, - validate_args=validate_args, - ), - reinterpreted_batch_ndims=tf.size(event_shape), - validate_args=validate_args, - ) - - @staticmethod - def params_size(event_shape=(), name=None): - """The number of `params` needed to create a single distribution.""" - with tf.name_scope(name or "IndependentTruncatedNormal_params_size"): - event_shape = tf.convert_to_tensor( - event_shape, name="event_shape", dtype_hint=tf.int32 - ) - return np.int32(2) * _event_size( - event_shape, name=name or "IndependentTruncatedNormal_params_size" - ) - - def get_config(self): - """Returns the config of this layer. - NOTE: At the moment, this configuration can only be serialized if the - Layer's `convert_to_tensor_fn` is a serializable Keras object (i.e., - implements `get_config`) or one of the standard values: - - `Distribution.sample` (or `"sample"`) - - `Distribution.mean` (or `"mean"`) - - `Distribution.mode` (or `"mode"`) - - `Distribution.stddev` (or `"stddev"`) - - `Distribution.variance` (or `"variance"`) - """ - config = { - "event_shape": self._event_shape, - "convert_to_tensor_fn": _serialize(self._convert_to_tensor_fn), - "validate_args": self._validate_args, - } - base_config = super(IndependentTruncatedNormal, self).get_config() - return dict(list(base_config.items()) + list(config.items())) - + def num_parameters(self): + return 2 + @property - def output(self): - """This allows the use of this layer with the shap package.""" - return super(IndependentTruncatedNormal, self).output[0] - - -@tf.keras.saving.register_keras_serializable() -class IndependentWeibull(tfpl.DistributionLambda): - """An independent Weibull Keras layer.""" - - def __init__( - self, - event_shape=(), - convert_to_tensor_fn=tfd.Distribution.mean, - validate_args=False, - **kwargs - ): - """Initialize the `IndependentWeibull` layer. - Args: - event_shape: integer vector `Tensor` representing the shape of single - draw from this distribution. - convert_to_tensor_fn: Python `callable` that takes a `tfd.Distribution` - instance and returns a `tf.Tensor`-like object. - Default value: `tfd.Distribution.mean`. - validate_args: Python `bool`, default `False`. When `True` distribution - parameters are checked for validity despite possibly degrading runtime - performance. When `False` invalid inputs may silently render incorrect - outputs. - Default value: `False`. - **kwargs: Additional keyword arguments passed to `tf.keras.Layer`. - """ - convert_to_tensor_fn = _get_convert_to_tensor_fn(convert_to_tensor_fn) - - # If there is a 'make_distribution_fn' keyword argument (e.g., because we - # are being called from a `from_config` method), remove it. We pass the - # distribution function to `DistributionLambda.__init__` below as the first - # positional argument. - kwargs.pop("make_distribution_fn", None) - - def new_from_t(t): - return IndependentWeibull.new(t, event_shape, validate_args) - - super(IndependentWeibull, self).__init__( - new_from_t, convert_to_tensor_fn, **kwargs - ) - - self._event_shape = event_shape - self._convert_to_tensor_fn = convert_to_tensor_fn - self._validate_args = validate_args - - @staticmethod - def new(params, event_shape=(), validate_args=False, name=None): - """Create the distribution instance from a `params` vector.""" - with tf.name_scope(name or "IndependentWeibull"): - params = tf.convert_to_tensor(params, name="params") - event_shape = dist_util.expand_to_vector( - tf.convert_to_tensor( - event_shape, name="event_shape", dtype_hint=tf.int32 - ), - tensor_name="event_shape", - ) - output_shape = tf.concat( - [ - tf.shape(params)[:-1], - event_shape, - ], - axis=0, - ) - concentration, scale = tf.split(params, 2, axis=-1) - return independent_lib.Independent( - tfd.Weibull( - concentration=tf.math.softplus( - tf.reshape(concentration, output_shape) - ) - + 1.0, - scale=tf.math.softplus(tf.reshape(scale, output_shape)), - validate_args=validate_args, - ), - reinterpreted_batch_ndims=tf.size(event_shape), - validate_args=validate_args, - ) - - @staticmethod - def params_size(event_shape=(), name=None): - """The number of `params` needed to create a single distribution.""" - with tf.name_scope(name or "IndependentWeibull_params_size"): - event_shape = tf.convert_to_tensor( - event_shape, name="event_shape", dtype_hint=tf.int32 - ) - return np.int32(2) * _event_size( - event_shape, name=name or "IndependentWeibull_params_size" - ) - - def get_config(self): - """Returns the config of this layer. - NOTE: At the moment, this configuration can only be serialized if the - Layer's `convert_to_tensor_fn` is a serializable Keras object (i.e., - implements `get_config`) or one of the standard values: - - `Distribution.sample` (or `"sample"`) - - `Distribution.mean` (or `"mean"`) - - `Distribution.mode` (or `"mode"`) - - `Distribution.stddev` (or `"stddev"`) - - `Distribution.variance` (or `"variance"`) - """ - config = { - "event_shape": self._event_shape, - "convert_to_tensor_fn": _serialize(self._convert_to_tensor_fn), - "validate_args": self._validate_args, - } - base_config = super(IndependentWeibull, self).get_config() - return dict(list(base_config.items()) + list(config.items())) + def name(self): + return self._name + +class UnivariateCensoredGaussianModule(BaseParametricDistributionModule): + _name = 'censored_gaussian' + _distribution = CensoredNormalDistribution + def __init__(self, a: torch.Tensor,b: torch.Tensor, **kwargs): + super(UnivariateCensoredGaussianModule, self).__init__() + self.get_positive_std = torch.nn.Softplus() + if type(a) != torch.Tensor: + a = torch.tensor(a) + if type(b) != torch.Tensor: + b = torch.tensor(b) + self.a, self.b = a, b + + def process_params(self, moments): + new_moments = moments.clone() + new_moments[:, 1] = self.get_positive_std(moments[:, 1]) + + censored_normal_dist = self._distribution(mu_bar=new_moments[:,0:1], sigma_bar=new_moments[:,1:2], a=self.a, b=self.b) + return censored_normal_dist @property - def output(self): - """This allows the use of this layer with the shap package.""" - return super(IndependentWeibull, self).output[0] - - -@tf.keras.saving.register_keras_serializable() -class MultivariateNormalDiag(tfpl.DistributionLambda): - """A `d`-variate normal Keras layer from `2* d` params, - with a diagonal scale matrix. + def num_parameters(self): + return 2 + + @property + def name(self): + return self._name + +class UnivariateLogNormalModule(BaseParametricDistributionModule): """ - - def __init__( - self, - event_size, - convert_to_tensor_fn=tfd.Distribution.mean, - validate_args=False, - **kwargs - ): - """Initialize the layer. - Args: - event_size: Scalar `int` representing the size of single draw from this - distribution. - convert_to_tensor_fn: Python `callable` that takes a `tfd.Distribution` - instance and returns a `tf.Tensor`-like object. For examples, see - `class` docstring. - Default value: `tfd.Distribution.sample`. - validate_args: Python `bool`, default `False`. When `True` distribution - parameters are checked for validity despite possibly degrading runtime - performance. When `False` invalid inputs may silently render incorrect - outputs. - Default value: `False`. - **kwargs: Additional keyword arguments passed to `tf.keras.Layer`. - """ - convert_to_tensor_fn = _get_convert_to_tensor_fn(convert_to_tensor_fn) - - # If there is a 'make_distribution_fn' keyword argument (e.g., because we - # are being called from a `from_config` method), remove it. We pass the - # distribution function to `DistributionLambda.__init__` below as the first - # positional argument. - kwargs.pop("make_distribution_fn", None) - - def new_from_t(t): - return MultivariateNormalDiag.new(t, event_size, validate_args) - - super(MultivariateNormalDiag, self).__init__( - new_from_t, convert_to_tensor_fn, **kwargs - ) - - self._event_size = event_size - self._convert_to_tensor_fn = convert_to_tensor_fn - self._validate_args = validate_args - - @staticmethod - def new(params, event_size, validate_args=False, name=None): - """Create the distribution instance from a 'params' vector.""" - with tf.name_scope(name or "MultivariateNormalDiag"): - params = tf.convert_to_tensor(params, name="params") - if event_size > 1: - dist = tfd.MultivariateNormalDiag( - loc=params[..., :event_size], - scale_diag=1e-5 + tf.math.softplus(params[..., event_size:]), - validate_args=validate_args, - ) - else: - dist = tfd.Normal( - loc=params[..., :event_size], - scale=1e-5 + tf.math.softplus(params[..., event_size:]), - validate_args=validate_args, - ) + Module implementing Y such that + X ~ Normal(loc, scale) + Y = exp(X) ~ LogNormal(loc, scale) + """ + _name = 'log_gaussian' + _distribution = torch.distributions.LogNormal + + def __init__(self, **kwargs): + super(UnivariateLogNormalModule, self).__init__() + self.get_positive_std = torch.nn.Softplus() + + + def process_params(self, moments): + new_moments = moments.clone() + new_moments[:, 1] = self.get_positive_std(moments[:, 1]) + + log_normal_dist = self._distribution(new_moments[:,0:1], new_moments[:,1:2]) + return log_normal_dist + + @property + def num_parameters(self): + return 2 + + @property + def name(self): + return self._name + +class WeibullModule(BaseParametricDistributionModule): + """ + Toch implementation of a 2-parameters Weibull distribution. + """ + _name = 'weibull' + _distribution = torch.distributions.Weibull + def __init__(self, **kwargs): + super(WeibullModule, self).__init__() + self.get_positive_params = torch.nn.Softplus() + + def process_params(self, params): + params = self.get_positive_params(params) + + weibull_dist = self._distribution(scale=params[:,0:1], + concentration=params[:,1:2]) + return weibull_dist + + @property + def num_parameters(self): + return 2 + + @property + def name(self): + return self._name + + +class ExponentialModule(BaseParametricDistributionModule): + _name = 'exponential' + _distribution = torch.distributions.Exponential + def __init__(self, **kwargs): + super(ExponentialModule, self).__init__() + self.get_positive_lambda = torch.nn.Softplus() + + def process_params(self, params): + params = self.get_positive_lambda(params) + + # return torch.distributions.Exponential(rate=params) + exp_dist = self._distribution(rate=params) + return exp_dist + + @property + def num_parameters(self): + return 1 + + @property + def name(self): + return self._name + +class BetaModule(BaseParametricDistributionModule): + _name = 'beta' + _distribution = torch.distributions.Beta + + def __init__(self, **kwargs): + super(BetaModule, self).__init__() + + self.get_positive_concentrations = torch.nn.Softplus() + + def process_params(self, params): + params = self.get_positive_concentrations(params) + + beta_dist = self._distribution(concentration1=params[:,0:1], # c1 = alpha + concentration0=params[:,1:2]) # c0 = beta + + return beta_dist + + @property + def num_parameters(self): + return 2 + + @property + def name(self): + return self._name + + +class GammaModule(BaseParametricDistributionModule): + + _name = 'gamma' + _distribution = torch.distributions.Gamma + def __init__(self, **kwargs): + super(GammaModule, self).__init__() + self.get_positive_params = torch.nn.Softplus() + + def process_params(self, params): + params = self.get_positive_params(params) + + gamma_dist = self._distribution(concentration=params[:,0:1], # alpha + rate=params[:,1:2]) # beta or 1/scale + + return gamma_dist + + @property + def num_parameters(self): + return 2 + + @property + def name(self): + return self._name + +@keras.saving.register_keras_serializable() +class BaseDistributionLayer(Layer): + ''' + Keras layer implementing a sampling layer with reparametrization + trick, based on an underlying parametric distribution. + This layer is responsible of preparing the shape of the input to the probabilistic layer + to match the number of parameters. It does not assume anything about their properties as + it merely applies a linear layer. The underlying probabilistic layer needs to take care + of parameter constraints, e.g, the positiveness of the parameters. + ''' + def __init__(self, distribution: BaseParametricDistributionModule, + num_samples: int=21, + bias_init = 'zeros', + **kwargs): + super(BaseDistributionLayer, self).__init__(**kwargs) + + self.prob_layer = TorchModuleWrapper(distribution, name=distribution.name) + self.num_dist_params = distribution.num_parameters + + if isinstance(bias_init, np.ndarray): + bias_init = np.hstack( + [bias_init, [0.0] * (distribution.num_parameters - bias_init.shape[0])] + ) + bias_init = initializers.Constant(bias_init) + self.bias_init = bias_init + # linear layer to map any input size into the number of parameters of the underlying distribution. + self.num_samples=num_samples + self.is_multivariate_gaussian = isinstance(distribution, MultivariateGaussianTriLModule) + + def build(self, input_shape): + if not self.is_multivariate_gaussian: + self.parameters_encoder = Dense(self.num_dist_params, name='parameters_encoder', bias_initializer=self.bias_init) + else: + self.parameters_encoder = MeanAndTriLCovLayer(d1=self.prob_layer.module.num_parameters[0]) + super().build(input_shape) + + def call(self, inputs, output_type: Literal["distribution", "samples"]='distribution', training=None, num_samples=1, pattern: Literal['sbd', 'bsd'] = 'sbd', reparametrized=False): + predicted_parameters = self.parameters_encoder(inputs) + if output_type == 'distribution': + dist = self.prob_layer(predicted_parameters, num_samples=0, return_dist=True, pattern=pattern, reparametrized=reparametrized) return dist + elif output_type == 'samples': + if training and not self.prob_layer.module.has_rsample: + raise MissingReparameterizationError(f"Gradient-based optimization will not work, as the underlying {self.prob_layer.module._distribution.__name__} distribution does not have a reparametrized sampling function.") + samples = self.prob_layer(predicted_parameters, num_samples=num_samples, return_dist=False, pattern=pattern, reparametrized=reparametrized) + return samples - @staticmethod - def params_size(event_size, name=None): - """The number of 'params' needed to create a single distribution.""" - with tf.name_scope(name or "MultivariateNormalDiag_params_size"): - return 2 * event_size + def compute_output_shape(self, input_shape): + return (input_shape[0], self.num_samples, 1) def get_config(self): - """Returns the config of this layer. - NOTE: At the moment, this configuration can only be serialized if the - Layer's `convert_to_tensor_fn` is a serializable Keras object (i.e., - implements `get_config`) or one of the standard values: - - `Distribution.sample` (or `"sample"`) - - `Distribution.mean` (or `"mean"`) - - `Distribution.mode` (or `"mode"`) - - `Distribution.stddev` (or `"stddev"`) - - `Distribution.variance` (or `"variance"`) - """ - config = { - "event_size": self._event_size, - "convert_to_tensor_fn": _serialize(self._convert_to_tensor_fn), - "validate_args": self._validate_args, - } - base_config = super(MultivariateNormalDiag, self).get_config() - return dict(list(base_config.items()) + list(config.items())) - - @property - def output(self): - """This allows the use of this layer with the shap package.""" - return super(MultivariateNormalDiag, self).output[0] - - -@tf.keras.saving.register_keras_serializable() -class MultivariateNormalTriL(tfpl.MultivariateNormalTriL): - def __init__( - self, - event_size, - convert_to_tensor_fn=tfd.Distribution.mean, - validate_args=False, - **kwargs - ): - convert_to_tensor_fn = _get_convert_to_tensor_fn(convert_to_tensor_fn) - - # If there is a 'make_distribution_fn' keyword argument (e.g., because we - # are being called from a `from_config` method), remove it. We pass the - # distribution function to `DistributionLambda.__init__` below as the first - # positional argument. - kwargs.pop("make_distribution_fn", None) - - super().__init__(event_size, convert_to_tensor_fn, validate_args, **kwargs) - self._event_size = event_size - self._convert_to_tensor_fn = convert_to_tensor_fn - self._validate_args = validate_args - - def get_config(self): - """Returns the config of this layer. - NOTE: At the moment, this configuration can only be serialized if the - Layer's `convert_to_tensor_fn` is a serializable Keras object (i.e., - implements `get_config`) or one of the standard values: - - `Distribution.sample` (or `"sample"`) - - `Distribution.mean` (or `"mean"`) - - `Distribution.mode` (or `"mode"`) - - `Distribution.stddev` (or `"stddev"`) - - `Distribution.variance` (or `"variance"`) - """ - config = { - "event_size": self._event_size, - "convert_to_tensor_fn": _serialize(self._convert_to_tensor_fn), - "validate_args": self._validate_args, - } - base_config = super(MultivariateNormalTriL, self).get_config() - return dict(list(base_config.items()) + list(config.items())) - - @property - def output(self): - """This allows the use of this layer with the shap package.""" - return super(MultivariateNormalTriL, self).output[0] + config = super(BaseDistributionLayer, self).get_config() + return config + + @classmethod + def from_config(cls, config): + return cls(**config) + + # @property + # def scoringrules_param_order(self): + # key = self.prob_layer.name + + # _param_orders = { + # 'univariate_gaussian': ['loc', 'scale'], + # 'exponential': ['rate'] + # } + + # return _param_orders[key] + + + + + +distribution_to_layer = {obj[1]._name: obj[1] for obj in getmembers(sys.modules[__name__], isclass) + if issubclass(obj[1], BaseParametricDistributionModule) and obj[0] != 'BaseParametricDistributionModule'} + + + + + +# """In this module, any custom built keras layers are included.""" + +# import numpy as np +# import tensorflow as tf +# import tensorflow_probability.python.layers as tfpl +# from tensorflow_probability.python import bijectors as tfb +# from tensorflow_probability.python import distributions as tfd +# from tensorflow_probability.python.layers.distribution_layer import ( +# _event_size, +# _get_convert_to_tensor_fn, +# _serialize, +# dist_util, +# independent_lib, +# ) + + +# # these almost work out of the box +# from tensorflow_probability.python.layers import ( +# IndependentNormal, +# IndependentLogistic, +# IndependentBernoulli, +# IndependentPoisson, +# ) + + +# @tf.keras.saving.register_keras_serializable() +# class IndependentNormal(IndependentNormal): +# @property +# def output(self): # this is necessary to use the layer within shap +# return super().output[0] + + +# @tf.keras.saving.register_keras_serializable() +# class IndependentLogistic(IndependentLogistic): +# @property +# def output(self): +# return super().output[0] + + +# @tf.keras.saving.register_keras_serializable() +# class IndependentBernoulli(IndependentBernoulli): +# @property +# def output(self): +# return super().output[0] + + +# @tf.keras.saving.register_keras_serializable() +# class IndependentPoisson(IndependentPoisson): +# @property +# def output(self): +# return super().output[0] + + +# @tf.keras.saving.register_keras_serializable() +# class IndependentBeta(tfpl.DistributionLambda): +# """An independent 2-parameter Beta Keras layer""" + +# def __init__( +# self, +# event_shape=(), +# convert_to_tensor_fn=tfd.Distribution.mean, +# validate_args=False, +# **kwargs +# ): +# """Initialize the `IndependentBeta` layer. +# Args: +# event_shape: integer vector `Tensor` representing the shape of single +# draw from this distribution. +# convert_to_tensor_fn: Python `callable` that takes a `tfd.Distribution` +# instance and returns a `tf.Tensor`-like object. +# Default value: `tfd.Distribution.mean`. +# validate_args: Python `bool`, default `False`. When `True` distribution +# parameters are checked for validity despite possibly degrading runtime +# performance. When `False` invalid inputs may silently render incorrect +# outputs. +# Default value: `False`. +# **kwargs: Additional keyword arguments passed to `tf.keras.Layer`. +# """ +# convert_to_tensor_fn = _get_convert_to_tensor_fn(convert_to_tensor_fn) + +# # If there is a 'make_distribution_fn' keyword argument (e.g., because we +# # are being called from a `from_config` method), remove it. We pass the +# # distribution function to `DistributionLambda.__init__` below as the first +# # positional argument. +# kwargs.pop("make_distribution_fn", None) + +# def new_from_t(t): +# return IndependentBeta.new(t, event_shape, validate_args) + +# super(IndependentBeta, self).__init__( +# new_from_t, convert_to_tensor_fn, **kwargs +# ) + +# self._event_shape = event_shape +# self._convert_to_tensor_fn = convert_to_tensor_fn +# self._validate_args = validate_args + +# @staticmethod +# def new(params, event_shape=(), validate_args=False, name=None): +# """Create the distribution instance from a `params` vector.""" +# with tf.name_scope(name or "IndependentBeta"): +# params = tf.convert_to_tensor(params, name="params") +# event_shape = dist_util.expand_to_vector( +# tf.convert_to_tensor( +# event_shape, name="event_shape", dtype_hint=tf.int32 +# ), +# tensor_name="event_shape", +# ) +# output_shape = tf.concat( +# [ +# tf.shape(params)[:-1], +# event_shape, +# ], +# axis=0, +# ) +# alpha, beta = tf.split(params, 2, axis=-1) + +# alpha = tf.math.softplus(tf.reshape(alpha, output_shape)) + 1e-3 +# beta = tf.math.softplus(tf.reshape(beta, output_shape)) + 1e-3 +# betad = tfd.Beta(alpha, beta, validate_args=validate_args) + +# return independent_lib.Independent( +# betad, +# reinterpreted_batch_ndims=tf.size(event_shape), +# validate_args=validate_args, +# ) + +# @staticmethod +# def params_size(event_shape=(), name=None): +# """The number of `params` needed to create a single distribution.""" +# with tf.name_scope(name or "IndependentBeta_params_size"): +# event_shape = tf.convert_to_tensor( +# event_shape, name="event_shape", dtype_hint=tf.int32 +# ) +# return np.int32(2) * _event_size( +# event_shape, name=name or "IndependentBeta_params_size" +# ) + +# def get_config(self): +# """Returns the config of this layer. +# NOTE: At the moment, this configuration can only be serialized if the +# Layer's `convert_to_tensor_fn` is a serializable Keras object (i.e., +# implements `get_config`) or one of the standard values: +# - `Distribution.sample` (or `"sample"`) +# - `Distribution.mean` (or `"mean"`) +# - `Distribution.mode` (or `"mode"`) +# - `Distribution.stddev` (or `"stddev"`) +# - `Distribution.variance` (or `"variance"`) +# """ +# config = { +# "event_shape": self._event_shape, +# "convert_to_tensor_fn": _serialize(self._convert_to_tensor_fn), +# "validate_args": self._validate_args, +# } +# base_config = super(IndependentBeta, self).get_config() +# return dict(list(base_config.items()) + list(config.items())) + +# @property +# def output(self): +# """This allows the use of this layer with the shap package.""" +# return super(IndependentBeta, self).output[0] + + +# @tf.keras.saving.register_keras_serializable() +# class Independent4ParamsBeta(tfpl.DistributionLambda): +# """An independent 4-parameter Beta Keras layer allowing control over scale as well as a 'shift' parameter.""" + +# def __init__( +# self, +# event_shape=(), +# convert_to_tensor_fn=tfd.Distribution.mean, +# validate_args=False, +# **kwargs +# ): +# """Initialize the `Independent4ParamsBeta` layer. +# Args: +# event_shape: integer vector `Tensor` representing the shape of single +# draw from this distribution. +# convert_to_tensor_fn: Python `callable` that takes a `tfd.Distribution` +# instance and returns a `tf.Tensor`-like object. +# Default value: `tfd.Distribution.mean`. +# validate_args: Python `bool`, default `False`. When `True` distribution +# parameters are checked for validity despite possibly degrading runtime +# performance. When `False` invalid inputs may silently render incorrect +# outputs. +# Default value: `False`. +# **kwargs: Additional keyword arguments passed to `tf.keras.Layer`. +# """ +# convert_to_tensor_fn = _get_convert_to_tensor_fn(convert_to_tensor_fn) + +# # If there is a 'make_distribution_fn' keyword argument (e.g., because we +# # are being called from a `from_config` method), remove it. We pass the +# # distribution function to `DistributionLambda.__init__` below as the first +# # positional argument. +# kwargs.pop("make_distribution_fn", None) + +# def new_from_t(t): +# return Independent4ParamsBeta.new(t, event_shape, validate_args) + +# super(Independent4ParamsBeta, self).__init__( +# new_from_t, convert_to_tensor_fn, **kwargs +# ) + +# self._event_shape = event_shape +# self._convert_to_tensor_fn = convert_to_tensor_fn +# self._validate_args = validate_args + +# @staticmethod +# def new(params, event_shape=(), validate_args=False, name=None): +# """Create the distribution instance from a `params` vector.""" +# with tf.name_scope(name or "Independent4ParamsBeta"): +# params = tf.convert_to_tensor(params, name="params") +# event_shape = dist_util.expand_to_vector( +# tf.convert_to_tensor( +# event_shape, name="event_shape", dtype_hint=tf.int32 +# ), +# tensor_name="event_shape", +# ) +# output_shape = tf.concat( +# [ +# tf.shape(params)[:-1], +# event_shape, +# ], +# axis=0, +# ) +# alpha, beta, shift, scale = tf.split(params, 4, axis=-1) +# # alpha > 2 and beta > 2 produce a concave downward Beta +# alpha = tf.math.softplus(tf.reshape(alpha, output_shape)) + 1e-3 +# beta = tf.math.softplus(tf.reshape(beta, output_shape)) + 1e-3 +# shift = tf.math.softplus(tf.reshape(shift, output_shape)) +# scale = tf.math.softplus(tf.reshape(scale, output_shape)) + 1e-3 +# betad = tfd.Beta(alpha, beta, validate_args=validate_args) +# transf_betad = tfd.TransformedDistribution( +# distribution=betad, bijector=tfb.Shift(shift)(tfb.Scale(scale)) +# ) +# return independent_lib.Independent( +# transf_betad, +# reinterpreted_batch_ndims=tf.size(event_shape), +# validate_args=validate_args, +# ) + +# @staticmethod +# def params_size(event_shape=(), name=None): +# """The number of `params` needed to create a single distribution.""" +# with tf.name_scope(name or "Independent4ParamsBeta_params_size"): +# event_shape = tf.convert_to_tensor( +# event_shape, name="event_shape", dtype_hint=tf.int32 +# ) +# return np.int32(4) * _event_size( +# event_shape, name=name or "Independent4ParamsBeta_params_size" +# ) + +# def get_config(self): +# """Returns the config of this layer. +# NOTE: At the moment, this configuration can only be serialized if the +# Layer's `convert_to_tensor_fn` is a serializable Keras object (i.e., +# implements `get_config`) or one of the standard values: +# - `Distribution.sample` (or `"sample"`) +# - `Distribution.mean` (or `"mean"`) +# - `Distribution.mode` (or `"mode"`) +# - `Distribution.stddev` (or `"stddev"`) +# - `Distribution.variance` (or `"variance"`) +# """ +# config = { +# "event_shape": self._event_shape, +# "convert_to_tensor_fn": _serialize(self._convert_to_tensor_fn), +# "validate_args": self._validate_args, +# } +# base_config = super(Independent4ParamsBeta, self).get_config() +# return dict(list(base_config.items()) + list(config.items())) + +# @property +# def output(self): +# """This allows the use of this layer with the shap package.""" +# return super(Independent4ParamsBeta, self).output[0] + + +# @tf.keras.saving.register_keras_serializable() +# class IndependentDoublyCensoredNormal(tfpl.DistributionLambda): +# """An independent censored normal Keras layer.""" + +# def __init__( +# self, +# event_shape=(), +# convert_to_tensor_fn=tfd.Distribution.mean, +# validate_args=False, +# **kwargs +# ): +# """Initialize the `IndependentDoublyCensoredNormal` layer. +# Args: +# event_shape: integer vector `Tensor` representing the shape of single +# draw from this distribution. +# convert_to_tensor_fn: Python `callable` that takes a `tfd.Distribution` +# instance and returns a `tf.Tensor`-like object. +# Default value: `tfd.Distribution.mean`. +# validate_args: Python `bool`, default `False`. When `True` distribution +# parameters are checked for validity despite possibly degrading runtime +# performance. When `False` invalid inputs may silently render incorrect +# outputs. +# Default value: `False`. +# **kwargs: Additional keyword arguments passed to `tf.keras.Layer`. +# """ +# convert_to_tensor_fn = _get_convert_to_tensor_fn(convert_to_tensor_fn) + +# # If there is a 'make_distribution_fn' keyword argument (e.g., because we +# # are being called from a `from_config` method), remove it. We pass the +# # distribution function to `DistributionLambda.__init__` below as the first +# # positional argument. +# kwargs.pop("make_distribution_fn", None) +# # get the clipping parameters and pop them +# _clip_low = kwargs.pop("clip_low", 0.0) +# _clip_high = kwargs.pop("clip_high", 1.0) + +# def new_from_t(t): +# return IndependentDoublyCensoredNormal.new( +# t, +# event_shape, +# validate_args, +# clip_low=_clip_low, +# clip_high=_clip_high, +# ) + +# super(IndependentDoublyCensoredNormal, self).__init__( +# new_from_t, convert_to_tensor_fn, **kwargs +# ) + +# self._event_shape = event_shape +# self._convert_to_tensor_fn = convert_to_tensor_fn +# self._validate_args = validate_args + +# @staticmethod +# def new( +# params, +# event_shape=(), +# validate_args=False, +# name=None, +# clip_low=0.0, +# clip_high=1.0, +# ): +# """Create the distribution instance from a `params` vector.""" +# with tf.name_scope(name or "IndependentDoublyCensoredNormal"): +# params = tf.convert_to_tensor(params, name="params") +# event_shape = dist_util.expand_to_vector( +# tf.convert_to_tensor( +# event_shape, name="event_shape", dtype_hint=tf.int32 +# ), +# tensor_name="event_shape", +# ) +# output_shape = tf.concat( +# [ +# tf.shape(params)[:-1], +# event_shape, +# ], +# axis=0, +# ) +# loc, scale = tf.split(params, 2, axis=-1) +# loc = tf.reshape(loc, output_shape) +# scale = tf.math.softplus(tf.reshape(scale, output_shape)) + 1e-3 +# normal_dist = tfd.Normal(loc=loc, scale=scale, validate_args=validate_args) + +# class CustomCensored(tfd.Distribution): +# def __init__(self, normal, clip_low=0.0, clip_high=1.0): +# self.normal = normal +# super(CustomCensored, self).__init__( +# dtype=normal.dtype, +# reparameterization_type=tfd.FULLY_REPARAMETERIZED, +# validate_args=validate_args, +# allow_nan_stats=True, +# ) +# self.clip_low = clip_low +# self.clip_high = clip_high + +# def _sample_n(self, n, seed=None): + +# # Sample from normal distribution +# samples = self.normal.sample(sample_shape=(n,), seed=seed) + +# # Clip values between 0 and 1 +# chosen_samples = tf.clip_by_value( +# samples, self.clip_low, self.clip_high +# ) + +# return chosen_samples + +# def _mean(self): +# """ +# Original: X ~ N(mu, sigma) +# Censored: Y = X if clip_low <= X <= clip_high else clip_low if X < clip_low else clip_high +# Phi / phi: CDF / PDF of standard normal distribution + +# Law of total expectations: +# E[Y] = E[Y | X > c_h] * P(X > c_h) + E[Y | X < c_l] * P(X < c_l) + E[Y | c_l <= X <= c_h] * P(c_l <= X <= c_h) +# = c_h * P(X > c_h) + P(X < c_l) * c_l + E[Y | c_l <= X <= c_h] * P(c_l <= X <= c_h) +# = c_h * P(X > c_h) + P(X < c_l) * c_l + E[Z ~ TruncNormal(mu, sigma, c_l, c_h)] * (Phi((c_h - mu) / sigma) - Phi(c_l - mu / sigma)) +# = c_h * (1 - Phi((c_h - mu) / sigma)) +# + c_l * Phi((c_l - mu) / sigma) +# + mu * (Phi((c_h - mu) / sigma) - Phi(c_l - mu / sigma)) +# + sigma * (phi(c_l - mu / sigma) - phi((c_h - mu) / sigma)) +# Ref for TruncatedNormal mean: https://en.wikipedia.org/wiki/Truncated_normal_distribution +# """ +# mu, sigma = self.normal.mean(), self.normal.stddev() +# low_bound_standard = (self.clip_low - mu) / sigma +# high_bound_standard = (self.clip_high - mu) / sigma + +# cdf = lambda x: tfd.Normal(0, 1).cdf(x) +# pdf = lambda x: tfd.Normal(0, 1).prob(x) + +# return ( +# self.clip_high * (1 - cdf(high_bound_standard)) +# + self.clip_low * cdf(low_bound_standard) +# + mu * (cdf(high_bound_standard) - cdf(low_bound_standard)) +# + sigma * (pdf(low_bound_standard) - pdf(high_bound_standard)) +# ) + +# def _log_prob(self, value): + +# mu, sigma = self.normal.mean(), self.normal.stddev() +# cdf = lambda x: tfd.Normal(0, 1).cdf(x) +# pdf = lambda x: tfd.Normal(0, 1).prob(x) + +# logprob_left = lambda x: tf.math.log( +# cdf(self.clip_low - mu / sigma) + 1e-3 +# ) +# logprob_middle = lambda x: self.normal.log_prob(x) +# logprob_right = lambda x: tf.math.log( +# 1 - cdf((self.clip_high - mu) / sigma) + 1e-3 +# ) + +# return ( +# logprob_left(value) +# + logprob_middle(value) +# + logprob_right(value) +# ) + +# return independent_lib.Independent( +# CustomCensored(normal_dist, clip_low=clip_low, clip_high=clip_high), +# reinterpreted_batch_ndims=tf.size(event_shape), +# validate_args=validate_args, +# ) + +# @staticmethod +# def params_size(event_shape=(), name=None): +# """The number of `params` needed to create a single distribution.""" +# with tf.name_scope(name or "IndependentDoublyCensoredNormal_params_size"): +# event_shape = tf.convert_to_tensor( +# event_shape, name="event_shape", dtype_hint=tf.int32 +# ) +# return np.int32(2) * _event_size( +# event_shape, name=name or "IndependentDoublyCensoredNormal_params_size" +# ) + +# def get_config(self): +# """Returns the config of this layer. +# NOTE: At the moment, this configuration can only be serialized if the +# Layer's `convert_to_tensor_fn` is a serializable Keras object (i.e., +# implements `get_config`) or one of the standard values: +# - `Distribution.sample` (or `"sample"`) +# - `Distribution.mean` (or `"mean"`) +# - `Distribution.mode` (or `"mode"`) +# - `Distribution.stddev` (or `"stddev"`) +# - `Distribution.variance` (or `"variance"`) +# """ +# config = { +# "event_shape": self._event_shape, +# "convert_to_tensor_fn": _serialize(self._convert_to_tensor_fn), +# "validate_args": self._validate_args, +# } +# base_config = super(IndependentDoublyCensoredNormal, self).get_config() +# return dict(list(base_config.items()) + list(config.items())) + +# @property +# def output(self): +# """This allows the use of this layer with the shap package.""" +# return super(IndependentDoublyCensoredNormal, self).output[0] + + +# @tf.keras.saving.register_keras_serializable() +# class IndependentConcaveBeta(tfpl.DistributionLambda): +# """An independent 4-parameter Beta Keras layer with enforced concavity""" + +# # INdependent +# def __init__( +# self, +# event_shape=(), +# convert_to_tensor_fn=tfd.Distribution.mean, +# validate_args=False, +# **kwargs +# ): +# """Initialize the `IndependentConcaveBeta` layer. +# Args: +# event_shape: integer vector `Tensor` representing the shape of single +# draw from this distribution. +# convert_to_tensor_fn: Python `callable` that takes a `tfd.Distribution` +# instance and returns a `tf.Tensor`-like object. +# Default value: `tfd.Distribution.mean`. +# validate_args: Python `bool`, default `False`. When `True` distribution +# parameters are checked for validity despite possibly degrading runtime +# performance. When `False` invalid inputs may silently render incorrect +# outputs. +# Default value: `False`. +# **kwargs: Additional keyword arguments passed to `tf.keras.Layer`. +# """ +# convert_to_tensor_fn = _get_convert_to_tensor_fn(convert_to_tensor_fn) + +# # If there is a 'make_distribution_fn' keyword argument (e.g., because we +# # are being called from a `from_config` method), remove it. We pass the +# # distribution function to `DistributionLambda.__init__` below as the first +# # positional argument. +# kwargs.pop("make_distribution_fn", None) + +# def new_from_t(t): +# return IndependentConcaveBeta.new(t, event_shape, validate_args) + +# super(IndependentConcaveBeta, self).__init__( +# new_from_t, convert_to_tensor_fn, **kwargs +# ) + +# self._event_shape = event_shape +# self._convert_to_tensor_fn = convert_to_tensor_fn +# self._validate_args = validate_args + +# @staticmethod +# def new(params, event_shape=(), validate_args=False, name=None): +# """Create the distribution instance from a `params` vector.""" +# with tf.name_scope(name or "IndependentConcaveBeta"): +# params = tf.convert_to_tensor(params, name="params") +# event_shape = dist_util.expand_to_vector( +# tf.convert_to_tensor( +# event_shape, name="event_shape", dtype_hint=tf.int32 +# ), +# tensor_name="event_shape", +# ) +# output_shape = tf.concat( +# [ +# tf.shape(params)[:-1], +# event_shape, +# ], +# axis=0, +# ) +# alpha, beta, shift, scale = tf.split(params, 4, axis=-1) +# # alpha > 2 and beta > 2 produce a concave downward Beta +# alpha = tf.math.softplus(tf.reshape(alpha, output_shape)) + 2.0 +# beta = tf.math.softplus(tf.reshape(beta, output_shape)) + 2.0 +# shift = tf.math.softplus(tf.reshape(shift, output_shape)) +# scale = tf.math.softplus(tf.reshape(scale, output_shape)) + 1e-3 +# betad = tfd.Beta(alpha, beta, validate_args=validate_args) +# transf_betad = tfd.TransformedDistribution( +# distribution=betad, bijector=tfb.Shift(shift)(tfb.Scale(scale)) +# ) +# return independent_lib.Independent( +# transf_betad, +# reinterpreted_batch_ndims=tf.size(event_shape), +# validate_args=validate_args, +# ) + +# @staticmethod +# def params_size(event_shape=(), name=None): +# """The number of `params` needed to create a single distribution.""" +# with tf.name_scope(name or "IndependentConcaveBeta_params_size"): +# event_shape = tf.convert_to_tensor( +# event_shape, name="event_shape", dtype_hint=tf.int32 +# ) +# return np.int32(4) * _event_size( +# event_shape, name=name or "IndependentConcaveBeta_params_size" +# ) + +# def get_config(self): +# """Returns the config of this layer. +# NOTE: At the moment, this configuration can only be serialized if the +# Layer's `convert_to_tensor_fn` is a serializable Keras object (i.e., +# implements `get_config`) or one of the standard values: +# - `Distribution.sample` (or `"sample"`) +# - `Distribution.mean` (or `"mean"`) +# - `Distribution.mode` (or `"mode"`) +# - `Distribution.stddev` (or `"stddev"`) +# - `Distribution.variance` (or `"variance"`) +# """ +# config = { +# "event_shape": self._event_shape, +# "convert_to_tensor_fn": _serialize(self._convert_to_tensor_fn), +# "validate_args": self._validate_args, +# } +# base_config = super(IndependentConcaveBeta, self).get_config() +# return dict(list(base_config.items()) + list(config.items())) + +# @property +# def output(self): +# """This allows the use of this layer with the shap package.""" +# return super(IndependentConcaveBeta, self).output[0] + + +# @tf.keras.saving.register_keras_serializable() +# class IndependentGamma(tfpl.DistributionLambda): +# """An independent gamma Keras layer.""" + +# def __init__( +# self, +# event_shape=(), +# convert_to_tensor_fn=tfd.Distribution.mean, +# validate_args=False, +# **kwargs +# ): +# """Initialize the `IndependentGamma` layer. +# Args: +# event_shape: integer vector `Tensor` representing the shape of single +# draw from this distribution. +# convert_to_tensor_fn: Python `callable` that takes a `tfd.Distribution` +# instance and returns a `tf.Tensor`-like object. +# Default value: `tfd.Distribution.mean`. +# validate_args: Python `bool`, default `False`. When `True` distribution +# parameters are checked for validity despite possibly degrading runtime +# performance. When `False` invalid inputs may silently render incorrect +# outputs. +# Default value: `False`. +# **kwargs: Additional keyword arguments passed to `tf.keras.Layer`. +# """ +# convert_to_tensor_fn = _get_convert_to_tensor_fn(convert_to_tensor_fn) + +# # If there is a 'make_distribution_fn' keyword argument (e.g., because we +# # are being called from a `from_config` method), remove it. We pass the +# # distribution function to `DistributionLambda.__init__` below as the first +# # positional argument. +# kwargs.pop("make_distribution_fn", None) + +# def new_from_t(t): +# return IndependentGamma.new(t, event_shape, validate_args) + +# super(IndependentGamma, self).__init__( +# new_from_t, convert_to_tensor_fn, **kwargs +# ) + +# self._event_shape = event_shape +# self._convert_to_tensor_fn = convert_to_tensor_fn +# self._validate_args = validate_args + +# @staticmethod +# def new(params, event_shape=(), validate_args=False, name=None): +# """Create the distribution instance from a `params` vector.""" +# with tf.name_scope(name or "IndependentGamma"): +# params = tf.convert_to_tensor(params, name="params") +# event_shape = dist_util.expand_to_vector( +# tf.convert_to_tensor( +# event_shape, name="event_shape", dtype_hint=tf.int32 +# ), +# tensor_name="event_shape", +# ) +# output_shape = tf.concat( +# [ +# tf.shape(params)[:-1], +# event_shape, +# ], +# axis=0, +# ) +# concentration, rate = tf.split(params, 2, axis=-1) +# return independent_lib.Independent( +# tfd.Gamma( +# concentration=tf.math.softplus( +# tf.reshape(concentration, output_shape) +# ), +# rate=tf.math.softplus(tf.reshape(rate, output_shape)), +# validate_args=validate_args, +# ), +# reinterpreted_batch_ndims=tf.size(event_shape), +# validate_args=validate_args, +# ) + +# @staticmethod +# def params_size(event_shape=(), name=None): +# """The number of `params` needed to create a single distribution.""" +# with tf.name_scope(name or "IndependentGamma_params_size"): +# event_shape = tf.convert_to_tensor( +# event_shape, name="event_shape", dtype_hint=tf.int32 +# ) +# return np.int32(2) * _event_size( +# event_shape, name=name or "IndependentGamma_params_size" +# ) + +# def get_config(self): +# """Returns the config of this layer. +# NOTE: At the moment, this configuration can only be serialized if the +# Layer's `convert_to_tensor_fn` is a serializable Keras object (i.e., +# implements `get_config`) or one of the standard values: +# - `Distribution.sample` (or `"sample"`) +# - `Distribution.mean` (or `"mean"`) +# - `Distribution.mode` (or `"mode"`) +# - `Distribution.stddev` (or `"stddev"`) +# - `Distribution.variance` (or `"variance"`) +# """ +# config = { +# "event_shape": self._event_shape, +# "convert_to_tensor_fn": _serialize(self._convert_to_tensor_fn), +# "validate_args": self._validate_args, +# } +# base_config = super(IndependentGamma, self).get_config() +# return dict(list(base_config.items()) + list(config.items())) + +# @property +# def output(self): +# """This allows the use of this layer with the shap package.""" +# return super(IndependentGamma, self).output[0] + + +# @tf.keras.saving.register_keras_serializable() +# class IndependentLogNormal(tfpl.DistributionLambda): +# """An independent LogNormal Keras layer.""" + +# def __init__( +# self, +# event_shape=(), +# convert_to_tensor_fn=tfd.Distribution.mean, +# validate_args=False, +# **kwargs +# ): +# """Initialize the `IndependentLogNormal` layer. +# Args: +# event_shape: integer vector `Tensor` representing the shape of single +# draw from this distribution. +# convert_to_tensor_fn: Python `callable` that takes a `tfd.Distribution` +# instance and returns a `tf.Tensor`-like object. +# Default value: `tfd.Distribution.mean`. +# validate_args: Python `bool`, default `False`. When `True` distribution +# parameters are checked for validity despite possibly degrading runtime +# performance. When `False` invalid inputs may silently render incorrect +# outputs. +# Default value: `False`. +# **kwargs: Additional keyword arguments passed to `tf.keras.Layer`. +# """ +# convert_to_tensor_fn = _get_convert_to_tensor_fn(convert_to_tensor_fn) + +# # If there is a 'make_distribution_fn' keyword argument (e.g., because we +# # are being called from a `from_config` method), remove it. We pass the +# # distribution function to `DistributionLambda.__init__` below as the first +# # positional argument. +# kwargs.pop("make_distribution_fn", None) + +# def new_from_t(t): +# return IndependentLogNormal.new(t, event_shape, validate_args) + +# super(IndependentLogNormal, self).__init__( +# new_from_t, convert_to_tensor_fn, **kwargs +# ) + +# self._event_shape = event_shape +# self._convert_to_tensor_fn = convert_to_tensor_fn +# self._validate_args = validate_args + +# @staticmethod +# def new(params, event_shape=(), validate_args=False, name=None): +# """Create the distribution instance from a `params` vector.""" +# with tf.name_scope(name or "IndependentLogNormal"): +# params = tf.convert_to_tensor(params, name="params") +# event_shape = dist_util.expand_to_vector( +# tf.convert_to_tensor( +# event_shape, name="event_shape", dtype_hint=tf.int32 +# ), +# tensor_name="event_shape", +# ) +# output_shape = tf.concat( +# [ +# tf.shape(params)[:-1], +# event_shape, +# ], +# axis=0, +# ) +# loc, scale = tf.split(params, 2, axis=-1) +# return independent_lib.Independent( +# tfd.LogNormal( +# loc=tf.reshape(loc, output_shape), +# scale=tf.math.softplus(tf.reshape(scale, output_shape)) + 1e-3, +# validate_args=validate_args, +# ), +# reinterpreted_batch_ndims=tf.size(event_shape), +# validate_args=validate_args, +# ) + +# @staticmethod +# def params_size(event_shape=(), name=None): +# """The number of `params` needed to create a single distribution.""" +# with tf.name_scope(name or "IndependentLogNormal_params_size"): +# event_shape = tf.convert_to_tensor( +# event_shape, name="event_shape", dtype_hint=tf.int32 +# ) +# return np.int32(2) * _event_size( +# event_shape, name=name or "IndependentLogNormal_params_size" +# ) + +# def get_config(self): +# """Returns the config of this layer. +# NOTE: At the moment, this configuration can only be serialized if the +# Layer's `convert_to_tensor_fn` is a serializable Keras object (i.e., +# implements `get_config`) or one of the standard values: +# - `Distribution.sample` (or `"sample"`) +# - `Distribution.mean` (or `"mean"`) +# - `Distribution.mode` (or `"mode"`) +# - `Distribution.stddev` (or `"stddev"`) +# - `Distribution.variance` (or `"variance"`) +# """ +# config = { +# "event_shape": self._event_shape, +# "convert_to_tensor_fn": _serialize(self._convert_to_tensor_fn), +# "validate_args": self._validate_args, +# } +# base_config = super(IndependentLogNormal, self).get_config() +# return dict(list(base_config.items()) + list(config.items())) + +# @property +# def output(self): +# """This allows the use of this layer with the shap package.""" +# return super(IndependentLogNormal, self).output[0] + + +# @tf.keras.saving.register_keras_serializable() +# class IndependentLogitNormal(tfpl.DistributionLambda): +# """An independent Logit-Normal Keras layer.""" + +# def __init__( +# self, +# event_shape=(), +# convert_to_tensor_fn=tfd.Distribution.sample, +# validate_args=False, +# **kwargs +# ): +# """Initialize the `IndependentLogitNormal` layer. +# Args: +# event_shape: integer vector `Tensor` representing the shape of single +# draw from this distribution. +# convert_to_tensor_fn: Python `callable` that takes a `tfd.Distribution` +# instance and returns a `tf.Tensor`-like object. +# Default value: `tfd.Distribution.mean`. +# validate_args: Python `bool`, default `False`. When `True` distribution +# parameters are checked for validity despite possibly degrading runtime +# performance. When `False` invalid inputs may silently render incorrect +# outputs. +# Default value: `False`. +# **kwargs: Additional keyword arguments passed to `tf.keras.Layer`. +# """ +# convert_to_tensor_fn = _get_convert_to_tensor_fn(convert_to_tensor_fn) + +# # If there is a 'make_distribution_fn' keyword argument (e.g., because we +# # are being called from a `from_config` method), remove it. We pass the +# # distribution function to `DistributionLambda.__init__` below as the first +# # positional argument. +# kwargs.pop("make_distribution_fn", None) + +# def new_from_t(t): +# return IndependentLogitNormal.new(t, event_shape, validate_args) + +# super(IndependentLogitNormal, self).__init__( +# new_from_t, convert_to_tensor_fn, **kwargs +# ) + +# self._event_shape = event_shape +# self._convert_to_tensor_fn = convert_to_tensor_fn +# self._validate_args = validate_args + +# @staticmethod +# def new(params, event_shape=(), validate_args=False, name=None): +# """Create the distribution instance from a `params` vector.""" +# with tf.name_scope(name or "IndependentLogitNormal"): +# params = tf.convert_to_tensor(params, name="params") +# event_shape = dist_util.expand_to_vector( +# tf.convert_to_tensor( +# event_shape, name="event_shape", dtype_hint=tf.int32 +# ), +# tensor_name="event_shape", +# ) +# output_shape = tf.concat( +# [ +# tf.shape(params)[:-1], +# event_shape, +# ], +# axis=0, +# ) +# loc, scale = tf.split(params, 2, axis=-1) +# return independent_lib.Independent( +# tfd.LogitNormal( +# loc=tf.reshape(loc, output_shape), +# scale=tf.math.softplus(tf.reshape(scale, output_shape)) + 1e-3, +# validate_args=validate_args, +# ), +# reinterpreted_batch_ndims=tf.size(event_shape), +# validate_args=validate_args, +# ) + +# @staticmethod +# def params_size(event_shape=(), name=None): +# """The number of `params` needed to create a single distribution.""" +# with tf.name_scope(name or "IndependentLogitNormal_params_size"): +# event_shape = tf.convert_to_tensor( +# event_shape, name="event_shape", dtype_hint=tf.int32 +# ) +# return np.int32(2) * _event_size( +# event_shape, name=name or "IndependentLogitNormal_params_size" +# ) + +# def get_config(self): +# """Returns the config of this layer. +# NOTE: At the moment, this configuration can only be serialized if the +# Layer's `convert_to_tensor_fn` is a serializable Keras object (i.e., +# implements `get_config`) or one of the standard values: +# - `Distribution.sample` (or `"sample"`) +# - `Distribution.mean` (or `"mean"`) +# - `Distribution.mode` (or `"mode"`) +# - `Distribution.stddev` (or `"stddev"`) +# - `Distribution.variance` (or `"variance"`) +# """ +# config = { +# "event_shape": self._event_shape, +# "convert_to_tensor_fn": _serialize(self._convert_to_tensor_fn), +# "validate_args": self._validate_args, +# } +# base_config = super(IndependentLogitNormal, self).get_config() +# return dict(list(base_config.items()) + list(config.items())) + +# @property +# def output(self): +# """This allows the use of this layer with the shap package.""" +# return super(IndependentLogitNormal, self).output[0] + + +# @tf.keras.saving.register_keras_serializable() +# class IndependentMixtureNormal(tfpl.DistributionLambda): +# """A mixture of two normal distributions Keras layer. +# 5-parameters distribution: loc1, scale1, loc2, scale2, weight +# """ + +# def __init__( +# self, +# event_shape=(), +# convert_to_tensor_fn=tfd.Distribution.mean, +# validate_args=False, +# **kwargs +# ): +# """Initialize the `IndependentMixtureNormal` layer. +# Args: +# event_shape: integer vector `Tensor` representing the shape of single +# draw from this distribution. +# convert_to_tensor_fn: Python `callable` that takes a `tfd.Distribution` +# instance and returns a `tf.Tensor`-like object. +# Default value: `tfd.Distribution.mean`. +# validate_args: Python `bool`, default `False`. When `True` distribution +# parameters are checked for validity despite possibly degrading runtime +# performance. When `False` invalid inputs may silently render incorrect +# outputs. +# Default value: `False`. +# **kwargs: Additional keyword arguments passed to `tf.keras.Layer`. +# """ + +# convert_to_tensor_fn = _get_convert_to_tensor_fn(convert_to_tensor_fn) + +# # If there is a 'make_distribution_fn' keyword argument (e.g., because we +# # are being called from a `from_config` method), remove it. We pass the +# # distribution function to `DistributionLambda.__init__` below as the first +# # positional argument. +# kwargs.pop("make_distribution_fn", None) + +# def new_from_t(t): +# return IndependentMixtureNormal.new(t, event_shape, validate_args) + +# super(IndependentMixtureNormal, self).__init__( +# new_from_t, convert_to_tensor_fn, **kwargs +# ) + +# self._event_shape = event_shape +# self._convert_to_tensor_fn = convert_to_tensor_fn +# self._validate_args = validate_args + +# @staticmethod +# def new(params, event_shape=(), validate_args=False, name=None): +# """Create the distribution instance from a `params` vector.""" +# with tf.name_scope(name or "IndependentMixtureNormal"): +# params = tf.convert_to_tensor(params, name="params") + +# event_shape = dist_util.expand_to_vector( +# tf.convert_to_tensor( +# event_shape, name="event_shape", dtype_hint=tf.int32 +# ), +# tensor_name="event_shape", +# ) + +# output_shape = tf.concat( +# [ +# tf.shape(params)[:-1], +# event_shape, +# ], +# axis=0, +# ) + +# loc1, scale1, loc2, scale2, weight = tf.split(params, 5, axis=-1) +# loc1 = tf.reshape(loc1, output_shape) +# scale1 = tf.math.softplus(tf.reshape(scale1, output_shape)) + 1e-3 +# loc2 = tf.reshape(loc2, output_shape) +# scale2 = tf.math.softplus(tf.reshape(scale2, output_shape)) + 1e-3 +# weight = tf.math.sigmoid(tf.reshape(weight, output_shape)) + +# # Create the component distributions +# normald1 = tfd.Normal(loc=loc1, scale=scale1) +# normald2 = tfd.Normal(loc=loc2, scale=scale2) + +# # Create a categorical distribution for the weights +# cat = tfd.Categorical( +# probs=tf.concat( +# [tf.expand_dims(weight, -1), tf.expand_dims(1 - weight, -1)], +# axis=-1, +# ) +# ) + +# class CustomMixture(tfd.Distribution): +# def __init__(self, cat, normald1, normald2): +# self.cat = cat +# self.normald1 = normald1 +# self.normald2 = normald2 +# super(CustomMixture, self).__init__( +# dtype=normald1.dtype, +# reparameterization_type=tfd.FULLY_REPARAMETERIZED, +# validate_args=validate_args, +# allow_nan_stats=True, +# ) + +# def _sample_n(self, n, seed=None): +# indices = self.cat.sample(sample_shape=(n,), seed=seed) + +# # Sample from both truncated normal distributions +# samples1 = self.normald1.sample(sample_shape=(n,), seed=seed) +# samples2 = self.normald2.sample(sample_shape=(n,), seed=seed) + +# # Stack the samples along a new axis +# samples = tf.stack([samples1, samples2], axis=-1) + +# # Gather samples according to indices from the categorical distribution +# chosen_samples = tf.gather( +# samples, +# indices, +# batch_dims=tf.get_static_value(tf.rank(indices)), +# ) + +# return chosen_samples + +# def _log_prob(self, value): +# log_prob1 = self.normald1.log_prob(value) +# log_prob2 = self.normald2.log_prob(value) +# log_probs = tf.stack([log_prob1, log_prob2], axis=-1) +# weighted_log_probs = log_probs + tf.math.log( +# tf.concat([weight, 1 - weight], axis=-1) +# ) +# return tf.reduce_logsumexp(weighted_log_probs, axis=-1) + +# def _mean(self): +# return ( +# weight * self.normald1.mean() +# + (1 - weight) * self.normald2.mean() +# ) + +# mixtured = CustomMixture(cat, normald1, normald2) + +# return independent_lib.Independent( +# mixtured, +# reinterpreted_batch_ndims=tf.size(event_shape), +# validate_args=validate_args, +# ) + +# @staticmethod +# def params_size(event_shape=(), name=None): +# """The number of `params` needed to create a single distribution.""" +# with tf.name_scope(name or "IndependentMixtureNormal_params_size"): +# event_shape = tf.convert_to_tensor( +# event_shape, name="event_shape", dtype_hint=tf.int32 +# ) +# return np.int32(5) * _event_size( +# event_shape, name=name or "IndependentMixtureNormal_params_size" +# ) + +# def get_config(self): +# """Returns the config of this layer. +# NOTE: At the moment, this configuration can only be serialized if the +# Layer's `convert_to_tensor_fn` is a serializable Keras object (i.e., +# implements `get_config`) or one of the standard values: +# - `Distribution.sample` (or `"sample"`) +# - `Distribution.mean` (or `"mean"`) +# - `Distribution.mode` (or `"mode"`) +# - `Distribution.stddev` (or `"stddev"`) +# - `Distribution.variance` (or `"variance"`) +# """ +# config = { +# "event_shape": self._event_shape, +# "convert_to_tensor_fn": _serialize(self._convert_to_tensor_fn), +# "validate_args": self._validate_args, +# } +# base_config = super(IndependentMixtureNormal, self).get_config() +# return dict(list(base_config.items()) + list(config.items())) + +# @property +# def output(self): +# """This allows the use of this layer with the shap package.""" +# return super(IndependentMixtureNormal, self).output[0] + + +# @tf.keras.saving.register_keras_serializable() +# class IndependentTruncatedNormal(tfpl.DistributionLambda): +# """An independent TruncatedNormal Keras layer.""" + +# def __init__( +# self, +# event_shape=(), +# convert_to_tensor_fn=tfd.Distribution.mean, +# validate_args=False, +# **kwargs +# ): +# """Initialize the `IndependentTruncatedNormal` layer. +# Args: +# event_shape: integer vector `Tensor` representing the shape of single +# draw from this distribution. +# convert_to_tensor_fn: Python `callable` that takes a `tfd.Distribution` +# instance and returns a `tf.Tensor`-like object. +# Default value: `tfd.Distribution.mean`. +# validate_args: Python `bool`, default `False`. When `True` distribution +# parameters are checked for validity despite possibly degrading runtime +# performance. When `False` invalid inputs may silently render incorrect +# outputs. +# Default value: `False`. +# **kwargs: Additional keyword arguments passed to `tf.keras.Layer`. +# """ +# convert_to_tensor_fn = _get_convert_to_tensor_fn(convert_to_tensor_fn) + +# # If there is a 'make_distribution_fn' keyword argument (e.g., because we +# # are being called from a `from_config` method), remove it. We pass the +# # distribution function to `DistributionLambda.__init__` below as the first +# # positional argument. +# kwargs.pop("make_distribution_fn", None) + +# def new_from_t(t): +# return IndependentTruncatedNormal.new(t, event_shape, validate_args) + +# super(IndependentTruncatedNormal, self).__init__( +# new_from_t, convert_to_tensor_fn, **kwargs +# ) + +# self._event_shape = event_shape +# self._convert_to_tensor_fn = convert_to_tensor_fn +# self._validate_args = validate_args + +# @staticmethod +# def new(params, event_shape=(), validate_args=False, name=None): +# """Create the distribution instance from a `params` vector.""" +# with tf.name_scope(name or "IndependentTruncatedNormal"): +# params = tf.convert_to_tensor(params, name="params") +# event_shape = dist_util.expand_to_vector( +# tf.convert_to_tensor( +# event_shape, name="event_shape", dtype_hint=tf.int32 +# ), +# tensor_name="event_shape", +# ) +# output_shape = tf.concat( +# [ +# tf.shape(params)[:-1], +# event_shape, +# ], +# axis=0, +# ) +# loc, scale = tf.split(params, 2, axis=-1) +# return independent_lib.Independent( +# tfd.TruncatedNormal( +# loc=tf.reshape(loc, output_shape), +# scale=tf.math.softplus(tf.reshape(scale, output_shape)) + 1e-3, +# low=0, +# high=np.inf, +# validate_args=validate_args, +# ), +# reinterpreted_batch_ndims=tf.size(event_shape), +# validate_args=validate_args, +# ) + +# @staticmethod +# def params_size(event_shape=(), name=None): +# """The number of `params` needed to create a single distribution.""" +# with tf.name_scope(name or "IndependentTruncatedNormal_params_size"): +# event_shape = tf.convert_to_tensor( +# event_shape, name="event_shape", dtype_hint=tf.int32 +# ) +# return np.int32(2) * _event_size( +# event_shape, name=name or "IndependentTruncatedNormal_params_size" +# ) + +# def get_config(self): +# """Returns the config of this layer. +# NOTE: At the moment, this configuration can only be serialized if the +# Layer's `convert_to_tensor_fn` is a serializable Keras object (i.e., +# implements `get_config`) or one of the standard values: +# - `Distribution.sample` (or `"sample"`) +# - `Distribution.mean` (or `"mean"`) +# - `Distribution.mode` (or `"mode"`) +# - `Distribution.stddev` (or `"stddev"`) +# - `Distribution.variance` (or `"variance"`) +# """ +# config = { +# "event_shape": self._event_shape, +# "convert_to_tensor_fn": _serialize(self._convert_to_tensor_fn), +# "validate_args": self._validate_args, +# } +# base_config = super(IndependentTruncatedNormal, self).get_config() +# return dict(list(base_config.items()) + list(config.items())) + +# @property +# def output(self): +# """This allows the use of this layer with the shap package.""" +# return super(IndependentTruncatedNormal, self).output[0] + + +# @tf.keras.saving.register_keras_serializable() +# class IndependentWeibull(tfpl.DistributionLambda): +# """An independent Weibull Keras layer.""" + +# def __init__( +# self, +# event_shape=(), +# convert_to_tensor_fn=tfd.Distribution.mean, +# validate_args=False, +# **kwargs +# ): +# """Initialize the `IndependentWeibull` layer. +# Args: +# event_shape: integer vector `Tensor` representing the shape of single +# draw from this distribution. +# convert_to_tensor_fn: Python `callable` that takes a `tfd.Distribution` +# instance and returns a `tf.Tensor`-like object. +# Default value: `tfd.Distribution.mean`. +# validate_args: Python `bool`, default `False`. When `True` distribution +# parameters are checked for validity despite possibly degrading runtime +# performance. When `False` invalid inputs may silently render incorrect +# outputs. +# Default value: `False`. +# **kwargs: Additional keyword arguments passed to `tf.keras.Layer`. +# """ +# convert_to_tensor_fn = _get_convert_to_tensor_fn(convert_to_tensor_fn) + +# # If there is a 'make_distribution_fn' keyword argument (e.g., because we +# # are being called from a `from_config` method), remove it. We pass the +# # distribution function to `DistributionLambda.__init__` below as the first +# # positional argument. +# kwargs.pop("make_distribution_fn", None) + +# def new_from_t(t): +# return IndependentWeibull.new(t, event_shape, validate_args) + +# super(IndependentWeibull, self).__init__( +# new_from_t, convert_to_tensor_fn, **kwargs +# ) + +# self._event_shape = event_shape +# self._convert_to_tensor_fn = convert_to_tensor_fn +# self._validate_args = validate_args + +# @staticmethod +# def new(params, event_shape=(), validate_args=False, name=None): +# """Create the distribution instance from a `params` vector.""" +# with tf.name_scope(name or "IndependentWeibull"): +# params = tf.convert_to_tensor(params, name="params") +# event_shape = dist_util.expand_to_vector( +# tf.convert_to_tensor( +# event_shape, name="event_shape", dtype_hint=tf.int32 +# ), +# tensor_name="event_shape", +# ) +# output_shape = tf.concat( +# [ +# tf.shape(params)[:-1], +# event_shape, +# ], +# axis=0, +# ) +# concentration, scale = tf.split(params, 2, axis=-1) +# return independent_lib.Independent( +# tfd.Weibull( +# concentration=tf.math.softplus( +# tf.reshape(concentration, output_shape) +# ) +# + 1.0, +# scale=tf.math.softplus(tf.reshape(scale, output_shape)), +# validate_args=validate_args, +# ), +# reinterpreted_batch_ndims=tf.size(event_shape), +# validate_args=validate_args, +# ) + +# @staticmethod +# def params_size(event_shape=(), name=None): +# """The number of `params` needed to create a single distribution.""" +# with tf.name_scope(name or "IndependentWeibull_params_size"): +# event_shape = tf.convert_to_tensor( +# event_shape, name="event_shape", dtype_hint=tf.int32 +# ) +# return np.int32(2) * _event_size( +# event_shape, name=name or "IndependentWeibull_params_size" +# ) + +# def get_config(self): +# """Returns the config of this layer. +# NOTE: At the moment, this configuration can only be serialized if the +# Layer's `convert_to_tensor_fn` is a serializable Keras object (i.e., +# implements `get_config`) or one of the standard values: +# - `Distribution.sample` (or `"sample"`) +# - `Distribution.mean` (or `"mean"`) +# - `Distribution.mode` (or `"mode"`) +# - `Distribution.stddev` (or `"stddev"`) +# - `Distribution.variance` (or `"variance"`) +# """ +# config = { +# "event_shape": self._event_shape, +# "convert_to_tensor_fn": _serialize(self._convert_to_tensor_fn), +# "validate_args": self._validate_args, +# } +# base_config = super(IndependentWeibull, self).get_config() +# return dict(list(base_config.items()) + list(config.items())) + +# @property +# def output(self): +# """This allows the use of this layer with the shap package.""" +# return super(IndependentWeibull, self).output[0] + + +# @tf.keras.saving.register_keras_serializable() +# class MultivariateNormalDiag(tfpl.DistributionLambda): +# """A `d`-variate normal Keras layer from `2* d` params, +# with a diagonal scale matrix. +# """ + +# def __init__( +# self, +# event_size, +# convert_to_tensor_fn=tfd.Distribution.mean, +# validate_args=False, +# **kwargs +# ): +# """Initialize the layer. +# Args: +# event_size: Scalar `int` representing the size of single draw from this +# distribution. +# convert_to_tensor_fn: Python `callable` that takes a `tfd.Distribution` +# instance and returns a `tf.Tensor`-like object. For examples, see +# `class` docstring. +# Default value: `tfd.Distribution.sample`. +# validate_args: Python `bool`, default `False`. When `True` distribution +# parameters are checked for validity despite possibly degrading runtime +# performance. When `False` invalid inputs may silently render incorrect +# outputs. +# Default value: `False`. +# **kwargs: Additional keyword arguments passed to `tf.keras.Layer`. +# """ +# convert_to_tensor_fn = _get_convert_to_tensor_fn(convert_to_tensor_fn) + +# # If there is a 'make_distribution_fn' keyword argument (e.g., because we +# # are being called from a `from_config` method), remove it. We pass the +# # distribution function to `DistributionLambda.__init__` below as the first +# # positional argument. +# kwargs.pop("make_distribution_fn", None) + +# def new_from_t(t): +# return MultivariateNormalDiag.new(t, event_size, validate_args) + +# super(MultivariateNormalDiag, self).__init__( +# new_from_t, convert_to_tensor_fn, **kwargs +# ) + +# self._event_size = event_size +# self._convert_to_tensor_fn = convert_to_tensor_fn +# self._validate_args = validate_args + +# @staticmethod +# def new(params, event_size, validate_args=False, name=None): +# """Create the distribution instance from a 'params' vector.""" +# with tf.name_scope(name or "MultivariateNormalDiag"): +# params = tf.convert_to_tensor(params, name="params") +# if event_size > 1: +# dist = tfd.MultivariateNormalDiag( +# loc=params[..., :event_size], +# scale_diag=1e-5 + tf.math.softplus(params[..., event_size:]), +# validate_args=validate_args, +# ) +# else: +# dist = tfd.Normal( +# loc=params[..., :event_size], +# scale=1e-5 + tf.math.softplus(params[..., event_size:]), +# validate_args=validate_args, +# ) +# return dist + +# @staticmethod +# def params_size(event_size, name=None): +# """The number of 'params' needed to create a single distribution.""" +# with tf.name_scope(name or "MultivariateNormalDiag_params_size"): +# return 2 * event_size + +# def get_config(self): +# """Returns the config of this layer. +# NOTE: At the moment, this configuration can only be serialized if the +# Layer's `convert_to_tensor_fn` is a serializable Keras object (i.e., +# implements `get_config`) or one of the standard values: +# - `Distribution.sample` (or `"sample"`) +# - `Distribution.mean` (or `"mean"`) +# - `Distribution.mode` (or `"mode"`) +# - `Distribution.stddev` (or `"stddev"`) +# - `Distribution.variance` (or `"variance"`) +# """ +# config = { +# "event_size": self._event_size, +# "convert_to_tensor_fn": _serialize(self._convert_to_tensor_fn), +# "validate_args": self._validate_args, +# } +# base_config = super(MultivariateNormalDiag, self).get_config() +# return dict(list(base_config.items()) + list(config.items())) + +# @property +# def output(self): +# """This allows the use of this layer with the shap package.""" +# return super(MultivariateNormalDiag, self).output[0] + + +# @tf.keras.saving.register_keras_serializable() +# class MultivariateNormalTriL(tfpl.MultivariateNormalTriL): +# def __init__( +# self, +# event_size, +# convert_to_tensor_fn=tfd.Distribution.mean, +# validate_args=False, +# **kwargs +# ): +# convert_to_tensor_fn = _get_convert_to_tensor_fn(convert_to_tensor_fn) + +# # If there is a 'make_distribution_fn' keyword argument (e.g., because we +# # are being called from a `from_config` method), remove it. We pass the +# # distribution function to `DistributionLambda.__init__` below as the first +# # positional argument. +# kwargs.pop("make_distribution_fn", None) + +# super().__init__(event_size, convert_to_tensor_fn, validate_args, **kwargs) +# self._event_size = event_size +# self._convert_to_tensor_fn = convert_to_tensor_fn +# self._validate_args = validate_args + +# def get_config(self): +# """Returns the config of this layer. +# NOTE: At the moment, this configuration can only be serialized if the +# Layer's `convert_to_tensor_fn` is a serializable Keras object (i.e., +# implements `get_config`) or one of the standard values: +# - `Distribution.sample` (or `"sample"`) +# - `Distribution.mean` (or `"mean"`) +# - `Distribution.mode` (or `"mode"`) +# - `Distribution.stddev` (or `"stddev"`) +# - `Distribution.variance` (or `"variance"`) +# """ +# config = { +# "event_size": self._event_size, +# "convert_to_tensor_fn": _serialize(self._convert_to_tensor_fn), +# "validate_args": self._validate_args, +# } +# base_config = super(MultivariateNormalTriL, self).get_config() +# return dict(list(base_config.items()) + list(config.items())) + +# @property +# def output(self): +# """This allows the use of this layer with the shap package.""" +# return super(MultivariateNormalTriL, self).output[0] diff --git a/mlpp_lib/train.py b/mlpp_lib/train.py index 519394a..be400ea 100644 --- a/mlpp_lib/train.py +++ b/mlpp_lib/train.py @@ -2,7 +2,8 @@ from pprint import pformat from typing import Optional -import tensorflow as tf +# import tensorflow as tf +import keras from mlpp_lib.callbacks import TimeHistory, EnsembleMetrics from mlpp_lib.datasets import DataLoader, DataModule @@ -37,11 +38,11 @@ def get_log_params(param_run: dict) -> dict: return log_params -def get_lr(optimizer: tf.keras.optimizers.Optimizer) -> float: +def get_lr(optimizer: keras.optimizers.Optimizer) -> float: """Get the learning rate of the optimizer""" def lr(y_true, y_pred): - return optimizer.lr + return optimizer.learning_rate return lr @@ -126,7 +127,7 @@ def train( # we don't need to export loss and metric functions for deployments model.compile(optimizer=optimizer, loss=None, metrics=None) - custom_objects = tf.keras.layers.serialize(model) + custom_objects = keras.layers.serialize(model) history = res.history # for some reasons, 'lr' is provided as float32 diff --git a/mlpp_lib/utils.py b/mlpp_lib/utils.py index 6ef69ce..f09ad25 100644 --- a/mlpp_lib/utils.py +++ b/mlpp_lib/utils.py @@ -3,8 +3,9 @@ import numpy as np import xarray as xr -import tensorflow as tf - +# import tensorflow as tf +import keras +import importlib from mlpp_lib import callbacks, losses, metrics, models @@ -29,9 +30,9 @@ def get_callback(callback: Union[str, dict]) -> Callable: if isinstance(callback_obj, type) else callback_obj ) - elif hasattr(tf.keras.callbacks, callback_name): + elif hasattr(keras.callbacks, callback_name): LOGGER.info(f"Using keras built-in callback: {callback_name}") - callback_obj = getattr(tf.keras.callbacks, callback_name) + callback_obj = getattr(keras.callbacks, callback_name) callback = ( callback_obj(**callback_options) if isinstance(callback_obj, type) @@ -47,7 +48,7 @@ def get_model( input_shape: tuple[int], output_shape: Union[int, tuple[int]], model_config: dict[str, Any], -) -> tf.keras.Model: +) -> keras.Model: """Get the keras model.""" model_name = list(model_config.keys())[0] @@ -58,7 +59,7 @@ def get_model( LOGGER.debug(model_options) if isinstance(output_shape, int): output_shape = (output_shape,) - model = getattr(models, model_name)(input_shape, output_shape[-1], **model_options) + model = getattr(models, model_name)(output_shape[-1], **model_options) return model @@ -67,24 +68,27 @@ def get_loss(loss: Union[str, dict]) -> Callable: """Get the loss function, either keras built-in or mlpp custom.""" if isinstance(loss, dict): - loss_name = list(loss.keys())[0] - loss_options = loss[loss_name] - else: - loss_name = loss - loss_options = {} - - if hasattr(losses, loss_name): - LOGGER.info(f"Using custom mlpp loss: {loss_name}") - loss_obj = getattr(losses, loss_name) - loss = loss_obj(**loss_options) if isinstance(loss_obj, type) else loss_obj - elif hasattr(tf.keras.losses, loss_name): - LOGGER.info(f"Using keras built-in loss: {loss_name}") - loss_obj = getattr(tf.keras.losses, loss_name) - loss = loss_obj(**loss_options) if isinstance(loss_obj, type) else loss_obj + wrapper = list(loss.keys())[0] + fn_ = loss[wrapper] + if isinstance(fn_, dict): + fn = list(fn_.keys())[0] + fn_args = fn_[fn] + + module_name, fn_name = fn.rsplit(".", 1) + + else: + module_name, fn_name = fn_.rsplit(".", 1) + fn_args = {} + + module = importlib.import_module(module_name) + loss_fn = getattr(module, fn_name) + LOGGER.info(f"Using {fn_name} loss from {module_name}") + return getattr(losses, wrapper)(fn=loss_fn, **fn_args) + else: - raise KeyError(f"The loss {loss_name} is not available.") + # TODO decide what to do + pass - return loss def get_metric(metric: Union[str, dict]) -> Callable: @@ -103,9 +107,9 @@ def get_metric(metric: Union[str, dict]) -> Callable: metric = ( metric_obj(**metric_options) if isinstance(metric_obj, type) else metric_obj ) - elif hasattr(tf.keras.metrics, metric_name): + elif hasattr(keras.metrics, metric_name): LOGGER.info(f"Using keras built-in metric: {metric_name}") - metric_obj = getattr(tf.keras.metrics, metric_name) + metric_obj = getattr(keras.metrics, metric_name) metric = ( metric_obj(**metric_options) if isinstance(metric_obj, type) else metric_obj ) @@ -117,7 +121,7 @@ def get_metric(metric: Union[str, dict]) -> Callable: def get_scheduler( scheduler_config: Union[dict, None] -) -> Optional[tf.keras.optimizers.schedules.LearningRateSchedule]: +) -> Optional[keras.optimizers.schedules.LearningRateSchedule]: """Create a learning rate scheduler from a config dictionary.""" if not isinstance(scheduler_config, dict): @@ -139,13 +143,13 @@ def get_scheduler( f"Scheduler options for '{scheduler_name}' should be a dictionary." ) - if hasattr(tf.keras.optimizers.schedules, scheduler_name): + if hasattr(keras.optimizers.schedules, scheduler_name): LOGGER.info(f"Using keras built-in learning rate scheduler: {scheduler_name}") - scheduler_cls = getattr(tf.keras.optimizers.schedules, scheduler_name) + scheduler_cls = getattr(keras.optimizers.schedules, scheduler_name) scheduler = scheduler_cls(**scheduler_options) else: raise KeyError( - f"The scheduler '{scheduler_name}' is not available in tf.keras.optimizers.schedules." + f"The scheduler '{scheduler_name}' is not available in keras.optimizers.schedules." ) return scheduler @@ -163,9 +167,9 @@ def get_optimizer(optimizer: Union[str, dict]) -> Callable: optimizer_name = optimizer optimizer_options = {} - if hasattr(tf.keras.optimizers, optimizer_name): + if hasattr(keras.optimizers, optimizer_name): LOGGER.info(f"Using keras built-in optimizer: {optimizer_name}") - optimizer_obj = getattr(tf.keras.optimizers, optimizer_name) + optimizer_obj = getattr(keras.optimizers, optimizer_name) optimizer = ( optimizer_obj(**optimizer_options) if isinstance(optimizer_obj, type) diff --git a/poetry.lock b/poetry.lock index c71d03b..cd9a48c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "absl-py" @@ -13,13 +13,13 @@ files = [ [[package]] name = "alembic" -version = "1.13.3" +version = "1.14.0" description = "A database migration tool for SQLAlchemy." optional = false python-versions = ">=3.8" files = [ - {file = "alembic-1.13.3-py3-none-any.whl", hash = "sha256:908e905976d15235fae59c9ac42c4c5b75cfcefe3d27c0fbf7ae15a37715d80e"}, - {file = "alembic-1.13.3.tar.gz", hash = "sha256:203503117415561e203aa14541740643a611f641517f0209fcae63e9fa09f1a2"}, + {file = "alembic-1.14.0-py3-none-any.whl", hash = "sha256:99bd884ca390466db5e27ffccff1d179ec5c05c965cfefc0607e69f9e411cb25"}, + {file = "alembic-1.14.0.tar.gz", hash = "sha256:b00892b53b3642d0b8dbedba234dbf1924b69be83a9a769d5a624b01094e304b"}, ] [package.dependencies] @@ -32,24 +32,24 @@ tz = ["backports.zoneinfo"] [[package]] name = "anyio" -version = "4.6.2.post1" +version = "4.7.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.9" files = [ - {file = "anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d"}, - {file = "anyio-4.6.2.post1.tar.gz", hash = "sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c"}, + {file = "anyio-4.7.0-py3-none-any.whl", hash = "sha256:ea60c3723ab42ba6fff7e8ccb0488c898ec538ff4df1f1d5e642c3601d07e352"}, + {file = "anyio-4.7.0.tar.gz", hash = "sha256:2f834749c602966b7d456a7567cafcb309f96482b5081d14ac93ccd457f9dd48"}, ] [package.dependencies] exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} idna = ">=2.8" sniffio = ">=1.1" -typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} +typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] -doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21.0b1)"] +doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] trio = ["trio (>=0.26.1)"] [[package]] @@ -151,36 +151,18 @@ files = [ [[package]] name = "asttokens" -version = "2.4.1" +version = "3.0.0" description = "Annotate AST trees with source code positions" optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, - {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"}, + {file = "asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2"}, + {file = "asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7"}, ] -[package.dependencies] -six = ">=1.12.0" - [package.extras] -astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"] -test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] - -[[package]] -name = "astunparse" -version = "1.6.3" -description = "An AST unparser for Python" -optional = false -python-versions = "*" -files = [ - {file = "astunparse-1.6.3-py2.py3-none-any.whl", hash = "sha256:c2652417f2c8b5bb325c885ae329bdf3f86424075c4fd1a128674bc6fba4b8e8"}, - {file = "astunparse-1.6.3.tar.gz", hash = "sha256:5ad93a8456f0d084c3456d059fd9a92cce667963232cbf763eac3bc5b7940872"}, -] - -[package.dependencies] -six = ">=1.6.1,<2.0" -wheel = ">=0.23.0,<1.0" +astroid = ["astroid (>=2,<4)"] +test = ["astroid (>=2,<4)", "pytest", "pytest-cov", "pytest-xdist"] [[package]] name = "async-lru" @@ -315,13 +297,13 @@ css = ["tinycss2 (>=1.1.0,<1.5)"] [[package]] name = "blinker" -version = "1.8.2" +version = "1.9.0" description = "Fast, simple object-to-object and broadcast signaling" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "blinker-1.8.2-py3-none-any.whl", hash = "sha256:1779309f71bf239144b9399d06ae925637cf6634cf6bd131104184531bf67c01"}, - {file = "blinker-1.8.2.tar.gz", hash = "sha256:8f77b09d3bf7c795e969e9486f39c2c5e9c39d4ee07424be2bc594ece9642d83"}, + {file = "blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc"}, + {file = "blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf"}, ] [[package]] @@ -594,76 +576,65 @@ test = ["pytest"] [[package]] name = "contourpy" -version = "1.3.0" +version = "1.3.1" description = "Python library for calculating contours of 2D quadrilateral grids" optional = false -python-versions = ">=3.9" -files = [ - {file = "contourpy-1.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:880ea32e5c774634f9fcd46504bf9f080a41ad855f4fef54f5380f5133d343c7"}, - {file = "contourpy-1.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:76c905ef940a4474a6289c71d53122a4f77766eef23c03cd57016ce19d0f7b42"}, - {file = "contourpy-1.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92f8557cbb07415a4d6fa191f20fd9d2d9eb9c0b61d1b2f52a8926e43c6e9af7"}, - {file = "contourpy-1.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36f965570cff02b874773c49bfe85562b47030805d7d8360748f3eca570f4cab"}, - {file = "contourpy-1.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cacd81e2d4b6f89c9f8a5b69b86490152ff39afc58a95af002a398273e5ce589"}, - {file = "contourpy-1.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69375194457ad0fad3a839b9e29aa0b0ed53bb54db1bfb6c3ae43d111c31ce41"}, - {file = "contourpy-1.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a52040312b1a858b5e31ef28c2e865376a386c60c0e248370bbea2d3f3b760d"}, - {file = "contourpy-1.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3faeb2998e4fcb256542e8a926d08da08977f7f5e62cf733f3c211c2a5586223"}, - {file = "contourpy-1.3.0-cp310-cp310-win32.whl", hash = "sha256:36e0cff201bcb17a0a8ecc7f454fe078437fa6bda730e695a92f2d9932bd507f"}, - {file = "contourpy-1.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:87ddffef1dbe5e669b5c2440b643d3fdd8622a348fe1983fad7a0f0ccb1cd67b"}, - {file = "contourpy-1.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0fa4c02abe6c446ba70d96ece336e621efa4aecae43eaa9b030ae5fb92b309ad"}, - {file = "contourpy-1.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:834e0cfe17ba12f79963861e0f908556b2cedd52e1f75e6578801febcc6a9f49"}, - {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbc4c3217eee163fa3984fd1567632b48d6dfd29216da3ded3d7b844a8014a66"}, - {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4865cd1d419e0c7a7bf6de1777b185eebdc51470800a9f42b9e9decf17762081"}, - {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:303c252947ab4b14c08afeb52375b26781ccd6a5ccd81abcdfc1fafd14cf93c1"}, - {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637f674226be46f6ba372fd29d9523dd977a291f66ab2a74fbeb5530bb3f445d"}, - {file = "contourpy-1.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:76a896b2f195b57db25d6b44e7e03f221d32fe318d03ede41f8b4d9ba1bff53c"}, - {file = "contourpy-1.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e1fd23e9d01591bab45546c089ae89d926917a66dceb3abcf01f6105d927e2cb"}, - {file = "contourpy-1.3.0-cp311-cp311-win32.whl", hash = "sha256:d402880b84df3bec6eab53cd0cf802cae6a2ef9537e70cf75e91618a3801c20c"}, - {file = "contourpy-1.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:6cb6cc968059db9c62cb35fbf70248f40994dfcd7aa10444bbf8b3faeb7c2d67"}, - {file = "contourpy-1.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:570ef7cf892f0afbe5b2ee410c507ce12e15a5fa91017a0009f79f7d93a1268f"}, - {file = "contourpy-1.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:da84c537cb8b97d153e9fb208c221c45605f73147bd4cadd23bdae915042aad6"}, - {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0be4d8425bfa755e0fd76ee1e019636ccc7c29f77a7c86b4328a9eb6a26d0639"}, - {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c0da700bf58f6e0b65312d0a5e695179a71d0163957fa381bb3c1f72972537c"}, - {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb8b141bb00fa977d9122636b16aa67d37fd40a3d8b52dd837e536d64b9a4d06"}, - {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3634b5385c6716c258d0419c46d05c8aa7dc8cb70326c9a4fb66b69ad2b52e09"}, - {file = "contourpy-1.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0dce35502151b6bd35027ac39ba6e5a44be13a68f55735c3612c568cac3805fd"}, - {file = "contourpy-1.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aea348f053c645100612b333adc5983d87be69acdc6d77d3169c090d3b01dc35"}, - {file = "contourpy-1.3.0-cp312-cp312-win32.whl", hash = "sha256:90f73a5116ad1ba7174341ef3ea5c3150ddf20b024b98fb0c3b29034752c8aeb"}, - {file = "contourpy-1.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:b11b39aea6be6764f84360fce6c82211a9db32a7c7de8fa6dd5397cf1d079c3b"}, - {file = "contourpy-1.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3e1c7fa44aaae40a2247e2e8e0627f4bea3dd257014764aa644f319a5f8600e3"}, - {file = "contourpy-1.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:364174c2a76057feef647c802652f00953b575723062560498dc7930fc9b1cb7"}, - {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32b238b3b3b649e09ce9aaf51f0c261d38644bdfa35cbaf7b263457850957a84"}, - {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d51fca85f9f7ad0b65b4b9fe800406d0d77017d7270d31ec3fb1cc07358fdea0"}, - {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:732896af21716b29ab3e988d4ce14bc5133733b85956316fb0c56355f398099b"}, - {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d73f659398a0904e125280836ae6f88ba9b178b2fed6884f3b1f95b989d2c8da"}, - {file = "contourpy-1.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c6c7c2408b7048082932cf4e641fa3b8ca848259212f51c8c59c45aa7ac18f14"}, - {file = "contourpy-1.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f317576606de89da6b7e0861cf6061f6146ead3528acabff9236458a6ba467f8"}, - {file = "contourpy-1.3.0-cp313-cp313-win32.whl", hash = "sha256:31cd3a85dbdf1fc002280c65caa7e2b5f65e4a973fcdf70dd2fdcb9868069294"}, - {file = "contourpy-1.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:4553c421929ec95fb07b3aaca0fae668b2eb5a5203d1217ca7c34c063c53d087"}, - {file = "contourpy-1.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:345af746d7766821d05d72cb8f3845dfd08dd137101a2cb9b24de277d716def8"}, - {file = "contourpy-1.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3bb3808858a9dc68f6f03d319acd5f1b8a337e6cdda197f02f4b8ff67ad2057b"}, - {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:420d39daa61aab1221567b42eecb01112908b2cab7f1b4106a52caaec8d36973"}, - {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d63ee447261e963af02642ffcb864e5a2ee4cbfd78080657a9880b8b1868e18"}, - {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:167d6c890815e1dac9536dca00828b445d5d0df4d6a8c6adb4a7ec3166812fa8"}, - {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:710a26b3dc80c0e4febf04555de66f5fd17e9cf7170a7b08000601a10570bda6"}, - {file = "contourpy-1.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:75ee7cb1a14c617f34a51d11fa7524173e56551646828353c4af859c56b766e2"}, - {file = "contourpy-1.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:33c92cdae89ec5135d036e7218e69b0bb2851206077251f04a6c4e0e21f03927"}, - {file = "contourpy-1.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a11077e395f67ffc2c44ec2418cfebed032cd6da3022a94fc227b6faf8e2acb8"}, - {file = "contourpy-1.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e8134301d7e204c88ed7ab50028ba06c683000040ede1d617298611f9dc6240c"}, - {file = "contourpy-1.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e12968fdfd5bb45ffdf6192a590bd8ddd3ba9e58360b29683c6bb71a7b41edca"}, - {file = "contourpy-1.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fd2a0fc506eccaaa7595b7e1418951f213cf8255be2600f1ea1b61e46a60c55f"}, - {file = "contourpy-1.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4cfb5c62ce023dfc410d6059c936dcf96442ba40814aefbfa575425a3a7f19dc"}, - {file = "contourpy-1.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68a32389b06b82c2fdd68276148d7b9275b5f5cf13e5417e4252f6d1a34f72a2"}, - {file = "contourpy-1.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:94e848a6b83da10898cbf1311a815f770acc9b6a3f2d646f330d57eb4e87592e"}, - {file = "contourpy-1.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d78ab28a03c854a873787a0a42254a0ccb3cb133c672f645c9f9c8f3ae9d0800"}, - {file = "contourpy-1.3.0-cp39-cp39-win32.whl", hash = "sha256:81cb5ed4952aae6014bc9d0421dec7c5835c9c8c31cdf51910b708f548cf58e5"}, - {file = "contourpy-1.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:14e262f67bd7e6eb6880bc564dcda30b15e351a594657e55b7eec94b6ef72843"}, - {file = "contourpy-1.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fe41b41505a5a33aeaed2a613dccaeaa74e0e3ead6dd6fd3a118fb471644fd6c"}, - {file = "contourpy-1.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eca7e17a65f72a5133bdbec9ecf22401c62bcf4821361ef7811faee695799779"}, - {file = "contourpy-1.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:1ec4dc6bf570f5b22ed0d7efba0dfa9c5b9e0431aeea7581aa217542d9e809a4"}, - {file = "contourpy-1.3.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:00ccd0dbaad6d804ab259820fa7cb0b8036bda0686ef844d24125d8287178ce0"}, - {file = "contourpy-1.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ca947601224119117f7c19c9cdf6b3ab54c5726ef1d906aa4a69dfb6dd58102"}, - {file = "contourpy-1.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6ec93afeb848a0845a18989da3beca3eec2c0f852322efe21af1931147d12cb"}, - {file = "contourpy-1.3.0.tar.gz", hash = "sha256:7ffa0db17717a8ffb127efd0c95a4362d996b892c2904db72428d5b52e1938a4"}, +python-versions = ">=3.10" +files = [ + {file = "contourpy-1.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a045f341a77b77e1c5de31e74e966537bba9f3c4099b35bf4c2e3939dd54cdab"}, + {file = "contourpy-1.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:500360b77259914f7805af7462e41f9cb7ca92ad38e9f94d6c8641b089338124"}, + {file = "contourpy-1.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2f926efda994cdf3c8d3fdb40b9962f86edbc4457e739277b961eced3d0b4c1"}, + {file = "contourpy-1.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:adce39d67c0edf383647a3a007de0a45fd1b08dedaa5318404f1a73059c2512b"}, + {file = "contourpy-1.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abbb49fb7dac584e5abc6636b7b2a7227111c4f771005853e7d25176daaf8453"}, + {file = "contourpy-1.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0cffcbede75c059f535725c1680dfb17b6ba8753f0c74b14e6a9c68c29d7ea3"}, + {file = "contourpy-1.3.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ab29962927945d89d9b293eabd0d59aea28d887d4f3be6c22deaefbb938a7277"}, + {file = "contourpy-1.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:974d8145f8ca354498005b5b981165b74a195abfae9a8129df3e56771961d595"}, + {file = "contourpy-1.3.1-cp310-cp310-win32.whl", hash = "sha256:ac4578ac281983f63b400f7fe6c101bedc10651650eef012be1ccffcbacf3697"}, + {file = "contourpy-1.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:174e758c66bbc1c8576992cec9599ce8b6672b741b5d336b5c74e35ac382b18e"}, + {file = "contourpy-1.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3e8b974d8db2c5610fb4e76307e265de0edb655ae8169e8b21f41807ccbeec4b"}, + {file = "contourpy-1.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:20914c8c973f41456337652a6eeca26d2148aa96dd7ac323b74516988bea89fc"}, + {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19d40d37c1c3a4961b4619dd9d77b12124a453cc3d02bb31a07d58ef684d3d86"}, + {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:113231fe3825ebf6f15eaa8bc1f5b0ddc19d42b733345eae0934cb291beb88b6"}, + {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4dbbc03a40f916a8420e420d63e96a1258d3d1b58cbdfd8d1f07b49fcbd38e85"}, + {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a04ecd68acbd77fa2d39723ceca4c3197cb2969633836ced1bea14e219d077c"}, + {file = "contourpy-1.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c414fc1ed8ee1dbd5da626cf3710c6013d3d27456651d156711fa24f24bd1291"}, + {file = "contourpy-1.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:31c1b55c1f34f80557d3830d3dd93ba722ce7e33a0b472cba0ec3b6535684d8f"}, + {file = "contourpy-1.3.1-cp311-cp311-win32.whl", hash = "sha256:f611e628ef06670df83fce17805c344710ca5cde01edfdc72751311da8585375"}, + {file = "contourpy-1.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:b2bdca22a27e35f16794cf585832e542123296b4687f9fd96822db6bae17bfc9"}, + {file = "contourpy-1.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0ffa84be8e0bd33410b17189f7164c3589c229ce5db85798076a3fa136d0e509"}, + {file = "contourpy-1.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805617228ba7e2cbbfb6c503858e626ab528ac2a32a04a2fe88ffaf6b02c32bc"}, + {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ade08d343436a94e633db932e7e8407fe7de8083967962b46bdfc1b0ced39454"}, + {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:47734d7073fb4590b4a40122b35917cd77be5722d80683b249dac1de266aac80"}, + {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2ba94a401342fc0f8b948e57d977557fbf4d515f03c67682dd5c6191cb2d16ec"}, + {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efa874e87e4a647fd2e4f514d5e91c7d493697127beb95e77d2f7561f6905bd9"}, + {file = "contourpy-1.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1bf98051f1045b15c87868dbaea84f92408337d4f81d0e449ee41920ea121d3b"}, + {file = "contourpy-1.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:61332c87493b00091423e747ea78200659dc09bdf7fd69edd5e98cef5d3e9a8d"}, + {file = "contourpy-1.3.1-cp312-cp312-win32.whl", hash = "sha256:e914a8cb05ce5c809dd0fe350cfbb4e881bde5e2a38dc04e3afe1b3e58bd158e"}, + {file = "contourpy-1.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:08d9d449a61cf53033612cb368f3a1b26cd7835d9b8cd326647efe43bca7568d"}, + {file = "contourpy-1.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a761d9ccfc5e2ecd1bf05534eda382aa14c3e4f9205ba5b1684ecfe400716ef2"}, + {file = "contourpy-1.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:523a8ee12edfa36f6d2a49407f705a6ef4c5098de4f498619787e272de93f2d5"}, + {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece6df05e2c41bd46776fbc712e0996f7c94e0d0543af1656956d150c4ca7c81"}, + {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:573abb30e0e05bf31ed067d2f82500ecfdaec15627a59d63ea2d95714790f5c2"}, + {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9fa36448e6a3a1a9a2ba23c02012c43ed88905ec80163f2ffe2421c7192a5d7"}, + {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ea9924d28fc5586bf0b42d15f590b10c224117e74409dd7a0be3b62b74a501c"}, + {file = "contourpy-1.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5b75aa69cb4d6f137b36f7eb2ace9280cfb60c55dc5f61c731fdf6f037f958a3"}, + {file = "contourpy-1.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:041b640d4ec01922083645a94bb3b2e777e6b626788f4095cf21abbe266413c1"}, + {file = "contourpy-1.3.1-cp313-cp313-win32.whl", hash = "sha256:36987a15e8ace5f58d4d5da9dca82d498c2bbb28dff6e5d04fbfcc35a9cb3a82"}, + {file = "contourpy-1.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:a7895f46d47671fa7ceec40f31fae721da51ad34bdca0bee83e38870b1f47ffd"}, + {file = "contourpy-1.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:9ddeb796389dadcd884c7eb07bd14ef12408aaae358f0e2ae24114d797eede30"}, + {file = "contourpy-1.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:19c1555a6801c2f084c7ddc1c6e11f02eb6a6016ca1318dd5452ba3f613a1751"}, + {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:841ad858cff65c2c04bf93875e384ccb82b654574a6d7f30453a04f04af71342"}, + {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4318af1c925fb9a4fb190559ef3eec206845f63e80fb603d47f2d6d67683901c"}, + {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:14c102b0eab282427b662cb590f2e9340a9d91a1c297f48729431f2dcd16e14f"}, + {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05e806338bfeaa006acbdeba0ad681a10be63b26e1b17317bfac3c5d98f36cda"}, + {file = "contourpy-1.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4d76d5993a34ef3df5181ba3c92fabb93f1eaa5729504fb03423fcd9f3177242"}, + {file = "contourpy-1.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:89785bb2a1980c1bd87f0cb1517a71cde374776a5f150936b82580ae6ead44a1"}, + {file = "contourpy-1.3.1-cp313-cp313t-win32.whl", hash = "sha256:8eb96e79b9f3dcadbad2a3891672f81cdcab7f95b27f28f1c67d75f045b6b4f1"}, + {file = "contourpy-1.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:287ccc248c9e0d0566934e7d606201abd74761b5703d804ff3df8935f523d546"}, + {file = "contourpy-1.3.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b457d6430833cee8e4b8e9b6f07aa1c161e5e0d52e118dc102c8f9bd7dd060d6"}, + {file = "contourpy-1.3.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb76c1a154b83991a3cbbf0dfeb26ec2833ad56f95540b442c73950af2013750"}, + {file = "contourpy-1.3.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:44a29502ca9c7b5ba389e620d44f2fbe792b1fb5734e8b931ad307071ec58c53"}, + {file = "contourpy-1.3.1.tar.gz", hash = "sha256:dfd97abd83335045a913e3bcc4a09c0ceadbe66580cf573fe961f4a825efa699"}, ] [package.dependencies] @@ -693,42 +664,42 @@ tests = ["pytest", "pytest-cov", "pytest-xdist"] [[package]] name = "dask" -version = "2024.8.0" +version = "2024.12.0" description = "Parallel PyData with Task Scheduling" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" files = [ - {file = "dask-2024.8.0-py3-none-any.whl", hash = "sha256:250ea3df30d4a25958290eec4f252850091c6cfaed82d098179c3b25bba18309"}, - {file = "dask-2024.8.0.tar.gz", hash = "sha256:f1fec39373d2f101bc045529ad4e9b30e34e6eb33b7aa0fa7073aec7b1bf9eee"}, + {file = "dask-2024.12.0-py3-none-any.whl", hash = "sha256:e038e87b9f06e7927b81ecde6cf2b49aa699bb902fec11abba5697cb48baeb8d"}, + {file = "dask-2024.12.0.tar.gz", hash = "sha256:ffd02b06ac06b993df0b48e0ba4fe02abceb5c8b34b40bd91d63f33ec7a272a4"}, ] [package.dependencies] click = ">=8.1" -cloudpickle = ">=1.5.0" +cloudpickle = ">=3.0.0" fsspec = ">=2021.09.0" -importlib-metadata = {version = ">=4.13.0", markers = "python_version < \"3.12\""} +importlib_metadata = {version = ">=4.13.0", markers = "python_version < \"3.12\""} packaging = ">=20.0" partd = ">=1.4.0" pyyaml = ">=5.3.1" toolz = ">=0.10.0" [package.extras] -array = ["numpy (>=1.21)"] -complete = ["dask[array,dataframe,diagnostics,distributed]", "lz4 (>=4.3.2)", "pyarrow (>=7.0)", "pyarrow-hotfix"] +array = ["numpy (>=1.24)"] +complete = ["dask[array,dataframe,diagnostics,distributed]", "lz4 (>=4.3.2)", "pyarrow (>=14.0.1)"] dataframe = ["dask-expr (>=1.1,<1.2)", "dask[array]", "pandas (>=2.0)"] -diagnostics = ["bokeh (>=2.4.2)", "jinja2 (>=2.10.3)"] -distributed = ["distributed (==2024.8.0)"] +diagnostics = ["bokeh (>=3.1.0)", "jinja2 (>=2.10.3)"] +distributed = ["distributed (==2024.12.0)"] test = ["pandas[test]", "pre-commit", "pytest", "pytest-cov", "pytest-rerunfailures", "pytest-timeout", "pytest-xdist"] [[package]] name = "databricks-sdk" -version = "0.36.0" +version = "0.38.0" description = "Databricks SDK for Python (Beta)" optional = false python-versions = ">=3.7" files = [ - {file = "databricks_sdk-0.36.0-py3-none-any.whl", hash = "sha256:e6105a2752c7980de35f7c7e3c4d63389c0763c9ef7bf7e2813e464acef907e9"}, - {file = "databricks_sdk-0.36.0.tar.gz", hash = "sha256:d8c46348cbd3e0b56991a6b7a59d7a6e0437947f6387bef832e6fe092e2dd427"}, + {file = "databricks_sdk-0.38.0-py3-none-any.whl", hash = "sha256:3cc3808e7a294ccf99a3f19f1e86c8e36a5dc0845ac62112dcae2e625ef97c28"}, + {file = "databricks_sdk-0.38.0.tar.gz", hash = "sha256:65e505201b65d8a2b4110d3eabfebce5a25426d3ccdd5f8bc69eb03333ea1f39"}, ] [package.dependencies] @@ -742,37 +713,37 @@ openai = ["httpx", "langchain-openai", "openai"] [[package]] name = "debugpy" -version = "1.8.7" +version = "1.8.9" description = "An implementation of the Debug Adapter Protocol for Python" optional = false python-versions = ">=3.8" files = [ - {file = "debugpy-1.8.7-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:95fe04a573b8b22896c404365e03f4eda0ce0ba135b7667a1e57bd079793b96b"}, - {file = "debugpy-1.8.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:628a11f4b295ffb4141d8242a9bb52b77ad4a63a2ad19217a93be0f77f2c28c9"}, - {file = "debugpy-1.8.7-cp310-cp310-win32.whl", hash = "sha256:85ce9c1d0eebf622f86cc68618ad64bf66c4fc3197d88f74bb695a416837dd55"}, - {file = "debugpy-1.8.7-cp310-cp310-win_amd64.whl", hash = "sha256:29e1571c276d643757ea126d014abda081eb5ea4c851628b33de0c2b6245b037"}, - {file = "debugpy-1.8.7-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:caf528ff9e7308b74a1749c183d6808ffbedbb9fb6af78b033c28974d9b8831f"}, - {file = "debugpy-1.8.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cba1d078cf2e1e0b8402e6bda528bf8fda7ccd158c3dba6c012b7897747c41a0"}, - {file = "debugpy-1.8.7-cp311-cp311-win32.whl", hash = "sha256:171899588bcd412151e593bd40d9907133a7622cd6ecdbdb75f89d1551df13c2"}, - {file = "debugpy-1.8.7-cp311-cp311-win_amd64.whl", hash = "sha256:6e1c4ffb0c79f66e89dfd97944f335880f0d50ad29525dc792785384923e2211"}, - {file = "debugpy-1.8.7-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:4d27d842311353ede0ad572600c62e4bcd74f458ee01ab0dd3a1a4457e7e3706"}, - {file = "debugpy-1.8.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:703c1fd62ae0356e194f3e7b7a92acd931f71fe81c4b3be2c17a7b8a4b546ec2"}, - {file = "debugpy-1.8.7-cp312-cp312-win32.whl", hash = "sha256:2f729228430ef191c1e4df72a75ac94e9bf77413ce5f3f900018712c9da0aaca"}, - {file = "debugpy-1.8.7-cp312-cp312-win_amd64.whl", hash = "sha256:45c30aaefb3e1975e8a0258f5bbd26cd40cde9bfe71e9e5a7ac82e79bad64e39"}, - {file = "debugpy-1.8.7-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:d050a1ec7e925f514f0f6594a1e522580317da31fbda1af71d1530d6ea1f2b40"}, - {file = "debugpy-1.8.7-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2f4349a28e3228a42958f8ddaa6333d6f8282d5edaea456070e48609c5983b7"}, - {file = "debugpy-1.8.7-cp313-cp313-win32.whl", hash = "sha256:11ad72eb9ddb436afb8337891a986302e14944f0f755fd94e90d0d71e9100bba"}, - {file = "debugpy-1.8.7-cp313-cp313-win_amd64.whl", hash = "sha256:2efb84d6789352d7950b03d7f866e6d180284bc02c7e12cb37b489b7083d81aa"}, - {file = "debugpy-1.8.7-cp38-cp38-macosx_14_0_x86_64.whl", hash = "sha256:4b908291a1d051ef3331484de8e959ef3e66f12b5e610c203b5b75d2725613a7"}, - {file = "debugpy-1.8.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da8df5b89a41f1fd31503b179d0a84a5fdb752dddd5b5388dbd1ae23cda31ce9"}, - {file = "debugpy-1.8.7-cp38-cp38-win32.whl", hash = "sha256:b12515e04720e9e5c2216cc7086d0edadf25d7ab7e3564ec8b4521cf111b4f8c"}, - {file = "debugpy-1.8.7-cp38-cp38-win_amd64.whl", hash = "sha256:93176e7672551cb5281577cdb62c63aadc87ec036f0c6a486f0ded337c504596"}, - {file = "debugpy-1.8.7-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:90d93e4f2db442f8222dec5ec55ccfc8005821028982f1968ebf551d32b28907"}, - {file = "debugpy-1.8.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6db2a370e2700557a976eaadb16243ec9c91bd46f1b3bb15376d7aaa7632c81"}, - {file = "debugpy-1.8.7-cp39-cp39-win32.whl", hash = "sha256:a6cf2510740e0c0b4a40330640e4b454f928c7b99b0c9dbf48b11efba08a8cda"}, - {file = "debugpy-1.8.7-cp39-cp39-win_amd64.whl", hash = "sha256:6a9d9d6d31846d8e34f52987ee0f1a904c7baa4912bf4843ab39dadf9b8f3e0d"}, - {file = "debugpy-1.8.7-py2.py3-none-any.whl", hash = "sha256:57b00de1c8d2c84a61b90880f7e5b6deaf4c312ecbde3a0e8912f2a56c4ac9ae"}, - {file = "debugpy-1.8.7.zip", hash = "sha256:18b8f731ed3e2e1df8e9cdaa23fb1fc9c24e570cd0081625308ec51c82efe42e"}, + {file = "debugpy-1.8.9-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:cfe1e6c6ad7178265f74981edf1154ffce97b69005212fbc90ca22ddfe3d017e"}, + {file = "debugpy-1.8.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ada7fb65102a4d2c9ab62e8908e9e9f12aed9d76ef44880367bc9308ebe49a0f"}, + {file = "debugpy-1.8.9-cp310-cp310-win32.whl", hash = "sha256:c36856343cbaa448171cba62a721531e10e7ffb0abff838004701454149bc037"}, + {file = "debugpy-1.8.9-cp310-cp310-win_amd64.whl", hash = "sha256:17c5e0297678442511cf00a745c9709e928ea4ca263d764e90d233208889a19e"}, + {file = "debugpy-1.8.9-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:b74a49753e21e33e7cf030883a92fa607bddc4ede1aa4145172debc637780040"}, + {file = "debugpy-1.8.9-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62d22dacdb0e296966d7d74a7141aaab4bec123fa43d1a35ddcb39bf9fd29d70"}, + {file = "debugpy-1.8.9-cp311-cp311-win32.whl", hash = "sha256:8138efff315cd09b8dcd14226a21afda4ca582284bf4215126d87342bba1cc66"}, + {file = "debugpy-1.8.9-cp311-cp311-win_amd64.whl", hash = "sha256:ff54ef77ad9f5c425398efb150239f6fe8e20c53ae2f68367eba7ece1e96226d"}, + {file = "debugpy-1.8.9-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:957363d9a7a6612a37458d9a15e72d03a635047f946e5fceee74b50d52a9c8e2"}, + {file = "debugpy-1.8.9-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e565fc54b680292b418bb809f1386f17081d1346dca9a871bf69a8ac4071afe"}, + {file = "debugpy-1.8.9-cp312-cp312-win32.whl", hash = "sha256:3e59842d6c4569c65ceb3751075ff8d7e6a6ada209ceca6308c9bde932bcef11"}, + {file = "debugpy-1.8.9-cp312-cp312-win_amd64.whl", hash = "sha256:66eeae42f3137eb428ea3a86d4a55f28da9bd5a4a3d369ba95ecc3a92c1bba53"}, + {file = "debugpy-1.8.9-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:957ecffff80d47cafa9b6545de9e016ae8c9547c98a538ee96ab5947115fb3dd"}, + {file = "debugpy-1.8.9-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1efbb3ff61487e2c16b3e033bc8595aea578222c08aaf3c4bf0f93fadbd662ee"}, + {file = "debugpy-1.8.9-cp313-cp313-win32.whl", hash = "sha256:7c4d65d03bee875bcb211c76c1d8f10f600c305dbd734beaed4077e902606fee"}, + {file = "debugpy-1.8.9-cp313-cp313-win_amd64.whl", hash = "sha256:e46b420dc1bea64e5bbedd678148be512442bc589b0111bd799367cde051e71a"}, + {file = "debugpy-1.8.9-cp38-cp38-macosx_14_0_x86_64.whl", hash = "sha256:472a3994999fe6c0756945ffa359e9e7e2d690fb55d251639d07208dbc37caea"}, + {file = "debugpy-1.8.9-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:365e556a4772d7d0d151d7eb0e77ec4db03bcd95f26b67b15742b88cacff88e9"}, + {file = "debugpy-1.8.9-cp38-cp38-win32.whl", hash = "sha256:54a7e6d3014c408eb37b0b06021366ee985f1539e12fe49ca2ee0d392d9ceca5"}, + {file = "debugpy-1.8.9-cp38-cp38-win_amd64.whl", hash = "sha256:8e99c0b1cc7bf86d83fb95d5ccdc4ad0586d4432d489d1f54e4055bcc795f693"}, + {file = "debugpy-1.8.9-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:7e8b079323a56f719977fde9d8115590cb5e7a1cba2fcee0986ef8817116e7c1"}, + {file = "debugpy-1.8.9-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6953b335b804a41f16a192fa2e7851bdcfd92173cbb2f9f777bb934f49baab65"}, + {file = "debugpy-1.8.9-cp39-cp39-win32.whl", hash = "sha256:7e646e62d4602bb8956db88b1e72fe63172148c1e25c041e03b103a25f36673c"}, + {file = "debugpy-1.8.9-cp39-cp39-win_amd64.whl", hash = "sha256:3d9755e77a2d680ce3d2c5394a444cf42be4a592caaf246dbfbdd100ffcf7ae5"}, + {file = "debugpy-1.8.9-py2.py3-none-any.whl", hash = "sha256:cc37a6c9987ad743d9c3a14fa1b1a14b7e4e6041f9dd0c8abf8895fe7a97b899"}, + {file = "debugpy-1.8.9.zip", hash = "sha256:1339e14c7d980407248f09824d1b25ff5c5616651689f1e0f0e51bdead3ea13e"}, ] [[package]] @@ -799,75 +770,20 @@ files = [ [[package]] name = "deprecated" -version = "1.2.14" +version = "1.2.15" description = "Python @deprecated decorator to deprecate old python classes, functions or methods." optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" files = [ - {file = "Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c"}, - {file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"}, + {file = "Deprecated-1.2.15-py2.py3-none-any.whl", hash = "sha256:353bc4a8ac4bfc96800ddab349d89c25dec1079f65fd53acdcc1e0b975b21320"}, + {file = "deprecated-1.2.15.tar.gz", hash = "sha256:683e561a90de76239796e6b6feac66b99030d2dd3fcf61ef996330f14bbb9b0d"}, ] [package.dependencies] wrapt = ">=1.10,<2" [package.extras] -dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] - -[[package]] -name = "dm-tree" -version = "0.1.8" -description = "Tree is a library for working with nested data structures." -optional = false -python-versions = "*" -files = [ - {file = "dm-tree-0.1.8.tar.gz", hash = "sha256:0fcaabbb14e7980377439e7140bd05552739ca5e515ecb3119f234acee4b9430"}, - {file = "dm_tree-0.1.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:35cc164a79336bfcfafb47e5f297898359123bbd3330c1967f0c4994f9cf9f60"}, - {file = "dm_tree-0.1.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39070ba268c0491af9fe7a58644d99e8b4f2cde6e5884ba3380bddc84ed43d5f"}, - {file = "dm_tree-0.1.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2869228d9c619074de501a3c10dc7f07c75422f8fab36ecdcb859b6f1b1ec3ef"}, - {file = "dm_tree-0.1.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d20f2faa3672b52e5013f4077117bfb99c4cfc0b445d3bde1584c34032b57436"}, - {file = "dm_tree-0.1.8-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5483dca4d7eb1a0d65fe86d3b6a53ae717face83c1f17e0887b1a4a64ae5c410"}, - {file = "dm_tree-0.1.8-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1d7c26e431fc93cc7e0cba867eb000db6a05f6f2b25af11ac4e9dada88fc5bca"}, - {file = "dm_tree-0.1.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d714371bb08839e4e5e29024fc95832d9affe129825ef38836b143028bd144"}, - {file = "dm_tree-0.1.8-cp310-cp310-win_amd64.whl", hash = "sha256:d40fa4106ca6edc66760246a08f500ec0c85ef55c762fb4a363f6ee739ba02ee"}, - {file = "dm_tree-0.1.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad16ceba90a56ec47cf45b21856d14962ac314787975ef786efb5e6e9ca75ec7"}, - {file = "dm_tree-0.1.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:803bfc53b4659f447ac694dbd04235f94a73ef7c1fd1e0df7c84ac41e0bc963b"}, - {file = "dm_tree-0.1.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:378cc8ad93c5fe3590f405a309980721f021c790ca1bdf9b15bb1d59daec57f5"}, - {file = "dm_tree-0.1.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1607ce49aa42f010d1e5e616d92ce899d66835d4d8bea49679582435285515de"}, - {file = "dm_tree-0.1.8-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:343a4a4ebaa127451ff971254a4be4084eb4bdc0b2513c32b46f6f728fd03f9e"}, - {file = "dm_tree-0.1.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa42a605d099ee7d41ba2b5fb75e21423951fd26e5d50583a00471238fb3021d"}, - {file = "dm_tree-0.1.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b7764de0d855338abefc6e3ee9fe40d301668310aa3baea3f778ff051f4393"}, - {file = "dm_tree-0.1.8-cp311-cp311-win_amd64.whl", hash = "sha256:a5d819c38c03f0bb5b3b3703c60e4b170355a0fc6b5819325bf3d4ceb3ae7e80"}, - {file = "dm_tree-0.1.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ea9e59e0451e7d29aece402d9f908f2e2a80922bcde2ebfd5dcb07750fcbfee8"}, - {file = "dm_tree-0.1.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:94d3f0826311f45ee19b75f5b48c99466e4218a0489e81c0f0167bda50cacf22"}, - {file = "dm_tree-0.1.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:435227cf3c5dc63f4de054cf3d00183790bd9ead4c3623138c74dde7f67f521b"}, - {file = "dm_tree-0.1.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09964470f76a5201aff2e8f9b26842976de7889300676f927930f6285e256760"}, - {file = "dm_tree-0.1.8-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:75c5d528bb992981c20793b6b453e91560784215dffb8a5440ba999753c14ceb"}, - {file = "dm_tree-0.1.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0a94aba18a35457a1b5cd716fd7b46c5dafdc4cf7869b4bae665b91c4682a8e"}, - {file = "dm_tree-0.1.8-cp312-cp312-win_amd64.whl", hash = "sha256:96a548a406a6fb15fe58f6a30a57ff2f2aafbf25f05afab00c8f5e5977b6c715"}, - {file = "dm_tree-0.1.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8c60a7eadab64c2278861f56bca320b2720f163dca9d7558103c3b77f2416571"}, - {file = "dm_tree-0.1.8-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af4b3d372f2477dcd89a6e717e4a575ca35ccc20cc4454a8a4b6f8838a00672d"}, - {file = "dm_tree-0.1.8-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de287fabc464b8734be251e46e06aa9aa1001f34198da2b6ce07bd197172b9cb"}, - {file = "dm_tree-0.1.8-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:054b461f8176f4bce7a21f7b1870f873a1ced3bdbe1282c816c550bb43c71fa6"}, - {file = "dm_tree-0.1.8-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f7915660f59c09068e428613c480150180df1060561fd0d1470684ae7007bd1"}, - {file = "dm_tree-0.1.8-cp37-cp37m-win_amd64.whl", hash = "sha256:b9f89a454e98806b44fe9d40ec9eee61f848388f7e79ac2371a55679bd5a3ac6"}, - {file = "dm_tree-0.1.8-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0e9620ccf06393eb6b613b5e366469304622d4ea96ae6540b28a33840e6c89cf"}, - {file = "dm_tree-0.1.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b095ba4f8ca1ba19350fd53cf1f8f3eb0bd406aa28af64a6dfc86707b32a810a"}, - {file = "dm_tree-0.1.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b9bd9b9ccb59409d33d51d84b7668010c04c2af7d4a371632874c1ca356cff3d"}, - {file = "dm_tree-0.1.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d3172394079a86c3a759179c65f64c48d1a42b89495fcf38976d11cc3bb952c"}, - {file = "dm_tree-0.1.8-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1612fcaecd79023dbc6a6ae48d51a80beb5c385d6f3f6d71688e57bc8d07de8"}, - {file = "dm_tree-0.1.8-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c5c8c12e3fda754ef6af94161bacdaeda816d941995fac415d6855c6c386af68"}, - {file = "dm_tree-0.1.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:694c3654cfd2a81552c08ec66bb5c4a3d48fa292b9a181880fb081c36c5b9134"}, - {file = "dm_tree-0.1.8-cp38-cp38-win_amd64.whl", hash = "sha256:bb2d109f42190225112da899b9f3d46d0d5f26aef501c61e43529fe9322530b5"}, - {file = "dm_tree-0.1.8-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d16e1f2a073604cfcc09f7131ae8d534674f43c3aef4c25742eae295bc60d04f"}, - {file = "dm_tree-0.1.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:250b692fb75f45f02e2f58fbef9ab338904ef334b90557565621fa251df267cf"}, - {file = "dm_tree-0.1.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:81fce77f22a302d7a5968aebdf4efafef4def7ce96528719a354e6990dcd49c7"}, - {file = "dm_tree-0.1.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7ac31b9aecccb2c6e1ab29706f6ded3eba0c2c69c770322c9c685929c3d6afb"}, - {file = "dm_tree-0.1.8-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fe962015b2fe1282892b28ebe962faed53c7f98d942da9a4625cbf27baef913"}, - {file = "dm_tree-0.1.8-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c52cbf4f8b3dbd0beaedf44f69fa85eec5e9dede612e08035e06ada6ec9426"}, - {file = "dm_tree-0.1.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:181c35521d480d0365f39300542cb6cd7fd2b77351bb43d7acfda15aef63b317"}, - {file = "dm_tree-0.1.8-cp39-cp39-win_amd64.whl", hash = "sha256:8ed3564abed97c806db122c2d3e1a2b64c74a63debe9903aad795167cc301368"}, -] +dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "jinja2 (>=3.0.3,<3.1.0)", "setuptools", "sphinx (<2)", "tox"] [[package]] name = "docker" @@ -932,107 +848,113 @@ files = [ [[package]] name = "fastjsonschema" -version = "2.20.0" +version = "2.21.1" description = "Fastest Python implementation of JSON schema" optional = false python-versions = "*" files = [ - {file = "fastjsonschema-2.20.0-py3-none-any.whl", hash = "sha256:5875f0b0fa7a0043a91e93a9b8f793bcbbba9691e7fd83dca95c28ba26d21f0a"}, - {file = "fastjsonschema-2.20.0.tar.gz", hash = "sha256:3d48fc5300ee96f5d116f10fe6f28d938e6008f59a6a025c2649475b87f76a23"}, + {file = "fastjsonschema-2.21.1-py3-none-any.whl", hash = "sha256:c9e5b7e908310918cf494a434eeb31384dd84a98b57a30bcb1f535015b554667"}, + {file = "fastjsonschema-2.21.1.tar.gz", hash = "sha256:794d4f0a58f848961ba16af7b9c85a3e88cd360df008c59aac6fc5ae9323b5d4"}, ] [package.extras] devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benchmark", "pytest-cache", "validictory"] +[[package]] +name = "filelock" +version = "3.16.1" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.8" +files = [ + {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, + {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, +] + +[package.extras] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] +typing = ["typing-extensions (>=4.12.2)"] + [[package]] name = "flask" -version = "3.0.3" +version = "3.1.0" description = "A simple framework for building complex web applications." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "flask-3.0.3-py3-none-any.whl", hash = "sha256:34e815dfaa43340d1d15a5c3a02b8476004037eb4840b34910c6e21679d288f3"}, - {file = "flask-3.0.3.tar.gz", hash = "sha256:ceb27b0af3823ea2737928a4d99d125a06175b8512c445cbd9a9ce200ef76842"}, + {file = "flask-3.1.0-py3-none-any.whl", hash = "sha256:d667207822eb83f1c4b50949b1623c8fc8d51f2341d65f72e1a1815397551136"}, + {file = "flask-3.1.0.tar.gz", hash = "sha256:5f873c5184c897c8d9d1b05df1e3d01b14910ce69607a117bd3277098a5836ac"}, ] [package.dependencies] -blinker = ">=1.6.2" +blinker = ">=1.9" click = ">=8.1.3" -importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} -itsdangerous = ">=2.1.2" +itsdangerous = ">=2.2" Jinja2 = ">=3.1.2" -Werkzeug = ">=3.0.0" +Werkzeug = ">=3.1" [package.extras] async = ["asgiref (>=3.2)"] dotenv = ["python-dotenv"] -[[package]] -name = "flatbuffers" -version = "24.3.25" -description = "The FlatBuffers serialization format for Python" -optional = false -python-versions = "*" -files = [ - {file = "flatbuffers-24.3.25-py2.py3-none-any.whl", hash = "sha256:8dbdec58f935f3765e4f7f3cf635ac3a77f83568138d6a2311f524ec96364812"}, - {file = "flatbuffers-24.3.25.tar.gz", hash = "sha256:de2ec5b203f21441716617f38443e0a8ebf3d25bf0d9c0bb0ce68fa00ad546a4"}, -] - [[package]] name = "fonttools" -version = "4.54.1" +version = "4.55.2" description = "Tools to manipulate font files" optional = false python-versions = ">=3.8" files = [ - {file = "fonttools-4.54.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ed7ee041ff7b34cc62f07545e55e1468808691dddfd315d51dd82a6b37ddef2"}, - {file = "fonttools-4.54.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41bb0b250c8132b2fcac148e2e9198e62ff06f3cc472065dff839327945c5882"}, - {file = "fonttools-4.54.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7965af9b67dd546e52afcf2e38641b5be956d68c425bef2158e95af11d229f10"}, - {file = "fonttools-4.54.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:278913a168f90d53378c20c23b80f4e599dca62fbffae4cc620c8eed476b723e"}, - {file = "fonttools-4.54.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0e88e3018ac809b9662615072dcd6b84dca4c2d991c6d66e1970a112503bba7e"}, - {file = "fonttools-4.54.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4aa4817f0031206e637d1e685251ac61be64d1adef111060df84fdcbc6ab6c44"}, - {file = "fonttools-4.54.1-cp310-cp310-win32.whl", hash = "sha256:7e3b7d44e18c085fd8c16dcc6f1ad6c61b71ff463636fcb13df7b1b818bd0c02"}, - {file = "fonttools-4.54.1-cp310-cp310-win_amd64.whl", hash = "sha256:dd9cc95b8d6e27d01e1e1f1fae8559ef3c02c76317da650a19047f249acd519d"}, - {file = "fonttools-4.54.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5419771b64248484299fa77689d4f3aeed643ea6630b2ea750eeab219588ba20"}, - {file = "fonttools-4.54.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:301540e89cf4ce89d462eb23a89464fef50915255ece765d10eee8b2bf9d75b2"}, - {file = "fonttools-4.54.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76ae5091547e74e7efecc3cbf8e75200bc92daaeb88e5433c5e3e95ea8ce5aa7"}, - {file = "fonttools-4.54.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82834962b3d7c5ca98cb56001c33cf20eb110ecf442725dc5fdf36d16ed1ab07"}, - {file = "fonttools-4.54.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d26732ae002cc3d2ecab04897bb02ae3f11f06dd7575d1df46acd2f7c012a8d8"}, - {file = "fonttools-4.54.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:58974b4987b2a71ee08ade1e7f47f410c367cdfc5a94fabd599c88165f56213a"}, - {file = "fonttools-4.54.1-cp311-cp311-win32.whl", hash = "sha256:ab774fa225238986218a463f3fe151e04d8c25d7de09df7f0f5fce27b1243dbc"}, - {file = "fonttools-4.54.1-cp311-cp311-win_amd64.whl", hash = "sha256:07e005dc454eee1cc60105d6a29593459a06321c21897f769a281ff2d08939f6"}, - {file = "fonttools-4.54.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:54471032f7cb5fca694b5f1a0aaeba4af6e10ae989df408e0216f7fd6cdc405d"}, - {file = "fonttools-4.54.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fa92cb248e573daab8d032919623cc309c005086d743afb014c836636166f08"}, - {file = "fonttools-4.54.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a911591200114969befa7f2cb74ac148bce5a91df5645443371aba6d222e263"}, - {file = "fonttools-4.54.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93d458c8a6a354dc8b48fc78d66d2a8a90b941f7fec30e94c7ad9982b1fa6bab"}, - {file = "fonttools-4.54.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5eb2474a7c5be8a5331146758debb2669bf5635c021aee00fd7c353558fc659d"}, - {file = "fonttools-4.54.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c9c563351ddc230725c4bdf7d9e1e92cbe6ae8553942bd1fb2b2ff0884e8b714"}, - {file = "fonttools-4.54.1-cp312-cp312-win32.whl", hash = "sha256:fdb062893fd6d47b527d39346e0c5578b7957dcea6d6a3b6794569370013d9ac"}, - {file = "fonttools-4.54.1-cp312-cp312-win_amd64.whl", hash = "sha256:e4564cf40cebcb53f3dc825e85910bf54835e8a8b6880d59e5159f0f325e637e"}, - {file = "fonttools-4.54.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6e37561751b017cf5c40fce0d90fd9e8274716de327ec4ffb0df957160be3bff"}, - {file = "fonttools-4.54.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:357cacb988a18aace66e5e55fe1247f2ee706e01debc4b1a20d77400354cddeb"}, - {file = "fonttools-4.54.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8e953cc0bddc2beaf3a3c3b5dd9ab7554677da72dfaf46951e193c9653e515a"}, - {file = "fonttools-4.54.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:58d29b9a294573d8319f16f2f79e42428ba9b6480442fa1836e4eb89c4d9d61c"}, - {file = "fonttools-4.54.1-cp313-cp313-win32.whl", hash = "sha256:9ef1b167e22709b46bf8168368b7b5d3efeaaa746c6d39661c1b4405b6352e58"}, - {file = "fonttools-4.54.1-cp313-cp313-win_amd64.whl", hash = "sha256:262705b1663f18c04250bd1242b0515d3bbae177bee7752be67c979b7d47f43d"}, - {file = "fonttools-4.54.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ed2f80ca07025551636c555dec2b755dd005e2ea8fbeb99fc5cdff319b70b23b"}, - {file = "fonttools-4.54.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9dc080e5a1c3b2656caff2ac2633d009b3a9ff7b5e93d0452f40cd76d3da3b3c"}, - {file = "fonttools-4.54.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d152d1be65652fc65e695e5619e0aa0982295a95a9b29b52b85775243c06556"}, - {file = "fonttools-4.54.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8583e563df41fdecef31b793b4dd3af8a9caa03397be648945ad32717a92885b"}, - {file = "fonttools-4.54.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:0d1d353ef198c422515a3e974a1e8d5b304cd54a4c2eebcae708e37cd9eeffb1"}, - {file = "fonttools-4.54.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:fda582236fee135d4daeca056c8c88ec5f6f6d88a004a79b84a02547c8f57386"}, - {file = "fonttools-4.54.1-cp38-cp38-win32.whl", hash = "sha256:e7d82b9e56716ed32574ee106cabca80992e6bbdcf25a88d97d21f73a0aae664"}, - {file = "fonttools-4.54.1-cp38-cp38-win_amd64.whl", hash = "sha256:ada215fd079e23e060157aab12eba0d66704316547f334eee9ff26f8c0d7b8ab"}, - {file = "fonttools-4.54.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f5b8a096e649768c2f4233f947cf9737f8dbf8728b90e2771e2497c6e3d21d13"}, - {file = "fonttools-4.54.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4e10d2e0a12e18f4e2dd031e1bf7c3d7017be5c8dbe524d07706179f355c5dac"}, - {file = "fonttools-4.54.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31c32d7d4b0958600eac75eaf524b7b7cb68d3a8c196635252b7a2c30d80e986"}, - {file = "fonttools-4.54.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c39287f5c8f4a0c5a55daf9eaf9ccd223ea59eed3f6d467133cc727d7b943a55"}, - {file = "fonttools-4.54.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a7a310c6e0471602fe3bf8efaf193d396ea561486aeaa7adc1f132e02d30c4b9"}, - {file = "fonttools-4.54.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d3b659d1029946f4ff9b6183984578041b520ce0f8fb7078bb37ec7445806b33"}, - {file = "fonttools-4.54.1-cp39-cp39-win32.whl", hash = "sha256:e96bc94c8cda58f577277d4a71f51c8e2129b8b36fd05adece6320dd3d57de8a"}, - {file = "fonttools-4.54.1-cp39-cp39-win_amd64.whl", hash = "sha256:e8a4b261c1ef91e7188a30571be6ad98d1c6d9fa2427244c545e2fa0a2494dd7"}, - {file = "fonttools-4.54.1-py3-none-any.whl", hash = "sha256:37cddd62d83dc4f72f7c3f3c2bcf2697e89a30efb152079896544a93907733bd"}, - {file = "fonttools-4.54.1.tar.gz", hash = "sha256:957f669d4922f92c171ba01bef7f29410668db09f6c02111e22b2bce446f3285"}, + {file = "fonttools-4.55.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:bef0f8603834643b1a6419d57902f18e7d950ec1a998fb70410635c598dc1a1e"}, + {file = "fonttools-4.55.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:944228b86d472612d3b48bcc83b31c25c2271e63fdc74539adfcfa7a96d487fb"}, + {file = "fonttools-4.55.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f0e55f5da594b85f269cfbecd2f6bd3e07d0abba68870bc3f34854de4fa4678"}, + {file = "fonttools-4.55.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b1a6e576db0c83c1b91925bf1363478c4bb968dbe8433147332fb5782ce6190"}, + {file = "fonttools-4.55.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:616368b15716781bc84df5c2191dc0540137aaef56c2771eb4b89b90933f347a"}, + {file = "fonttools-4.55.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7bbae4f3915225c2c37670da68e2bf18a21206060ad31dfb95fec91ef641caa7"}, + {file = "fonttools-4.55.2-cp310-cp310-win32.whl", hash = "sha256:8b02b10648d69d67a7eb055f4d3eedf4a85deb22fb7a19fbd9acbae7c7538199"}, + {file = "fonttools-4.55.2-cp310-cp310-win_amd64.whl", hash = "sha256:bbea0ab841113ac8e8edde067e099b7288ffc6ac2dded538b131c2c0595d5f77"}, + {file = "fonttools-4.55.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d34525e8141286fa976e14806639d32294bfb38d28bbdb5f6be9f46a1cd695a6"}, + {file = "fonttools-4.55.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0ecd1c2b1c2ec46bb73685bc5473c72e16ed0930ef79bc2919ccadc43a99fb16"}, + {file = "fonttools-4.55.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9008438ad59e5a8e403a62fbefef2b2ff377eb3857d90a3f2a5f4d674ff441b2"}, + {file = "fonttools-4.55.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:131591ac8d7a47043aaf29581aba755ae151d46e49d2bf49608601efd71e8b4d"}, + {file = "fonttools-4.55.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4c83381c3e3e3d9caa25527c4300543578341f21aae89e4fbbb4debdda8d82a2"}, + {file = "fonttools-4.55.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:42aca564b575252fd9954ed0d91d97a24de24289a16ce8ff74ed0bdf5ecebf11"}, + {file = "fonttools-4.55.2-cp311-cp311-win32.whl", hash = "sha256:c6457f650ebe15baa17fc06e256227f0a47f46f80f27ec5a0b00160de8dc2c13"}, + {file = "fonttools-4.55.2-cp311-cp311-win_amd64.whl", hash = "sha256:5cfa67414d7414442a5635ff634384101c54f53bb7b0e04aa6a61b013fcce194"}, + {file = "fonttools-4.55.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:18f082445b8fe5e91c53e6184f4c1c73f3f965c8bcc614c6cd6effd573ce6c1a"}, + {file = "fonttools-4.55.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:27c0f91adbbd706e8acd1db73e3e510118e62d0ffb651864567dccc5b2339f90"}, + {file = "fonttools-4.55.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d8ccce035320d63dba0c35f52499322f5531dbe85bba1514c7cea26297e4c54"}, + {file = "fonttools-4.55.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96e126df9615df214ec7f04bebcf60076297fbc10b75c777ce58b702d7708ffb"}, + {file = "fonttools-4.55.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:508ebb42956a7a931c4092dfa2d9b4ffd4f94cea09b8211199090d2bd082506b"}, + {file = "fonttools-4.55.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c1b9de46ef7b683d50400abf9f1578eaceee271ff51c36bf4b7366f2be29f498"}, + {file = "fonttools-4.55.2-cp312-cp312-win32.whl", hash = "sha256:2df61d9fc15199cc86dad29f64dd686874a3a52dda0c2d8597d21f509f95c332"}, + {file = "fonttools-4.55.2-cp312-cp312-win_amd64.whl", hash = "sha256:d337ec087da8216a828574aa0525d869df0a2ac217a2efc1890974ddd1fbc5b9"}, + {file = "fonttools-4.55.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:10aff204e2edee1d312fa595c06f201adf8d528a3b659cfb34cd47eceaaa6a26"}, + {file = "fonttools-4.55.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:09fe922a3eff181fd07dd724cdb441fb6b9fc355fd1c0f1aa79aca60faf1fbdd"}, + {file = "fonttools-4.55.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:487e1e8b524143a799bda0169c48b44a23a6027c1bb1957d5a172a7d3a1dd704"}, + {file = "fonttools-4.55.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b1726872e09268bbedb14dc02e58b7ea31ecdd1204c6073eda4911746b44797"}, + {file = "fonttools-4.55.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6fc88cfb58b0cd7b48718c3e61dd0d0a3ee8e2c86b973342967ce09fbf1db6d4"}, + {file = "fonttools-4.55.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e857fe1859901ad8c5cab32e0eebc920adb09f413d2d73b74b677cf47b28590c"}, + {file = "fonttools-4.55.2-cp313-cp313-win32.whl", hash = "sha256:81ccd2b3a420b8050c7d9db3be0555d71662973b3ef2a1d921a2880b58957db8"}, + {file = "fonttools-4.55.2-cp313-cp313-win_amd64.whl", hash = "sha256:d559eb1744c7dcfa90ae60cb1a4b3595e898e48f4198738c321468c01180cd83"}, + {file = "fonttools-4.55.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6b5917ef79cac8300b88fd6113003fd01bbbbea2ea060a27b95d8f77cb4c65c2"}, + {file = "fonttools-4.55.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:663eba5615d6abaaf616432354eb7ce951d518e43404371bcc2b0694ef21e8d6"}, + {file = "fonttools-4.55.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:803d5cef5fc47f44f5084d154aa3d6f069bb1b60e32390c225f897fa19b0f939"}, + {file = "fonttools-4.55.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8bc5f100de0173cc39102c0399bd6c3bd544bbdf224957933f10ee442d43cddd"}, + {file = "fonttools-4.55.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:3d9bbc1e380fdaf04ad9eabd8e3e6a4301eaf3487940893e9fd98537ea2e283b"}, + {file = "fonttools-4.55.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:42a9afedff07b6f75aa0f39b5e49922ac764580ef3efce035ca30284b2ee65c8"}, + {file = "fonttools-4.55.2-cp38-cp38-win32.whl", hash = "sha256:f1c76f423f1a241df08f87614364dff6e0b7ce23c962c1b74bd995ec7c0dad13"}, + {file = "fonttools-4.55.2-cp38-cp38-win_amd64.whl", hash = "sha256:25062b6ca03464dd5179fc2040fb19e03391b7cc49b9cc4f879312e638605c5c"}, + {file = "fonttools-4.55.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d1100d8e665fe386a79cab59446992de881ea74d0d6c191bb988642692aa2421"}, + {file = "fonttools-4.55.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dbdc251c5e472e5ae6bc816f9b82718b8e93ff7992e7331d6cf3562b96aa268e"}, + {file = "fonttools-4.55.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0bf24d2b02dbc9376d795a63062632ff73e3e9e60c0229373f500aed7e86dd7"}, + {file = "fonttools-4.55.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4ff250ed4ff05015dfd9cf2adf7570c7a383ca80f4d9732ac484a5ed0d8453c"}, + {file = "fonttools-4.55.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:44cf2a98aa661dbdeb8c03f5e405b074e2935196780bb729888639f5276067d9"}, + {file = "fonttools-4.55.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22ef222740eb89d189bf0612eb98fbae592c61d7efeac51bfbc2a1592d469557"}, + {file = "fonttools-4.55.2-cp39-cp39-win32.whl", hash = "sha256:93f439ca27e55f585e7aaa04a74990acd983b5f2245e41d6b79f0a8b44e684d8"}, + {file = "fonttools-4.55.2-cp39-cp39-win_amd64.whl", hash = "sha256:627cf10d6f5af5bec6324c18a2670f134c29e1b7dce3fb62e8ef88baa6cba7a9"}, + {file = "fonttools-4.55.2-py3-none-any.whl", hash = "sha256:8e2d89fbe9b08d96e22c7a81ec04a4e8d8439c31223e2dc6f2f9fc8ff14bdf9f"}, + {file = "fonttools-4.55.2.tar.gz", hash = "sha256:45947e7b3f9673f91df125d375eb57b9a23f2a603f438a1aebf3171bffa7a205"}, ] [package.extras] @@ -1099,17 +1021,6 @@ test-downstream = ["aiobotocore (>=2.5.4,<3.0.0)", "dask-expr", "dask[dataframe, test-full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "cloudpickle", "dask", "distributed", "dropbox", "dropboxdrivefs", "fastparquet", "fusepy", "gcsfs", "jinja2", "kerchunk", "libarchive-c", "lz4", "notebook", "numpy", "ocifs", "pandas", "panel", "paramiko", "pyarrow", "pyarrow (>=1)", "pyftpdlib", "pygit2", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "python-snappy", "requests", "smbprotocol", "tqdm", "urllib3", "zarr", "zstandard"] tqdm = ["tqdm"] -[[package]] -name = "gast" -version = "0.6.0" -description = "Python AST that abstracts the underlying Python version" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -files = [ - {file = "gast-0.6.0-py3-none-any.whl", hash = "sha256:52b182313f7330389f72b069ba00f174cfe2a06411099547288839c6cbafbd54"}, - {file = "gast-0.6.0.tar.gz", hash = "sha256:88fc5300d32c7ac6ca7b515310862f71e6fdf2c029bbec7c66c0f5dd47b6b1fb"}, -] - [[package]] name = "gitdb" version = "4.0.11" @@ -1144,13 +1055,13 @@ test = ["coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", [[package]] name = "google-auth" -version = "2.35.0" +version = "2.36.0" description = "Google Authentication Library" optional = false python-versions = ">=3.7" files = [ - {file = "google_auth-2.35.0-py2.py3-none-any.whl", hash = "sha256:25df55f327ef021de8be50bad0dfd4a916ad0de96da86cd05661c9297723ad3f"}, - {file = "google_auth-2.35.0.tar.gz", hash = "sha256:f4c64ed4e01e8e8b646ef34c018f8bf3338df0c8e37d8b3bba40e7f574a3278a"}, + {file = "google_auth-2.36.0-py2.py3-none-any.whl", hash = "sha256:51a15d47028b66fd36e5c64a82d2d57480075bccc7da37cde257fc94177a61fb"}, + {file = "google_auth-2.36.0.tar.gz", hash = "sha256:545e9618f2df0bcbb7dcbc45a546485b1212624716975a1ea5ae8149ce769ab1"}, ] [package.dependencies] @@ -1165,30 +1076,15 @@ pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] reauth = ["pyu2f (>=0.1.5)"] requests = ["requests (>=2.20.0,<3.0.0.dev0)"] -[[package]] -name = "google-pasta" -version = "0.2.0" -description = "pasta is an AST-based Python refactoring library" -optional = false -python-versions = "*" -files = [ - {file = "google-pasta-0.2.0.tar.gz", hash = "sha256:c9f2c8dfc8f96d0d5808299920721be30c9eec37f2389f28904f454565c8a16e"}, - {file = "google_pasta-0.2.0-py2-none-any.whl", hash = "sha256:4612951da876b1a10fe3960d7226f0c7682cf901e16ac06e473b267a5afa8954"}, - {file = "google_pasta-0.2.0-py3-none-any.whl", hash = "sha256:b32482794a366b5366a32c92a9a9201b107821889935a02b3e51f6b432ea84ed"}, -] - -[package.dependencies] -six = "*" - [[package]] name = "graphene" -version = "3.4.1" +version = "3.4.3" description = "GraphQL Framework for Python" optional = false python-versions = "*" files = [ - {file = "graphene-3.4.1-py2.py3-none-any.whl", hash = "sha256:ca98f853201293871cdc7f55faf2523a9bc077181fe0f4947db5a243e5c67083"}, - {file = "graphene-3.4.1.tar.gz", hash = "sha256:828a8d7b1bce450566a72cc8733716c20f3acfc659960de73dd38f46dc302040"}, + {file = "graphene-3.4.3-py2.py3-none-any.whl", hash = "sha256:820db6289754c181007a150db1f7fff544b94142b556d12e3ebc777a7bf36c71"}, + {file = "graphene-3.4.3.tar.gz", hash = "sha256:2a3786948ce75fe7e078443d37f609cbe5bb36ad8d6b828740ad3b95ed1a0aaa"}, ] [package.dependencies] @@ -1212,9 +1108,6 @@ files = [ {file = "graphql_core-3.2.5.tar.gz", hash = "sha256:e671b90ed653c808715645e3998b7ab67d382d55467b7e2978549111bbabf8d5"}, ] -[package.dependencies] -typing-extensions = {version = ">=4,<5", markers = "python_version < \"3.10\""} - [[package]] name = "graphql-relay" version = "3.2.0" @@ -1315,73 +1208,6 @@ files = [ docs = ["Sphinx", "furo"] test = ["objgraph", "psutil"] -[[package]] -name = "grpcio" -version = "1.67.1" -description = "HTTP/2-based RPC framework" -optional = false -python-versions = ">=3.8" -files = [ - {file = "grpcio-1.67.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:8b0341d66a57f8a3119b77ab32207072be60c9bf79760fa609c5609f2deb1f3f"}, - {file = "grpcio-1.67.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:f5a27dddefe0e2357d3e617b9079b4bfdc91341a91565111a21ed6ebbc51b22d"}, - {file = "grpcio-1.67.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:43112046864317498a33bdc4797ae6a268c36345a910de9b9c17159d8346602f"}, - {file = "grpcio-1.67.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9b929f13677b10f63124c1a410994a401cdd85214ad83ab67cc077fc7e480f0"}, - {file = "grpcio-1.67.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7d1797a8a3845437d327145959a2c0c47c05947c9eef5ff1a4c80e499dcc6fa"}, - {file = "grpcio-1.67.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0489063974d1452436139501bf6b180f63d4977223ee87488fe36858c5725292"}, - {file = "grpcio-1.67.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9fd042de4a82e3e7aca44008ee2fb5da01b3e5adb316348c21980f7f58adc311"}, - {file = "grpcio-1.67.1-cp310-cp310-win32.whl", hash = "sha256:638354e698fd0c6c76b04540a850bf1db27b4d2515a19fcd5cf645c48d3eb1ed"}, - {file = "grpcio-1.67.1-cp310-cp310-win_amd64.whl", hash = "sha256:608d87d1bdabf9e2868b12338cd38a79969eaf920c89d698ead08f48de9c0f9e"}, - {file = "grpcio-1.67.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:7818c0454027ae3384235a65210bbf5464bd715450e30a3d40385453a85a70cb"}, - {file = "grpcio-1.67.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ea33986b70f83844cd00814cee4451055cd8cab36f00ac64a31f5bb09b31919e"}, - {file = "grpcio-1.67.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:c7a01337407dd89005527623a4a72c5c8e2894d22bead0895306b23c6695698f"}, - {file = "grpcio-1.67.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80b866f73224b0634f4312a4674c1be21b2b4afa73cb20953cbbb73a6b36c3cc"}, - {file = "grpcio-1.67.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9fff78ba10d4250bfc07a01bd6254a6d87dc67f9627adece85c0b2ed754fa96"}, - {file = "grpcio-1.67.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8a23cbcc5bb11ea7dc6163078be36c065db68d915c24f5faa4f872c573bb400f"}, - {file = "grpcio-1.67.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1a65b503d008f066e994f34f456e0647e5ceb34cfcec5ad180b1b44020ad4970"}, - {file = "grpcio-1.67.1-cp311-cp311-win32.whl", hash = "sha256:e29ca27bec8e163dca0c98084040edec3bc49afd10f18b412f483cc68c712744"}, - {file = "grpcio-1.67.1-cp311-cp311-win_amd64.whl", hash = "sha256:786a5b18544622bfb1e25cc08402bd44ea83edfb04b93798d85dca4d1a0b5be5"}, - {file = "grpcio-1.67.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:267d1745894200e4c604958da5f856da6293f063327cb049a51fe67348e4f953"}, - {file = "grpcio-1.67.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:85f69fdc1d28ce7cff8de3f9c67db2b0ca9ba4449644488c1e0303c146135ddb"}, - {file = "grpcio-1.67.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:f26b0b547eb8d00e195274cdfc63ce64c8fc2d3e2d00b12bf468ece41a0423a0"}, - {file = "grpcio-1.67.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4422581cdc628f77302270ff839a44f4c24fdc57887dc2a45b7e53d8fc2376af"}, - {file = "grpcio-1.67.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d7616d2ded471231c701489190379e0c311ee0a6c756f3c03e6a62b95a7146e"}, - {file = "grpcio-1.67.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8a00efecde9d6fcc3ab00c13f816313c040a28450e5e25739c24f432fc6d3c75"}, - {file = "grpcio-1.67.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:699e964923b70f3101393710793289e42845791ea07565654ada0969522d0a38"}, - {file = "grpcio-1.67.1-cp312-cp312-win32.whl", hash = "sha256:4e7b904484a634a0fff132958dabdb10d63e0927398273917da3ee103e8d1f78"}, - {file = "grpcio-1.67.1-cp312-cp312-win_amd64.whl", hash = "sha256:5721e66a594a6c4204458004852719b38f3d5522082be9061d6510b455c90afc"}, - {file = "grpcio-1.67.1-cp313-cp313-linux_armv7l.whl", hash = "sha256:aa0162e56fd10a5547fac8774c4899fc3e18c1aa4a4759d0ce2cd00d3696ea6b"}, - {file = "grpcio-1.67.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:beee96c8c0b1a75d556fe57b92b58b4347c77a65781ee2ac749d550f2a365dc1"}, - {file = "grpcio-1.67.1-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:a93deda571a1bf94ec1f6fcda2872dad3ae538700d94dc283c672a3b508ba3af"}, - {file = "grpcio-1.67.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e6f255980afef598a9e64a24efce87b625e3e3c80a45162d111a461a9f92955"}, - {file = "grpcio-1.67.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e838cad2176ebd5d4a8bb03955138d6589ce9e2ce5d51c3ada34396dbd2dba8"}, - {file = "grpcio-1.67.1-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:a6703916c43b1d468d0756c8077b12017a9fcb6a1ef13faf49e67d20d7ebda62"}, - {file = "grpcio-1.67.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:917e8d8994eed1d86b907ba2a61b9f0aef27a2155bca6cbb322430fc7135b7bb"}, - {file = "grpcio-1.67.1-cp313-cp313-win32.whl", hash = "sha256:e279330bef1744040db8fc432becc8a727b84f456ab62b744d3fdb83f327e121"}, - {file = "grpcio-1.67.1-cp313-cp313-win_amd64.whl", hash = "sha256:fa0c739ad8b1996bd24823950e3cb5152ae91fca1c09cc791190bf1627ffefba"}, - {file = "grpcio-1.67.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:178f5db771c4f9a9facb2ab37a434c46cb9be1a75e820f187ee3d1e7805c4f65"}, - {file = "grpcio-1.67.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0f3e49c738396e93b7ba9016e153eb09e0778e776df6090c1b8c91877cc1c426"}, - {file = "grpcio-1.67.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:24e8a26dbfc5274d7474c27759b54486b8de23c709d76695237515bc8b5baeab"}, - {file = "grpcio-1.67.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b6c16489326d79ead41689c4b84bc40d522c9a7617219f4ad94bc7f448c5085"}, - {file = "grpcio-1.67.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60e6a4dcf5af7bbc36fd9f81c9f372e8ae580870a9e4b6eafe948cd334b81cf3"}, - {file = "grpcio-1.67.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:95b5f2b857856ed78d72da93cd7d09b6db8ef30102e5e7fe0961fe4d9f7d48e8"}, - {file = "grpcio-1.67.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b49359977c6ec9f5d0573ea4e0071ad278ef905aa74e420acc73fd28ce39e9ce"}, - {file = "grpcio-1.67.1-cp38-cp38-win32.whl", hash = "sha256:f5b76ff64aaac53fede0cc93abf57894ab2a7362986ba22243d06218b93efe46"}, - {file = "grpcio-1.67.1-cp38-cp38-win_amd64.whl", hash = "sha256:804c6457c3cd3ec04fe6006c739579b8d35c86ae3298ffca8de57b493524b771"}, - {file = "grpcio-1.67.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:a25bdea92b13ff4d7790962190bf6bf5c4639876e01c0f3dda70fc2769616335"}, - {file = "grpcio-1.67.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cdc491ae35a13535fd9196acb5afe1af37c8237df2e54427be3eecda3653127e"}, - {file = "grpcio-1.67.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:85f862069b86a305497e74d0dc43c02de3d1d184fc2c180993aa8aa86fbd19b8"}, - {file = "grpcio-1.67.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ec74ef02010186185de82cc594058a3ccd8d86821842bbac9873fd4a2cf8be8d"}, - {file = "grpcio-1.67.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01f616a964e540638af5130469451cf580ba8c7329f45ca998ab66e0c7dcdb04"}, - {file = "grpcio-1.67.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:299b3d8c4f790c6bcca485f9963b4846dd92cf6f1b65d3697145d005c80f9fe8"}, - {file = "grpcio-1.67.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:60336bff760fbb47d7e86165408126f1dded184448e9a4c892189eb7c9d3f90f"}, - {file = "grpcio-1.67.1-cp39-cp39-win32.whl", hash = "sha256:5ed601c4c6008429e3d247ddb367fe8c7259c355757448d7c1ef7bd4a6739e8e"}, - {file = "grpcio-1.67.1-cp39-cp39-win_amd64.whl", hash = "sha256:5db70d32d6703b89912af16d6d45d78406374a8b8ef0d28140351dd0ec610e98"}, - {file = "grpcio-1.67.1.tar.gz", hash = "sha256:3dc2ed4cabea4dc14d5e708c2b426205956077cc5de419b4d4079315017e9732"}, -] - -[package.extras] -protobuf = ["grpcio-tools (>=1.67.1)"] - [[package]] name = "gunicorn" version = "23.0.0" @@ -1454,13 +1280,13 @@ numpy = ">=1.19.3" [[package]] name = "httpcore" -version = "1.0.6" +version = "1.0.7" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpcore-1.0.6-py3-none-any.whl", hash = "sha256:27b59625743b85577a8c0e10e55b50b5368a4f2cfe8cc7bcfa9cf00829c2682f"}, - {file = "httpcore-1.0.6.tar.gz", hash = "sha256:73f6dbd6eb8c21bbf7ef8efad555481853f5f6acdeaff1edb0694289269ee17f"}, + {file = "httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd"}, + {file = "httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c"}, ] [package.dependencies] @@ -1475,13 +1301,13 @@ trio = ["trio (>=0.22.0,<1.0)"] [[package]] name = "httpx" -version = "0.27.2" +version = "0.28.1" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, - {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, + {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, + {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, ] [package.dependencies] @@ -1489,7 +1315,6 @@ anyio = "*" certifi = "*" httpcore = "==1.*" idna = "*" -sniffio = "*" [package.extras] brotli = ["brotli", "brotlicffi"] @@ -1514,43 +1339,25 @@ all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2 [[package]] name = "importlib-metadata" -version = "8.4.0" +version = "8.5.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-8.4.0-py3-none-any.whl", hash = "sha256:66f342cc6ac9818fc6ff340576acd24d65ba0b3efabb2b4ac08b598965a4a2f1"}, - {file = "importlib_metadata-8.4.0.tar.gz", hash = "sha256:9a547d3bc3608b025f93d403fdd1aae741c24fbb8314df4b155675742ce303c5"}, + {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, + {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, ] [package.dependencies] -zipp = ">=0.5" - -[package.extras] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -perf = ["ipython"] -test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] - -[[package]] -name = "importlib-resources" -version = "6.4.5" -description = "Read resources from Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "importlib_resources-6.4.5-py3-none-any.whl", hash = "sha256:ac29d5f956f01d5e4bb63102a5a19957f1b9175e45649977264a1416783bb717"}, - {file = "importlib_resources-6.4.5.tar.gz", hash = "sha256:980862a1d16c9e147a59603677fa2aa5fd82b87f223b6cb870695bcfce830065"}, -] - -[package.dependencies] -zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} +zipp = ">=3.20" [package.extras] check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] -test = ["jaraco.test (>=5.4)", "pytest (>=6,!=8.1.*)", "zipp (>=3.17)"] +perf = ["ipython"] +test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] type = ["pytest-mypy"] [[package]] @@ -1599,13 +1406,13 @@ test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio [[package]] name = "ipython" -version = "8.18.1" +version = "8.30.0" description = "IPython: Productive Interactive Computing" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" files = [ - {file = "ipython-8.18.1-py3-none-any.whl", hash = "sha256:e8267419d72d81955ec1177f8a29aaa90ac80ad647499201119e2f05e99aa397"}, - {file = "ipython-8.18.1.tar.gz", hash = "sha256:ca6f079bb33457c66e233e4580ebfc4128855b4cf6370dddd73842a9563e8a27"}, + {file = "ipython-8.30.0-py3-none-any.whl", hash = "sha256:85ec56a7e20f6c38fce7727dcca699ae4ffc85985aa7b23635a8008f918ae321"}, + {file = "ipython-8.30.0.tar.gz", hash = "sha256:cb0a405a306d2995a5cbb9901894d240784a9f341394c6ba3f4fe8c6eb89ff6e"}, ] [package.dependencies] @@ -1614,25 +1421,26 @@ decorator = "*" exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} jedi = ">=0.16" matplotlib-inline = "*" -pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} -prompt-toolkit = ">=3.0.41,<3.1.0" +pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""} +prompt_toolkit = ">=3.0.41,<3.1.0" pygments = ">=2.4.0" -stack-data = "*" -traitlets = ">=5" -typing-extensions = {version = "*", markers = "python_version < \"3.10\""} +stack_data = "*" +traitlets = ">=5.13.0" +typing_extensions = {version = ">=4.6", markers = "python_version < \"3.12\""} [package.extras] -all = ["black", "curio", "docrepr", "exceptiongroup", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.22)", "pandas", "pickleshare", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio (<0.22)", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"] +all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"] black = ["black"] -doc = ["docrepr", "exceptiongroup", "ipykernel", "matplotlib", "pickleshare", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio (<0.22)", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"] +doc = ["docrepr", "exceptiongroup", "intersphinx_registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli", "typing_extensions"] kernel = ["ipykernel"] +matplotlib = ["matplotlib"] nbconvert = ["nbconvert"] nbformat = ["nbformat"] notebook = ["ipywidgets", "notebook"] parallel = ["ipyparallel"] qtconsole = ["qtconsole"] -test = ["pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath"] -test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.22)", "pandas", "pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath", "trio"] +test = ["packaging", "pickleshare", "pytest", "pytest-asyncio (<0.22)", "testpath"] +test-extra = ["curio", "ipython[test]", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.23)", "pandas", "trio"] [[package]] name = "ipywidgets" @@ -1682,22 +1490,22 @@ files = [ [[package]] name = "jedi" -version = "0.19.1" +version = "0.19.2" description = "An autocompletion tool for Python that can be used for text editors." optional = false python-versions = ">=3.6" files = [ - {file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"}, - {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"}, + {file = "jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9"}, + {file = "jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0"}, ] [package.dependencies] -parso = ">=0.8.3,<0.9.0" +parso = ">=0.8.4,<0.9.0" [package.extras] docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] -testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] +testing = ["Django", "attrs", "colorama", "docopt", "pytest (<9.0.0)"] [[package]] name = "jinja2" @@ -1729,15 +1537,18 @@ files = [ [[package]] name = "json5" -version = "0.9.25" +version = "0.10.0" description = "A Python implementation of the JSON5 data format." optional = false -python-versions = ">=3.8" +python-versions = ">=3.8.0" files = [ - {file = "json5-0.9.25-py3-none-any.whl", hash = "sha256:34ed7d834b1341a86987ed52f3f76cd8ee184394906b6e22a1e0deb9ab294e8f"}, - {file = "json5-0.9.25.tar.gz", hash = "sha256:548e41b9be043f9426776f05df8635a00fe06104ea51ed24b67f908856e151ae"}, + {file = "json5-0.10.0-py3-none-any.whl", hash = "sha256:19b23410220a7271e8377f81ba8aacba2fdd56947fbb137ee5977cbe1f5e8dfa"}, + {file = "json5-0.10.0.tar.gz", hash = "sha256:e66941c8f0a02026943c52c2eb34ebeb2a6f819a0be05920a6f5243cd30fd559"}, ] +[package.extras] +dev = ["build (==1.2.2.post1)", "coverage (==7.5.3)", "mypy (==1.13.0)", "pip (==24.3.1)", "pylint (==3.2.3)", "ruff (==0.7.3)", "twine (==5.1.1)", "uv (==0.5.1)"] + [[package]] name = "jsonpointer" version = "3.0.0" @@ -1823,7 +1634,6 @@ files = [ ] [package.dependencies] -importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.10\""} jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" python-dateutil = ">=2.8.2" pyzmq = ">=23.0" @@ -1915,7 +1725,6 @@ files = [ ] [package.dependencies] -importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.10\""} jupyter-server = ">=1.1.2" [[package]] @@ -1975,19 +1784,18 @@ test = ["jupyter-server (>=2.0.0)", "pytest (>=7.0)", "pytest-jupyter[server] (> [[package]] name = "jupyterlab" -version = "4.3.0" +version = "4.3.2" description = "JupyterLab computational environment" optional = false python-versions = ">=3.8" files = [ - {file = "jupyterlab-4.3.0-py3-none-any.whl", hash = "sha256:f67e1095ad61ae04349024f0b40345062ab108a0c6998d9810fec6a3c1a70cd5"}, - {file = "jupyterlab-4.3.0.tar.gz", hash = "sha256:7c6835cbf8df0af0ec8a39332e85ff11693fb9a468205343b4fc0bfbc74817e5"}, + {file = "jupyterlab-4.3.2-py3-none-any.whl", hash = "sha256:e87100cbab8b886ff7a4f325c856100ba6fdfe916162a85409daf0e707e19d1d"}, + {file = "jupyterlab-4.3.2.tar.gz", hash = "sha256:3c0a6882dbddcc0a7bfdd5e2236f351b2b263e48780236e6996c2aca13ac5b22"}, ] [package.dependencies] async-lru = ">=1.0.0" -httpx = ">=0.25.0" -importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.10\""} +httpx = ">=0.28.0,<0.29.0" ipykernel = ">=6.5.0" jinja2 = ">=3.0.3" jupyter-core = "*" @@ -1996,7 +1804,7 @@ jupyter-server = ">=2.4.0,<3" jupyterlab-server = ">=2.27.1,<3" notebook-shim = ">=0.2" packaging = "*" -setuptools = ">=40.1.0" +setuptools = ">=40.8.0" tomli = {version = ">=1.2.2", markers = "python_version < \"3.11\""} tornado = ">=6.2.0" traitlets = "*" @@ -2032,7 +1840,6 @@ files = [ [package.dependencies] babel = ">=2.10" -importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.10\""} jinja2 = ">=3.0.3" json5 = ">=0.9.0" jsonschema = ">=4.18.0" @@ -2058,13 +1865,13 @@ files = [ [[package]] name = "keras" -version = "3.6.0" -description = "Multi-backend Keras." +version = "3.7.0" +description = "Multi-backend Keras" optional = false python-versions = ">=3.9" files = [ - {file = "keras-3.6.0-py3-none-any.whl", hash = "sha256:49585e4577f6e86bd890d96dfbcb1890f5bab5967ef831c07fd63f9d86e4bfe9"}, - {file = "keras-3.6.0.tar.gz", hash = "sha256:405727525a3522ed8f9ec0b46e0667e4c65fcf714a067322c16a00d902ded41d"}, + {file = "keras-3.7.0-py3-none-any.whl", hash = "sha256:546a64f302e4779c129c06d9826fa586de752cdfd43d7dc4010c31b282587969"}, + {file = "keras-3.7.0.tar.gz", hash = "sha256:a4451a5591e75dfb414d0b84a3fd2fb9c0240cc87ebe7e397f547ce10b0e67b7"}, ] [package.dependencies] @@ -2200,53 +2007,29 @@ files = [ {file = "kiwisolver-1.4.7.tar.gz", hash = "sha256:9893ff81bd7107f7b685d3017cc6583daadb4fc26e4a888350df530e41980a60"}, ] -[[package]] -name = "libclang" -version = "18.1.1" -description = "Clang Python Bindings, mirrored from the official LLVM repo: https://github.com/llvm/llvm-project/tree/main/clang/bindings/python, to make the installation process easier." -optional = false -python-versions = "*" -files = [ - {file = "libclang-18.1.1-1-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:0b2e143f0fac830156feb56f9231ff8338c20aecfe72b4ffe96f19e5a1dbb69a"}, - {file = "libclang-18.1.1-py2.py3-none-macosx_10_9_x86_64.whl", hash = "sha256:6f14c3f194704e5d09769108f03185fce7acaf1d1ae4bbb2f30a72c2400cb7c5"}, - {file = "libclang-18.1.1-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:83ce5045d101b669ac38e6da8e58765f12da2d3aafb3b9b98d88b286a60964d8"}, - {file = "libclang-18.1.1-py2.py3-none-manylinux2010_x86_64.whl", hash = "sha256:c533091d8a3bbf7460a00cb6c1a71da93bffe148f172c7d03b1c31fbf8aa2a0b"}, - {file = "libclang-18.1.1-py2.py3-none-manylinux2014_aarch64.whl", hash = "sha256:54dda940a4a0491a9d1532bf071ea3ef26e6dbaf03b5000ed94dd7174e8f9592"}, - {file = "libclang-18.1.1-py2.py3-none-manylinux2014_armv7l.whl", hash = "sha256:cf4a99b05376513717ab5d82a0db832c56ccea4fd61a69dbb7bccf2dfb207dbe"}, - {file = "libclang-18.1.1-py2.py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:69f8eb8f65c279e765ffd28aaa7e9e364c776c17618af8bff22a8df58677ff4f"}, - {file = "libclang-18.1.1-py2.py3-none-win_amd64.whl", hash = "sha256:4dd2d3b82fab35e2bf9ca717d7b63ac990a3519c7e312f19fa8e86dcc712f7fb"}, - {file = "libclang-18.1.1-py2.py3-none-win_arm64.whl", hash = "sha256:3f0e1f49f04d3cd198985fea0511576b0aee16f9ff0e0f0cad7f9c57ec3c20e8"}, - {file = "libclang-18.1.1.tar.gz", hash = "sha256:a1214966d08d73d971287fc3ead8dfaf82eb07fb197680d8b3859dbbbbf78250"}, -] - [[package]] name = "llvmlite" -version = "0.43.0" +version = "0.34.0" description = "lightweight wrapper around basic LLVM functionality" optional = true -python-versions = ">=3.9" +python-versions = ">=3.6" files = [ - {file = "llvmlite-0.43.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a289af9a1687c6cf463478f0fa8e8aa3b6fb813317b0d70bf1ed0759eab6f761"}, - {file = "llvmlite-0.43.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d4fd101f571a31acb1559ae1af30f30b1dc4b3186669f92ad780e17c81e91bc"}, - {file = "llvmlite-0.43.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7d434ec7e2ce3cc8f452d1cd9a28591745de022f931d67be688a737320dfcead"}, - {file = "llvmlite-0.43.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6912a87782acdff6eb8bf01675ed01d60ca1f2551f8176a300a886f09e836a6a"}, - {file = "llvmlite-0.43.0-cp310-cp310-win_amd64.whl", hash = "sha256:14f0e4bf2fd2d9a75a3534111e8ebeb08eda2f33e9bdd6dfa13282afacdde0ed"}, - {file = "llvmlite-0.43.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3e8d0618cb9bfe40ac38a9633f2493d4d4e9fcc2f438d39a4e854f39cc0f5f98"}, - {file = "llvmlite-0.43.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0a9a1a39d4bf3517f2af9d23d479b4175ead205c592ceeb8b89af48a327ea57"}, - {file = "llvmlite-0.43.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1da416ab53e4f7f3bc8d4eeba36d801cc1894b9fbfbf2022b29b6bad34a7df2"}, - {file = "llvmlite-0.43.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:977525a1e5f4059316b183fb4fd34fa858c9eade31f165427a3977c95e3ee749"}, - {file = "llvmlite-0.43.0-cp311-cp311-win_amd64.whl", hash = "sha256:d5bd550001d26450bd90777736c69d68c487d17bf371438f975229b2b8241a91"}, - {file = "llvmlite-0.43.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f99b600aa7f65235a5a05d0b9a9f31150c390f31261f2a0ba678e26823ec38f7"}, - {file = "llvmlite-0.43.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:35d80d61d0cda2d767f72de99450766250560399edc309da16937b93d3b676e7"}, - {file = "llvmlite-0.43.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eccce86bba940bae0d8d48ed925f21dbb813519169246e2ab292b5092aba121f"}, - {file = "llvmlite-0.43.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df6509e1507ca0760787a199d19439cc887bfd82226f5af746d6977bd9f66844"}, - {file = "llvmlite-0.43.0-cp312-cp312-win_amd64.whl", hash = "sha256:7a2872ee80dcf6b5dbdc838763d26554c2a18aa833d31a2635bff16aafefb9c9"}, - {file = "llvmlite-0.43.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9cd2a7376f7b3367019b664c21f0c61766219faa3b03731113ead75107f3b66c"}, - {file = "llvmlite-0.43.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:18e9953c748b105668487b7c81a3e97b046d8abf95c4ddc0cd3c94f4e4651ae8"}, - {file = "llvmlite-0.43.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74937acd22dc11b33946b67dca7680e6d103d6e90eeaaaf932603bec6fe7b03a"}, - {file = "llvmlite-0.43.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc9efc739cc6ed760f795806f67889923f7274276f0eb45092a1473e40d9b867"}, - {file = "llvmlite-0.43.0-cp39-cp39-win_amd64.whl", hash = "sha256:47e147cdda9037f94b399bf03bfd8a6b6b1f2f90be94a454e3386f006455a9b4"}, - {file = "llvmlite-0.43.0.tar.gz", hash = "sha256:ae2b5b5c3ef67354824fb75517c8db5fbe93bc02cd9671f3c62271626bc041d5"}, + {file = "llvmlite-0.34.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:11342e5ac320c953590bdd9d0dec8c52f4b5252c4c6335ba25f1e7b9f91f9325"}, + {file = "llvmlite-0.34.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:5bdf0ce430adfaf938ced5844d12f80616eb8321b5b9edfc45ef84ada5c5242c"}, + {file = "llvmlite-0.34.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:e08d9d2dc5a31636bfc6b516d2d7daba95632afa3419eb8730dc76a7951e9558"}, + {file = "llvmlite-0.34.0-cp36-cp36m-win32.whl", hash = "sha256:9ff1dcdad03be0cf953aca5fc8cffdca25ccee2ec9e8ec7e95571722cdc02d55"}, + {file = "llvmlite-0.34.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5acdc3c3c7ea0ef7a1a6b442272e05d695bc8492e5b07666135ed1cfbf4ab9d2"}, + {file = "llvmlite-0.34.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:bb96989bc57a1ccb131e7a0e061d07b68139b6f81a98912345d53d9239e231e1"}, + {file = "llvmlite-0.34.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:6d3f81992f52a94077e7b9b16497029daf5b5eebb2cce56f3c8345bbc9c6308e"}, + {file = "llvmlite-0.34.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:d841248d1c630426c93e3eb3f8c45bca0dab77c09faeb7553b1a500220e362ce"}, + {file = "llvmlite-0.34.0-cp37-cp37m-win32.whl", hash = "sha256:408b15ffec30696406e821c89da010f1bb1eb0aa572be4561c98eb2536d610ab"}, + {file = "llvmlite-0.34.0-cp37-cp37m-win_amd64.whl", hash = "sha256:5d1f370bf150db7239204f09cf6a0603292ea28bac984e69b167e16fe160d803"}, + {file = "llvmlite-0.34.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:132322bc084abf336c80dd106f9357978c8c085911fb656898d3be0d9ff057ea"}, + {file = "llvmlite-0.34.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:8f344102745fceba6eb5bf03c228bb290e9bc79157e9506a4a72878d636f9b3c"}, + {file = "llvmlite-0.34.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:05253f3f44fab0148276335b2c1b2c4a78143dfa78e6bafd7f937d6248f297cc"}, + {file = "llvmlite-0.34.0-cp38-cp38-win32.whl", hash = "sha256:28264f9e2b3df4135cbcfca5a91c5b0b31dd3fc02fa623b4bb13327f0cd4fc80"}, + {file = "llvmlite-0.34.0-cp38-cp38-win_amd64.whl", hash = "sha256:964f8f7a2184963cb3617d057c2382575953e488b7bb061b632ee014cfef110a"}, + {file = "llvmlite-0.34.0.tar.gz", hash = "sha256:f03ee0d19bca8f2fe922bb424a909d05c28411983b0c2bc58b020032a0d11f63"}, ] [[package]] @@ -2262,13 +2045,13 @@ files = [ [[package]] name = "mako" -version = "1.3.6" +version = "1.3.8" description = "A super-fast templating language that borrows the best ideas from the existing templating languages." optional = false python-versions = ">=3.8" files = [ - {file = "Mako-1.3.6-py3-none-any.whl", hash = "sha256:a91198468092a2f1a0de86ca92690fb0cfc43ca90ee17e15d93662b4c04b241a"}, - {file = "mako-1.3.6.tar.gz", hash = "sha256:9ec3a1583713479fae654f83ed9fa8c9a4c16b7bb0daba0e6bbebff50c0d983d"}, + {file = "Mako-1.3.8-py3-none-any.whl", hash = "sha256:42f48953c7eb91332040ff567eb7eea69b22e7a4affbc5ba8e845e8f730f6627"}, + {file = "mako-1.3.8.tar.gz", hash = "sha256:577b97e414580d3e088d47c2dbbe9594aa7a5146ed2875d4dfa9075af2dd3cc8"}, ] [package.dependencies] @@ -2290,9 +2073,6 @@ files = [ {file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"}, ] -[package.dependencies] -importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} - [package.extras] docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] testing = ["coverage", "pyyaml"] @@ -2393,58 +2173,58 @@ files = [ [[package]] name = "matplotlib" -version = "3.9.2" +version = "3.9.3" description = "Python plotting package" optional = false python-versions = ">=3.9" files = [ - {file = "matplotlib-3.9.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9d78bbc0cbc891ad55b4f39a48c22182e9bdaea7fc0e5dbd364f49f729ca1bbb"}, - {file = "matplotlib-3.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c375cc72229614632c87355366bdf2570c2dac01ac66b8ad048d2dabadf2d0d4"}, - {file = "matplotlib-3.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d94ff717eb2bd0b58fe66380bd8b14ac35f48a98e7c6765117fe67fb7684e64"}, - {file = "matplotlib-3.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab68d50c06938ef28681073327795c5db99bb4666214d2d5f880ed11aeaded66"}, - {file = "matplotlib-3.9.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:65aacf95b62272d568044531e41de26285d54aec8cb859031f511f84bd8b495a"}, - {file = "matplotlib-3.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:3fd595f34aa8a55b7fc8bf9ebea8aa665a84c82d275190a61118d33fbc82ccae"}, - {file = "matplotlib-3.9.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d8dd059447824eec055e829258ab092b56bb0579fc3164fa09c64f3acd478772"}, - {file = "matplotlib-3.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c797dac8bb9c7a3fd3382b16fe8f215b4cf0f22adccea36f1545a6d7be310b41"}, - {file = "matplotlib-3.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d719465db13267bcef19ea8954a971db03b9f48b4647e3860e4bc8e6ed86610f"}, - {file = "matplotlib-3.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8912ef7c2362f7193b5819d17dae8629b34a95c58603d781329712ada83f9447"}, - {file = "matplotlib-3.9.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7741f26a58a240f43bee74965c4882b6c93df3e7eb3de160126d8c8f53a6ae6e"}, - {file = "matplotlib-3.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:ae82a14dab96fbfad7965403c643cafe6515e386de723e498cf3eeb1e0b70cc7"}, - {file = "matplotlib-3.9.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ac43031375a65c3196bee99f6001e7fa5bdfb00ddf43379d3c0609bdca042df9"}, - {file = "matplotlib-3.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:be0fc24a5e4531ae4d8e858a1a548c1fe33b176bb13eff7f9d0d38ce5112a27d"}, - {file = "matplotlib-3.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf81de2926c2db243c9b2cbc3917619a0fc85796c6ba4e58f541df814bbf83c7"}, - {file = "matplotlib-3.9.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6ee45bc4245533111ced13f1f2cace1e7f89d1c793390392a80c139d6cf0e6c"}, - {file = "matplotlib-3.9.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:306c8dfc73239f0e72ac50e5a9cf19cc4e8e331dd0c54f5e69ca8758550f1e1e"}, - {file = "matplotlib-3.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:5413401594cfaff0052f9d8b1aafc6d305b4bd7c4331dccd18f561ff7e1d3bd3"}, - {file = "matplotlib-3.9.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:18128cc08f0d3cfff10b76baa2f296fc28c4607368a8402de61bb3f2eb33c7d9"}, - {file = "matplotlib-3.9.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4876d7d40219e8ae8bb70f9263bcbe5714415acfdf781086601211335e24f8aa"}, - {file = "matplotlib-3.9.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d9f07a80deab4bb0b82858a9e9ad53d1382fd122be8cde11080f4e7dfedb38b"}, - {file = "matplotlib-3.9.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7c0410f181a531ec4e93bbc27692f2c71a15c2da16766f5ba9761e7ae518413"}, - {file = "matplotlib-3.9.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:909645cce2dc28b735674ce0931a4ac94e12f5b13f6bb0b5a5e65e7cea2c192b"}, - {file = "matplotlib-3.9.2-cp313-cp313-win_amd64.whl", hash = "sha256:f32c7410c7f246838a77d6d1eff0c0f87f3cb0e7c4247aebea71a6d5a68cab49"}, - {file = "matplotlib-3.9.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:37e51dd1c2db16ede9cfd7b5cabdfc818b2c6397c83f8b10e0e797501c963a03"}, - {file = "matplotlib-3.9.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b82c5045cebcecd8496a4d694d43f9cc84aeeb49fe2133e036b207abe73f4d30"}, - {file = "matplotlib-3.9.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f053c40f94bc51bc03832a41b4f153d83f2062d88c72b5e79997072594e97e51"}, - {file = "matplotlib-3.9.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dbe196377a8248972f5cede786d4c5508ed5f5ca4a1e09b44bda889958b33f8c"}, - {file = "matplotlib-3.9.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5816b1e1fe8c192cbc013f8f3e3368ac56fbecf02fb41b8f8559303f24c5015e"}, - {file = "matplotlib-3.9.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:cef2a73d06601437be399908cf13aee74e86932a5ccc6ccdf173408ebc5f6bb2"}, - {file = "matplotlib-3.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e0830e188029c14e891fadd99702fd90d317df294c3298aad682739c5533721a"}, - {file = "matplotlib-3.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03ba9c1299c920964e8d3857ba27173b4dbb51ca4bab47ffc2c2ba0eb5e2cbc5"}, - {file = "matplotlib-3.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1cd93b91ab47a3616b4d3c42b52f8363b88ca021e340804c6ab2536344fad9ca"}, - {file = "matplotlib-3.9.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6d1ce5ed2aefcdce11904fc5bbea7d9c21fff3d5f543841edf3dea84451a09ea"}, - {file = "matplotlib-3.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:b2696efdc08648536efd4e1601b5fd491fd47f4db97a5fbfd175549a7365c1b2"}, - {file = "matplotlib-3.9.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:d52a3b618cb1cbb769ce2ee1dcdb333c3ab6e823944e9a2d36e37253815f9556"}, - {file = "matplotlib-3.9.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:039082812cacd6c6bec8e17a9c1e6baca230d4116d522e81e1f63a74d01d2e21"}, - {file = "matplotlib-3.9.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6758baae2ed64f2331d4fd19be38b7b4eae3ecec210049a26b6a4f3ae1c85dcc"}, - {file = "matplotlib-3.9.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:050598c2b29e0b9832cde72bcf97627bf00262adbc4a54e2b856426bb2ef0697"}, - {file = "matplotlib-3.9.2.tar.gz", hash = "sha256:96ab43906269ca64a6366934106fa01534454a69e471b7bf3d79083981aaab92"}, + {file = "matplotlib-3.9.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:41b016e3be4e740b66c79a031a0a6e145728dbc248142e751e8dab4f3188ca1d"}, + {file = "matplotlib-3.9.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e0143975fc2a6d7136c97e19c637321288371e8f09cff2564ecd73e865ea0b9"}, + {file = "matplotlib-3.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f459c8ee2c086455744723628264e43c884be0c7d7b45d84b8cd981310b4815"}, + {file = "matplotlib-3.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:687df7ceff57b8f070d02b4db66f75566370e7ae182a0782b6d3d21b0d6917dc"}, + {file = "matplotlib-3.9.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:edd14cf733fdc4f6e6fe3f705af97676a7e52859bf0044aa2c84e55be739241c"}, + {file = "matplotlib-3.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:1c40c244221a1adbb1256692b1133c6fb89418df27bf759a31a333e7912a4010"}, + {file = "matplotlib-3.9.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:cf2a60daf6cecff6828bc608df00dbc794380e7234d2411c0ec612811f01969d"}, + {file = "matplotlib-3.9.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:213d6dc25ce686516208d8a3e91120c6a4fdae4a3e06b8505ced5b716b50cc04"}, + {file = "matplotlib-3.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c52f48eb75fcc119a4fdb68ba83eb5f71656999420375df7c94cc68e0e14686e"}, + {file = "matplotlib-3.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3c93796b44fa111049b88a24105e947f03c01966b5c0cc782e2ee3887b790a3"}, + {file = "matplotlib-3.9.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cd1077b9a09b16d8c3c7075a8add5ffbfe6a69156a57e290c800ed4d435bef1d"}, + {file = "matplotlib-3.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:c96eeeb8c68b662c7747f91a385688d4b449687d29b691eff7068a4602fe6dc4"}, + {file = "matplotlib-3.9.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0a361bd5583bf0bcc08841df3c10269617ee2a36b99ac39d455a767da908bbbc"}, + {file = "matplotlib-3.9.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e14485bb1b83eeb3d55b6878f9560240981e7bbc7a8d4e1e8c38b9bd6ec8d2de"}, + {file = "matplotlib-3.9.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a8d279f78844aad213c4935c18f8292a9432d51af2d88bca99072c903948045"}, + {file = "matplotlib-3.9.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6c12514329ac0d03128cf1dcceb335f4fbf7c11da98bca68dca8dcb983153a9"}, + {file = "matplotlib-3.9.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6e9de2b390d253a508dd497e9b5579f3a851f208763ed67fdca5dc0c3ea6849c"}, + {file = "matplotlib-3.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:d796272408f8567ff7eaa00eb2856b3a00524490e47ad505b0b4ca6bb8a7411f"}, + {file = "matplotlib-3.9.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:203d18df84f5288973b2d56de63d4678cc748250026ca9e1ad8f8a0fd8a75d83"}, + {file = "matplotlib-3.9.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b651b0d3642991259109dc0351fc33ad44c624801367bb8307be9bfc35e427ad"}, + {file = "matplotlib-3.9.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:66d7b171fecf96940ce069923a08ba3df33ef542de82c2ff4fe8caa8346fa95a"}, + {file = "matplotlib-3.9.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6be0ba61f6ff2e6b68e4270fb63b6813c9e7dec3d15fc3a93f47480444fd72f0"}, + {file = "matplotlib-3.9.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d6b2e8856dec3a6db1ae51aec85c82223e834b228c1d3228aede87eee2b34f9"}, + {file = "matplotlib-3.9.3-cp313-cp313-win_amd64.whl", hash = "sha256:90a85a004fefed9e583597478420bf904bb1a065b0b0ee5b9d8d31b04b0f3f70"}, + {file = "matplotlib-3.9.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3119b2f16de7f7b9212ba76d8fe6a0e9f90b27a1e04683cd89833a991682f639"}, + {file = "matplotlib-3.9.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:87ad73763d93add1b6c1f9fcd33af662fd62ed70e620c52fcb79f3ac427cf3a6"}, + {file = "matplotlib-3.9.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:026bdf3137ab6022c866efa4813b6bbeddc2ed4c9e7e02f0e323a7bca380dfa0"}, + {file = "matplotlib-3.9.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:760a5e89ebbb172989e8273024a1024b0f084510b9105261b3b00c15e9c9f006"}, + {file = "matplotlib-3.9.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a42b9dc42de2cfe357efa27d9c50c7833fc5ab9b2eb7252ccd5d5f836a84e1e4"}, + {file = "matplotlib-3.9.3-cp313-cp313t-win_amd64.whl", hash = "sha256:e0fcb7da73fbf67b5f4bdaa57d85bb585a4e913d4a10f3e15b32baea56a67f0a"}, + {file = "matplotlib-3.9.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:031b7f5b8e595cc07def77ec5b58464e9bb67dc5760be5d6f26d9da24892481d"}, + {file = "matplotlib-3.9.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9fa6e193c14d6944e0685cdb527cb6b38b0e4a518043e7212f214113af7391da"}, + {file = "matplotlib-3.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e6eefae6effa0c35bbbc18c25ee6e0b1da44d2359c3cd526eb0c9e703cf055d"}, + {file = "matplotlib-3.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10d3e5c7a99bd28afb957e1ae661323b0800d75b419f24d041ed1cc5d844a764"}, + {file = "matplotlib-3.9.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:816a966d5d376bf24c92af8f379e78e67278833e4c7cbc9fa41872eec629a060"}, + {file = "matplotlib-3.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fb0b37c896172899a4a93d9442ffdc6f870165f59e05ce2e07c6fded1c15749"}, + {file = "matplotlib-3.9.3-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5f2a4ea08e6876206d511365b0bc234edc813d90b930be72c3011bbd7898796f"}, + {file = "matplotlib-3.9.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:9b081dac96ab19c54fd8558fac17c9d2c9cb5cc4656e7ed3261ddc927ba3e2c5"}, + {file = "matplotlib-3.9.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a0a63cb8404d1d1f94968ef35738900038137dab8af836b6c21bb6f03d75465"}, + {file = "matplotlib-3.9.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:896774766fd6be4571a43bc2fcbcb1dcca0807e53cab4a5bf88c4aa861a08e12"}, + {file = "matplotlib-3.9.3.tar.gz", hash = "sha256:cd5dbbc8e25cad5f706845c4d100e2c8b34691b412b93717ce38d8ae803bcfa5"}, ] [package.dependencies] contourpy = ">=1.0.1" cycler = ">=0.10" fonttools = ">=4.22.0" -importlib-resources = {version = ">=3.2.0", markers = "python_version < \"3.10\""} kiwisolver = ">=1.3.1" numpy = ">=1.23" packaging = ">=20.0" @@ -2453,7 +2233,7 @@ pyparsing = ">=2.3.1" python-dateutil = ">=2.7" [package.extras] -dev = ["meson-python (>=0.13.1)", "numpy (>=1.25)", "pybind11 (>=2.6)", "setuptools (>=64)", "setuptools_scm (>=7)"] +dev = ["meson-python (>=0.13.1)", "numpy (>=1.25)", "pybind11 (>=2.6,!=2.13.3)", "setuptools (>=64)", "setuptools_scm (>=7)"] [[package]] name = "matplotlib-inline" @@ -2493,48 +2273,49 @@ files = [ [[package]] name = "ml-dtypes" -version = "0.3.2" +version = "0.5.0" description = "" optional = false python-versions = ">=3.9" files = [ - {file = "ml_dtypes-0.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7afde548890a92b41c0fed3a6c525f1200a5727205f73dc21181a2726571bb53"}, - {file = "ml_dtypes-0.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1a746fe5fb9cd974a91070174258f0be129c592b93f9ce7df6cc336416c3fbd"}, - {file = "ml_dtypes-0.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:961134ea44c7b8ca63eda902a44b58cd8bd670e21d62e255c81fba0a8e70d9b7"}, - {file = "ml_dtypes-0.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:6b35c4e8ca957c877ac35c79ffa77724ecc3702a1e4b18b08306c03feae597bb"}, - {file = "ml_dtypes-0.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:763697ab8a88d47443997a7cdf3aac7340049aed45f7521f6b0ec8a0594821fe"}, - {file = "ml_dtypes-0.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b89b194e9501a92d289c1ffd411380baf5daafb9818109a4f49b0a1b6dce4462"}, - {file = "ml_dtypes-0.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c34f2ba9660b21fe1034b608308a01be82bbef2a92fb8199f24dc6bad0d5226"}, - {file = "ml_dtypes-0.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:6604877d567a29bfe7cc02969ae0f2425260e5335505cf5e7fefc3e5465f5655"}, - {file = "ml_dtypes-0.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:93b78f53431c93953f7850bb1b925a17f0ab5d97527e38a7e865b5b4bc5cfc18"}, - {file = "ml_dtypes-0.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a17ef2322e60858d93584e9c52a5be7dd6236b056b7fa1ec57f1bb6ba043e33"}, - {file = "ml_dtypes-0.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8505946df1665db01332d885c2020b4cb9e84a8b1241eb4ba69d59591f65855"}, - {file = "ml_dtypes-0.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:f47619d978ab1ae7dfdc4052ea97c636c6263e1f19bd1be0e42c346b98d15ff4"}, - {file = "ml_dtypes-0.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c7b3fb3d4f6b39bcd4f6c4b98f406291f0d681a895490ee29a0f95bab850d53c"}, - {file = "ml_dtypes-0.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a4c3fcbf86fa52d0204f07cfd23947ef05b4ad743a1a988e163caa34a201e5e"}, - {file = "ml_dtypes-0.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91f8783fd1f2c23fd3b9ee5ad66b785dafa58ba3cdb050c4458021fa4d1eb226"}, - {file = "ml_dtypes-0.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:7ba8e1fafc7fff3e643f453bffa7d082df1678a73286ce8187d3e825e776eb94"}, - {file = "ml_dtypes-0.3.2.tar.gz", hash = "sha256:533059bc5f1764fac071ef54598db358c167c51a718f68f5bb55e3dee79d2967"}, + {file = "ml_dtypes-0.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c32138975797e681eb175996d64356bcfa124bdbb6a70460b9768c2b35a6fa4"}, + {file = "ml_dtypes-0.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab046f2ff789b1f11b2491909682c5d089934835f9a760fafc180e47dcb676b8"}, + {file = "ml_dtypes-0.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7a9152f5876fef565516aa5dd1dccd6fc298a5891b2467973905103eb5c7856"}, + {file = "ml_dtypes-0.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:968fede07d1f9b926a63df97d25ac656cac1a57ebd33701734eaf704bc55d8d8"}, + {file = "ml_dtypes-0.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60275f2b51b56834e840c4809fca840565f9bf8e9a73f6d8c94f5b5935701215"}, + {file = "ml_dtypes-0.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76942f6aeb5c40766d5ea62386daa4148e6a54322aaf5b53eae9e7553240222f"}, + {file = "ml_dtypes-0.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e7534392682c3098bc7341648c650864207169c654aed83143d7a19c67ae06f"}, + {file = "ml_dtypes-0.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:dc74fd9995513d33eac63d64e436240f5494ec74d522a9f0920194942fc3d2d7"}, + {file = "ml_dtypes-0.5.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d4b1a70a3e5219790d6b55b9507606fc4e02911d1497d16c18dd721eb7efe7d0"}, + {file = "ml_dtypes-0.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a988bac6572630e1e9c2edd9b1277b4eefd1c86209e52b0d061b775ac33902ff"}, + {file = "ml_dtypes-0.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a38df8df61194aeaae1ab7579075779b4ad32cd1cffd012c28be227fa7f2a70a"}, + {file = "ml_dtypes-0.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:afa08343069874a30812871d639f9c02b4158ace065601406a493a8511180c02"}, + {file = "ml_dtypes-0.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d3b3db9990c3840986a0e70524e122cfa32b91139c3653df76121ba7776e015f"}, + {file = "ml_dtypes-0.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e04fde367b2fe901b1d47234426fe8819909bd1dd862a5adb630f27789c20599"}, + {file = "ml_dtypes-0.5.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54415257f00eb44fbcc807454efac3356f75644f1cbfc2d4e5522a72ae1dacab"}, + {file = "ml_dtypes-0.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:cb5cc7b25acabd384f75bbd78892d0c724943f3e2e1986254665a1aa10982e07"}, + {file = "ml_dtypes-0.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5f2b59233a0dbb6a560b3137ed6125433289ccba2f8d9c3695a52423a369ed15"}, + {file = "ml_dtypes-0.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:099e09edd54e676903b4538f3815b5ab96f5b119690514602d96bfdb67172cbe"}, + {file = "ml_dtypes-0.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a03fc861b86cc586728e3d093ba37f0cc05e65330c3ebd7688e7bae8290f8859"}, + {file = "ml_dtypes-0.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:7ee9c320bb0f9ffdf9f6fa6a696ef2e005d1f66438d6f1c1457338e00a02e8cf"}, + {file = "ml_dtypes-0.5.0.tar.gz", hash = "sha256:3e7d3a380fe73a63c884f06136f8baa7a5249cc8e9fdec677997dd78549f8128"}, ] [package.dependencies] -numpy = [ - {version = ">=1.21.2", markers = "python_version >= \"3.10\""}, - {version = ">1.20", markers = "python_version < \"3.10\""}, -] +numpy = {version = ">=1.21.2", markers = "python_version >= \"3.10\""} [package.extras] dev = ["absl-py", "pyink", "pylint (>=2.6.0)", "pytest", "pytest-xdist"] [[package]] name = "mlflow" -version = "2.17.2" +version = "2.18.0" description = "MLflow is an open source platform for the complete machine learning lifecycle" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "mlflow-2.17.2-py3-none-any.whl", hash = "sha256:5cd1b105d31db49d2c23d80d6e07ddde437793ffc1985b117fb013bf8ccb1ae8"}, - {file = "mlflow-2.17.2.tar.gz", hash = "sha256:3ecee5fa0eea9134154a99aec3006fe8fdbfc61d990a74ce99fa69cd3b54e933"}, + {file = "mlflow-2.18.0-py3-none-any.whl", hash = "sha256:844a5c26ce8f83bbb5e038a7ce5a47be62edee89ad13ecf98ae2dbf0db9fa58f"}, + {file = "mlflow-2.18.0.tar.gz", hash = "sha256:90f0d04b02e35c0f2fccc88e892e37b84871cb4f766acd3ef904c1c30be63ee3"}, ] [package.dependencies] @@ -2549,10 +2330,10 @@ Jinja2 = [ ] markdown = ">=3.3,<4" matplotlib = "<4" -mlflow-skinny = "2.17.2" +mlflow-skinny = "2.18.0" numpy = "<3" pandas = "<3" -pyarrow = ">=4.0.0,<18" +pyarrow = ">=4.0.0,<19" scikit-learn = "<2" scipy = "<2" sqlalchemy = ">=1.4.0,<3" @@ -2565,20 +2346,20 @@ extras = ["azureml-core (>=1.2.0)", "boto3", "botocore", "google-cloud-storage ( gateway = ["aiohttp (<4)", "boto3 (>=1.28.56,<2)", "fastapi (<1)", "pydantic (>=1.0,<3)", "slowapi (>=0.1.9,<1)", "tiktoken (<1)", "uvicorn[standard] (<1)", "watchfiles (<1)"] genai = ["aiohttp (<4)", "boto3 (>=1.28.56,<2)", "fastapi (<1)", "pydantic (>=1.0,<3)", "slowapi (>=0.1.9,<1)", "tiktoken (<1)", "uvicorn[standard] (<1)", "watchfiles (<1)"] jfrog = ["mlflow-jfrog-plugin"] -langchain = ["langchain (>=0.1.0,<=0.3.1)"] +langchain = ["langchain (>=0.1.0,<=0.3.7)"] mlserver = ["mlserver (>=1.2.0,!=1.3.1)", "mlserver-mlflow (>=1.2.0,!=1.3.1)"] sqlserver = ["mlflow-dbstore"] xethub = ["mlflow-xethub"] [[package]] name = "mlflow-skinny" -version = "2.17.2" +version = "2.18.0" description = "MLflow is an open source platform for the complete machine learning lifecycle" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "mlflow_skinny-2.17.2-py3-none-any.whl", hash = "sha256:d45d6ba7a05e4755110eca71afcad12d6ede51114b2be0fbb60a3f28d9159ab5"}, - {file = "mlflow_skinny-2.17.2.tar.gz", hash = "sha256:944144f2c94afff293c3f000dc605c7f08f5f8287bc6108d52a6f6e13dc4b64b"}, + {file = "mlflow_skinny-2.18.0-py3-none-any.whl", hash = "sha256:b924730b38cf9a7400737aa3e011c97edf978eed354bb0eb89ccb1f9e42764dc"}, + {file = "mlflow_skinny-2.18.0.tar.gz", hash = "sha256:87e83f56c362a520196b2f0292b24efdca7f8b2068a6a6941f2ec9feb9bfd914"}, ] [package.dependencies] @@ -2603,11 +2384,28 @@ extras = ["azureml-core (>=1.2.0)", "boto3", "botocore", "google-cloud-storage ( gateway = ["aiohttp (<4)", "boto3 (>=1.28.56,<2)", "fastapi (<1)", "pydantic (>=1.0,<3)", "slowapi (>=0.1.9,<1)", "tiktoken (<1)", "uvicorn[standard] (<1)", "watchfiles (<1)"] genai = ["aiohttp (<4)", "boto3 (>=1.28.56,<2)", "fastapi (<1)", "pydantic (>=1.0,<3)", "slowapi (>=0.1.9,<1)", "tiktoken (<1)", "uvicorn[standard] (<1)", "watchfiles (<1)"] jfrog = ["mlflow-jfrog-plugin"] -langchain = ["langchain (>=0.1.0,<=0.3.1)"] +langchain = ["langchain (>=0.1.0,<=0.3.7)"] mlserver = ["mlserver (>=1.2.0,!=1.3.1)", "mlserver-mlflow (>=1.2.0,!=1.3.1)"] sqlserver = ["mlflow-dbstore"] xethub = ["mlflow-xethub"] +[[package]] +name = "mpmath" +version = "1.3.0" +description = "Python library for arbitrary-precision floating-point arithmetic" +optional = false +python-versions = "*" +files = [ + {file = "mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c"}, + {file = "mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f"}, +] + +[package.extras] +develop = ["codecov", "pycodestyle", "pytest (>=4.6)", "pytest-cov", "wheel"] +docs = ["sphinx"] +gmpy = ["gmpy2 (>=2.1.0a4)"] +tests = ["pytest (>=4.6)"] + [[package]] name = "mypy-extensions" version = "1.0.0" @@ -2632,13 +2430,13 @@ files = [ [[package]] name = "nbclient" -version = "0.10.0" +version = "0.10.1" description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." optional = false python-versions = ">=3.8.0" files = [ - {file = "nbclient-0.10.0-py3-none-any.whl", hash = "sha256:f13e3529332a1f1f81d82a53210322476a168bb7090a0289c795fe9cc11c9d3f"}, - {file = "nbclient-0.10.0.tar.gz", hash = "sha256:4b3f1b7dba531e498449c4db4f53da339c91d449dc11e9af3a43b4eb5c5abb09"}, + {file = "nbclient-0.10.1-py3-none-any.whl", hash = "sha256:949019b9240d66897e442888cfb618f69ef23dc71c01cb5fced8499c2cfc084d"}, + {file = "nbclient-0.10.1.tar.gz", hash = "sha256:3e93e348ab27e712acd46fccd809139e356eb9a31aab641d1a7991a6eb4e6f68"}, ] [package.dependencies] @@ -2649,7 +2447,7 @@ traitlets = ">=5.4" [package.extras] dev = ["pre-commit"] -docs = ["autodoc-traits", "mock", "moto", "myst-parser", "nbclient[test]", "sphinx (>=1.7)", "sphinx-book-theme", "sphinxcontrib-spelling"] +docs = ["autodoc-traits", "flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "mock", "moto", "myst-parser", "nbconvert (>=7.0.0)", "pytest (>=7.0,<8)", "pytest-asyncio", "pytest-cov (>=4.0)", "sphinx (>=1.7)", "sphinx-book-theme", "sphinxcontrib-spelling", "testpath", "xmltodict"] test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>=7.0.0)", "pytest (>=7.0,<8)", "pytest-asyncio", "pytest-cov (>=4.0)", "testpath", "xmltodict"] [[package]] @@ -2667,7 +2465,6 @@ files = [ beautifulsoup4 = "*" bleach = "!=5.0.0" defusedxml = "*" -importlib-metadata = {version = ">=3.6", markers = "python_version < \"3.10\""} jinja2 = ">=3.0" jupyter-core = ">=4.7" jupyterlab-pygments = "*" @@ -2722,28 +2519,47 @@ files = [ {file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"}, ] +[[package]] +name = "networkx" +version = "3.4.2" +description = "Python package for creating and manipulating graphs and networks" +optional = false +python-versions = ">=3.10" +files = [ + {file = "networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f"}, + {file = "networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1"}, +] + +[package.extras] +default = ["matplotlib (>=3.7)", "numpy (>=1.24)", "pandas (>=2.0)", "scipy (>=1.10,!=1.11.0,!=1.11.1)"] +developer = ["changelist (==0.5)", "mypy (>=1.1)", "pre-commit (>=3.2)", "rtoml"] +doc = ["intersphinx-registry", "myst-nb (>=1.1)", "numpydoc (>=1.8.0)", "pillow (>=9.4)", "pydata-sphinx-theme (>=0.15)", "sphinx (>=7.3)", "sphinx-gallery (>=0.16)", "texext (>=0.6.7)"] +example = ["cairocffi (>=1.7)", "contextily (>=1.6)", "igraph (>=0.11)", "momepy (>=0.7.2)", "osmnx (>=1.9)", "scikit-learn (>=1.5)", "seaborn (>=0.13)"] +extra = ["lxml (>=4.6)", "pydot (>=3.0.1)", "pygraphviz (>=1.14)", "sympy (>=1.10)"] +test = ["pytest (>=7.2)", "pytest-cov (>=4.0)"] + [[package]] name = "notebook" -version = "7.0.7" +version = "7.3.1" description = "Jupyter Notebook - A web-based notebook environment for interactive computing" optional = false python-versions = ">=3.8" files = [ - {file = "notebook-7.0.7-py3-none-any.whl", hash = "sha256:289b606d7e173f75a18beb1406ef411b43f97f7a9c55ba03efa3622905a62346"}, - {file = "notebook-7.0.7.tar.gz", hash = "sha256:3bcff00c17b3ac142ef5f436d50637d936b274cfa0b41f6ac0175363de9b4e09"}, + {file = "notebook-7.3.1-py3-none-any.whl", hash = "sha256:212e1486b2230fe22279043f33c7db5cf9a01d29feb063a85cb139747b7c9483"}, + {file = "notebook-7.3.1.tar.gz", hash = "sha256:84381c2a82d867517fd25b86e986dae1fe113a70b98f03edff9b94e499fec8fa"}, ] [package.dependencies] jupyter-server = ">=2.4.0,<3" -jupyterlab = ">=4.0.2,<5" -jupyterlab-server = ">=2.22.1,<3" +jupyterlab = ">=4.3.2,<4.4" +jupyterlab-server = ">=2.27.1,<3" notebook-shim = ">=0.2,<0.3" tornado = ">=6.2.0" [package.extras] dev = ["hatch", "pre-commit"] docs = ["myst-parser", "nbsphinx", "pydata-sphinx-theme", "sphinx (>=1.3.6)", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] -test = ["importlib-resources (>=5.0)", "ipykernel", "jupyter-server[test] (>=2.4.0,<3)", "jupyterlab-server[test] (>=2.22.1,<3)", "nbval", "pytest (>=7.0)", "pytest-console-scripts", "pytest-timeout", "pytest-tornasync", "requests"] +test = ["importlib-resources (>=5.0)", "ipykernel", "jupyter-server[test] (>=2.4.0,<3)", "jupyterlab-server[test] (>=2.27.1,<3)", "nbval", "pytest (>=7.0)", "pytest-console-scripts", "pytest-timeout", "pytest-tornasync", "requests"] [[package]] name = "notebook-shim" @@ -2764,275 +2580,431 @@ test = ["pytest", "pytest-console-scripts", "pytest-jupyter", "pytest-tornasync" [[package]] name = "numba" -version = "0.60.0" +version = "0.51.2" description = "compiling Python code using LLVM" optional = true -python-versions = ">=3.9" +python-versions = ">=3.6" files = [ - {file = "numba-0.60.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d761de835cd38fb400d2c26bb103a2726f548dc30368853121d66201672e651"}, - {file = "numba-0.60.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:159e618ef213fba758837f9837fb402bbe65326e60ba0633dbe6c7f274d42c1b"}, - {file = "numba-0.60.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1527dc578b95c7c4ff248792ec33d097ba6bef9eda466c948b68dfc995c25781"}, - {file = "numba-0.60.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe0b28abb8d70f8160798f4de9d486143200f34458d34c4a214114e445d7124e"}, - {file = "numba-0.60.0-cp310-cp310-win_amd64.whl", hash = "sha256:19407ced081d7e2e4b8d8c36aa57b7452e0283871c296e12d798852bc7d7f198"}, - {file = "numba-0.60.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a17b70fc9e380ee29c42717e8cc0bfaa5556c416d94f9aa96ba13acb41bdece8"}, - {file = "numba-0.60.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3fb02b344a2a80efa6f677aa5c40cd5dd452e1b35f8d1c2af0dfd9ada9978e4b"}, - {file = "numba-0.60.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5f4fde652ea604ea3c86508a3fb31556a6157b2c76c8b51b1d45eb40c8598703"}, - {file = "numba-0.60.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4142d7ac0210cc86432b818338a2bc368dc773a2f5cf1e32ff7c5b378bd63ee8"}, - {file = "numba-0.60.0-cp311-cp311-win_amd64.whl", hash = "sha256:cac02c041e9b5bc8cf8f2034ff6f0dbafccd1ae9590dc146b3a02a45e53af4e2"}, - {file = "numba-0.60.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d7da4098db31182fc5ffe4bc42c6f24cd7d1cb8a14b59fd755bfee32e34b8404"}, - {file = "numba-0.60.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:38d6ea4c1f56417076ecf8fc327c831ae793282e0ff51080c5094cb726507b1c"}, - {file = "numba-0.60.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:62908d29fb6a3229c242e981ca27e32a6e606cc253fc9e8faeb0e48760de241e"}, - {file = "numba-0.60.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0ebaa91538e996f708f1ab30ef4d3ddc344b64b5227b67a57aa74f401bb68b9d"}, - {file = "numba-0.60.0-cp312-cp312-win_amd64.whl", hash = "sha256:f75262e8fe7fa96db1dca93d53a194a38c46da28b112b8a4aca168f0df860347"}, - {file = "numba-0.60.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:01ef4cd7d83abe087d644eaa3d95831b777aa21d441a23703d649e06b8e06b74"}, - {file = "numba-0.60.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:819a3dfd4630d95fd574036f99e47212a1af41cbcb019bf8afac63ff56834449"}, - {file = "numba-0.60.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0b983bd6ad82fe868493012487f34eae8bf7dd94654951404114f23c3466d34b"}, - {file = "numba-0.60.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c151748cd269ddeab66334bd754817ffc0cabd9433acb0f551697e5151917d25"}, - {file = "numba-0.60.0-cp39-cp39-win_amd64.whl", hash = "sha256:3031547a015710140e8c87226b4cfe927cac199835e5bf7d4fe5cb64e814e3ab"}, - {file = "numba-0.60.0.tar.gz", hash = "sha256:5df6158e5584eece5fc83294b949fd30b9f1125df7708862205217e068aabf16"}, + {file = "numba-0.51.2-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:af798310eeb318c56cdb83254abbe9a938cc0182d08671d7f9f032dc817e064d"}, + {file = "numba-0.51.2-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:93e18350f2094e7432321c1275730a3143b94af012fb609cc180fa376c44867f"}, + {file = "numba-0.51.2-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:9e2bb1f129bfadd757ad7a9c18ab79c3ab25ce6d6a68e58565d6c52ad07b3566"}, + {file = "numba-0.51.2-cp36-cp36m-win32.whl", hash = "sha256:31cdf6b6d1301d5fb6c4fcb8b4c711ba5c9f60ba2fca008b550da9b56185367c"}, + {file = "numba-0.51.2-cp36-cp36m-win_amd64.whl", hash = "sha256:df6edca13c04a31fdb5addf5205199478a7da372712829157ef491e8a6e7031f"}, + {file = "numba-0.51.2-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:a628122dacfcba9a3ea68a9e95578c6b6391016e34962c46550ea8e189e0412e"}, + {file = "numba-0.51.2-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:106736d5a8dab6bebce989d4ab1b3f169c264582598f172e6e5b736210d2e834"}, + {file = "numba-0.51.2-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:a12f16fdb4ca5edc94e2ef412e4e768c29217ef9b6fdfc237d064ebe30acfe14"}, + {file = "numba-0.51.2-cp37-cp37m-win32.whl", hash = "sha256:025b033fd31c44bba17802293c81270084b5454b5b055b8c10c394385c232f00"}, + {file = "numba-0.51.2-cp37-cp37m-win_amd64.whl", hash = "sha256:081788f584fa500339e9b74bf02e3c5029d408c114e555ada19cae0b92721416"}, + {file = "numba-0.51.2-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:5416b584183fd599afda11b947b64f89450fcf26a9c15b408167f412b98a3a94"}, + {file = "numba-0.51.2-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:05da65dca2ac28a192c9d8f20e9e477eb1237205cfc4d131c414f5f8092c6639"}, + {file = "numba-0.51.2-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:aee435e3b7e465dd49971f8ea76aa414532a87736916cb399534e017334d1138"}, + {file = "numba-0.51.2-cp38-cp38-win32.whl", hash = "sha256:bbbe2432433b11d3fadab0226a84c1a81918cb905ba1aeb022249e8d2ba8856c"}, + {file = "numba-0.51.2-cp38-cp38-win_amd64.whl", hash = "sha256:259e7c15b24feec4a99fb41eb8c47b5ad49b544d1a5ad40ad0252ef531ba06fd"}, + {file = "numba-0.51.2.tar.gz", hash = "sha256:16bd59572114adbf5f600ea383880d7b2071ae45477e84a24994e089ea390768"}, ] [package.dependencies] -llvmlite = "==0.43.*" -numpy = ">=1.22,<2.1" +llvmlite = "==0.34.*" +numpy = ">=1.15" +setuptools = "*" [[package]] name = "numcodecs" -version = "0.12.1" +version = "0.13.1" description = "A Python package providing buffer compression and transformation codecs for use in data storage and communication applications." optional = true -python-versions = ">=3.8" -files = [ - {file = "numcodecs-0.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d37f628fe92b3699e65831d5733feca74d2e33b50ef29118ffd41c13c677210e"}, - {file = "numcodecs-0.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:941b7446b68cf79f089bcfe92edaa3b154533dcbcd82474f994b28f2eedb1c60"}, - {file = "numcodecs-0.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e79bf9d1d37199ac00a60ff3adb64757523291d19d03116832e600cac391c51"}, - {file = "numcodecs-0.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:82d7107f80f9307235cb7e74719292d101c7ea1e393fe628817f0d635b7384f5"}, - {file = "numcodecs-0.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:eeaf42768910f1c6eebf6c1bb00160728e62c9343df9e2e315dc9fe12e3f6071"}, - {file = "numcodecs-0.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:135b2d47563f7b9dc5ee6ce3d1b81b0f1397f69309e909f1a35bb0f7c553d45e"}, - {file = "numcodecs-0.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a191a8e347ecd016e5c357f2bf41fbcb026f6ffe78fff50c77ab12e96701d155"}, - {file = "numcodecs-0.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:21d8267bd4313f4d16f5b6287731d4c8ebdab236038f29ad1b0e93c9b2ca64ee"}, - {file = "numcodecs-0.12.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:2f84df6b8693206365a5b37c005bfa9d1be486122bde683a7b6446af4b75d862"}, - {file = "numcodecs-0.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:760627780a8b6afdb7f942f2a0ddaf4e31d3d7eea1d8498cf0fd3204a33c4618"}, - {file = "numcodecs-0.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c258bd1d3dfa75a9b708540d23b2da43d63607f9df76dfa0309a7597d1de3b73"}, - {file = "numcodecs-0.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:e04649ea504aff858dbe294631f098fbfd671baf58bfc04fc48d746554c05d67"}, - {file = "numcodecs-0.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:caf1a1e6678aab9c1e29d2109b299f7a467bd4d4c34235b1f0e082167846b88f"}, - {file = "numcodecs-0.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c17687b1fd1fef68af616bc83f896035d24e40e04e91e7e6dae56379eb59fe33"}, - {file = "numcodecs-0.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29dfb195f835a55c4d490fb097aac8c1bcb96c54cf1b037d9218492c95e9d8c5"}, - {file = "numcodecs-0.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:2f1ba2f4af3fd3ba65b1bcffb717fe65efe101a50a91c368f79f3101dbb1e243"}, - {file = "numcodecs-0.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2fbb12a6a1abe95926f25c65e283762d63a9bf9e43c0de2c6a1a798347dfcb40"}, - {file = "numcodecs-0.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f2207871868b2464dc11c513965fd99b958a9d7cde2629be7b2dc84fdaab013b"}, - {file = "numcodecs-0.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abff3554a6892a89aacf7b642a044e4535499edf07aeae2f2e6e8fc08c9ba07f"}, - {file = "numcodecs-0.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:ef964d4860d3e6b38df0633caf3e51dc850a6293fd8e93240473642681d95136"}, - {file = "numcodecs-0.12.1.tar.gz", hash = "sha256:05d91a433733e7eef268d7e80ec226a0232da244289614a8f3826901aec1098e"}, +python-versions = ">=3.10" +files = [ + {file = "numcodecs-0.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:96add4f783c5ce57cc7e650b6cac79dd101daf887c479a00a29bc1487ced180b"}, + {file = "numcodecs-0.13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:237b7171609e868a20fd313748494444458ccd696062f67e198f7f8f52000c15"}, + {file = "numcodecs-0.13.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96e42f73c31b8c24259c5fac6adba0c3ebf95536e37749dc6c62ade2989dca28"}, + {file = "numcodecs-0.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:eda7d7823c9282e65234731fd6bd3986b1f9e035755f7fed248d7d366bb291ab"}, + {file = "numcodecs-0.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2eda97dd2f90add98df6d295f2c6ae846043396e3d51a739ca5db6c03b5eb666"}, + {file = "numcodecs-0.13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2a86f5367af9168e30f99727ff03b27d849c31ad4522060dde0bce2923b3a8bc"}, + {file = "numcodecs-0.13.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233bc7f26abce24d57e44ea8ebeb5cd17084690b4e7409dd470fdb75528d615f"}, + {file = "numcodecs-0.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:796b3e6740107e4fa624cc636248a1580138b3f1c579160f260f76ff13a4261b"}, + {file = "numcodecs-0.13.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5195bea384a6428f8afcece793860b1ab0ae28143c853f0b2b20d55a8947c917"}, + {file = "numcodecs-0.13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3501a848adaddce98a71a262fee15cd3618312692aa419da77acd18af4a6a3f6"}, + {file = "numcodecs-0.13.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da2230484e6102e5fa3cc1a5dd37ca1f92dfbd183d91662074d6f7574e3e8f53"}, + {file = "numcodecs-0.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:e5db4824ebd5389ea30e54bc8aeccb82d514d28b6b68da6c536b8fa4596f4bca"}, + {file = "numcodecs-0.13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7a60d75179fd6692e301ddfb3b266d51eb598606dcae7b9fc57f986e8d65cb43"}, + {file = "numcodecs-0.13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3f593c7506b0ab248961a3b13cb148cc6e8355662ff124ac591822310bc55ecf"}, + {file = "numcodecs-0.13.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80d3071465f03522e776a31045ddf2cfee7f52df468b977ed3afdd7fe5869701"}, + {file = "numcodecs-0.13.1-cp313-cp313-win_amd64.whl", hash = "sha256:90d3065ae74c9342048ae0046006f99dcb1388b7288da5a19b3bddf9c30c3176"}, + {file = "numcodecs-0.13.1.tar.gz", hash = "sha256:a3cf37881df0898f3a9c0d4477df88133fe85185bffe57ba31bcc2fa207709bc"}, ] [package.dependencies] numpy = ">=1.7" [package.extras] -docs = ["mock", "numpydoc", "sphinx (<7.0.0)", "sphinx-issues"] +docs = ["mock", "numpydoc", "pydata-sphinx-theme", "sphinx", "sphinx-issues"] msgpack = ["msgpack"] -test = ["coverage", "flake8", "pytest", "pytest-cov"] +pcodec = ["pcodec (>=0.2.0)"] +test = ["coverage", "pytest", "pytest-cov"] test-extras = ["importlib-metadata"] -zfpy = ["zfpy (>=1.0.0)"] +zfpy = ["numpy (<2.0.0)", "zfpy (>=1.0.0)"] [[package]] name = "numpy" -version = "1.26.4" +version = "2.2.0" description = "Fundamental package for array computing in Python" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" +files = [ + {file = "numpy-2.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1e25507d85da11ff5066269d0bd25d06e0a0f2e908415534f3e603d2a78e4ffa"}, + {file = "numpy-2.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a62eb442011776e4036af5c8b1a00b706c5bc02dc15eb5344b0c750428c94219"}, + {file = "numpy-2.2.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:b606b1aaf802e6468c2608c65ff7ece53eae1a6874b3765f69b8ceb20c5fa78e"}, + {file = "numpy-2.2.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:36b2b43146f646642b425dd2027730f99bac962618ec2052932157e213a040e9"}, + {file = "numpy-2.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fe8f3583e0607ad4e43a954e35c1748b553bfe9fdac8635c02058023277d1b3"}, + {file = "numpy-2.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:122fd2fcfafdefc889c64ad99c228d5a1f9692c3a83f56c292618a59aa60ae83"}, + {file = "numpy-2.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3f2f5cddeaa4424a0a118924b988746db6ffa8565e5829b1841a8a3bd73eb59a"}, + {file = "numpy-2.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7fe4bb0695fe986a9e4deec3b6857003b4cfe5c5e4aac0b95f6a658c14635e31"}, + {file = "numpy-2.2.0-cp310-cp310-win32.whl", hash = "sha256:b30042fe92dbd79f1ba7f6898fada10bdaad1847c44f2dff9a16147e00a93661"}, + {file = "numpy-2.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:54dc1d6d66f8d37843ed281773c7174f03bf7ad826523f73435deb88ba60d2d4"}, + {file = "numpy-2.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9874bc2ff574c40ab7a5cbb7464bf9b045d617e36754a7bc93f933d52bd9ffc6"}, + {file = "numpy-2.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0da8495970f6b101ddd0c38ace92edea30e7e12b9a926b57f5fabb1ecc25bb90"}, + {file = "numpy-2.2.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0557eebc699c1c34cccdd8c3778c9294e8196df27d713706895edc6f57d29608"}, + {file = "numpy-2.2.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:3579eaeb5e07f3ded59298ce22b65f877a86ba8e9fe701f5576c99bb17c283da"}, + {file = "numpy-2.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40deb10198bbaa531509aad0cd2f9fadb26c8b94070831e2208e7df543562b74"}, + {file = "numpy-2.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2aed8fcf8abc3020d6a9ccb31dbc9e7d7819c56a348cc88fd44be269b37427e"}, + {file = "numpy-2.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a222d764352c773aa5ebde02dd84dba3279c81c6db2e482d62a3fa54e5ece69b"}, + {file = "numpy-2.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4e58666988605e251d42c2818c7d3d8991555381be26399303053b58a5bbf30d"}, + {file = "numpy-2.2.0-cp311-cp311-win32.whl", hash = "sha256:4723a50e1523e1de4fccd1b9a6dcea750c2102461e9a02b2ac55ffeae09a4410"}, + {file = "numpy-2.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:16757cf28621e43e252c560d25b15f18a2f11da94fea344bf26c599b9cf54b73"}, + {file = "numpy-2.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cff210198bb4cae3f3c100444c5eaa573a823f05c253e7188e1362a5555235b3"}, + {file = "numpy-2.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58b92a5828bd4d9aa0952492b7de803135038de47343b2aa3cc23f3b71a3dc4e"}, + {file = "numpy-2.2.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:ebe5e59545401fbb1b24da76f006ab19734ae71e703cdb4a8b347e84a0cece67"}, + {file = "numpy-2.2.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e2b8cd48a9942ed3f85b95ca4105c45758438c7ed28fff1e4ce3e57c3b589d8e"}, + {file = "numpy-2.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57fcc997ffc0bef234b8875a54d4058afa92b0b0c4223fc1f62f24b3b5e86038"}, + {file = "numpy-2.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ad7d11b309bd132d74397fcf2920933c9d1dc865487128f5c03d580f2c3d03"}, + {file = "numpy-2.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cb24cca1968b21355cc6f3da1a20cd1cebd8a023e3c5b09b432444617949085a"}, + {file = "numpy-2.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0798b138c291d792f8ea40fe3768610f3c7dd2574389e37c3f26573757c8f7ef"}, + {file = "numpy-2.2.0-cp312-cp312-win32.whl", hash = "sha256:afe8fb968743d40435c3827632fd36c5fbde633b0423da7692e426529b1759b1"}, + {file = "numpy-2.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:3a4199f519e57d517ebd48cb76b36c82da0360781c6a0353e64c0cac30ecaad3"}, + {file = "numpy-2.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f8c8b141ef9699ae777c6278b52c706b653bf15d135d302754f6b2e90eb30367"}, + {file = "numpy-2.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0f0986e917aca18f7a567b812ef7ca9391288e2acb7a4308aa9d265bd724bdae"}, + {file = "numpy-2.2.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:1c92113619f7b272838b8d6702a7f8ebe5edea0df48166c47929611d0b4dea69"}, + {file = "numpy-2.2.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:5a145e956b374e72ad1dff82779177d4a3c62bc8248f41b80cb5122e68f22d13"}, + {file = "numpy-2.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18142b497d70a34b01642b9feabb70156311b326fdddd875a9981f34a369b671"}, + {file = "numpy-2.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7d41d1612c1a82b64697e894b75db6758d4f21c3ec069d841e60ebe54b5b571"}, + {file = "numpy-2.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a98f6f20465e7618c83252c02041517bd2f7ea29be5378f09667a8f654a5918d"}, + {file = "numpy-2.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e09d40edfdb4e260cb1567d8ae770ccf3b8b7e9f0d9b5c2a9992696b30ce2742"}, + {file = "numpy-2.2.0-cp313-cp313-win32.whl", hash = "sha256:3905a5fffcc23e597ee4d9fb3fcd209bd658c352657548db7316e810ca80458e"}, + {file = "numpy-2.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:a184288538e6ad699cbe6b24859206e38ce5fba28f3bcfa51c90d0502c1582b2"}, + {file = "numpy-2.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7832f9e8eb00be32f15fdfb9a981d6955ea9adc8574c521d48710171b6c55e95"}, + {file = "numpy-2.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f0dd071b95bbca244f4cb7f70b77d2ff3aaaba7fa16dc41f58d14854a6204e6c"}, + {file = "numpy-2.2.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:b0b227dcff8cdc3efbce66d4e50891f04d0a387cce282fe1e66199146a6a8fca"}, + {file = "numpy-2.2.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:6ab153263a7c5ccaf6dfe7e53447b74f77789f28ecb278c3b5d49db7ece10d6d"}, + {file = "numpy-2.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e500aba968a48e9019e42c0c199b7ec0696a97fa69037bea163b55398e390529"}, + {file = "numpy-2.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:440cfb3db4c5029775803794f8638fbdbf71ec702caf32735f53b008e1eaece3"}, + {file = "numpy-2.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a55dc7a7f0b6198b07ec0cd445fbb98b05234e8b00c5ac4874a63372ba98d4ab"}, + {file = "numpy-2.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4bddbaa30d78c86329b26bd6aaaea06b1e47444da99eddac7bf1e2fab717bd72"}, + {file = "numpy-2.2.0-cp313-cp313t-win32.whl", hash = "sha256:30bf971c12e4365153afb31fc73f441d4da157153f3400b82db32d04de1e4066"}, + {file = "numpy-2.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d35717333b39d1b6bb8433fa758a55f1081543de527171543a2b710551d40881"}, + {file = "numpy-2.2.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e12c6c1ce84628c52d6367863773f7c8c8241be554e8b79686e91a43f1733773"}, + {file = "numpy-2.2.0-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:b6207dc8fb3c8cb5668e885cef9ec7f70189bec4e276f0ff70d5aa078d32c88e"}, + {file = "numpy-2.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a50aeff71d0f97b6450d33940c7181b08be1441c6c193e678211bff11aa725e7"}, + {file = "numpy-2.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:df12a1f99b99f569a7c2ae59aa2d31724e8d835fc7f33e14f4792e3071d11221"}, + {file = "numpy-2.2.0.tar.gz", hash = "sha256:140dd80ff8981a583a60980be1a655068f8adebf7a45a06a6858c873fcdcd4a0"}, +] + +[[package]] +name = "nvidia-cublas-cu12" +version = "12.4.5.8" +description = "CUBLAS native runtime libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0f8aa1706812e00b9f19dfe0cdb3999b092ccb8ca168c0db5b8ea712456fd9b3"}, + {file = "nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_x86_64.whl", hash = "sha256:2fc8da60df463fdefa81e323eef2e36489e1c94335b5358bcb38360adf75ac9b"}, + {file = "nvidia_cublas_cu12-12.4.5.8-py3-none-win_amd64.whl", hash = "sha256:5a796786da89203a0657eda402bcdcec6180254a8ac22d72213abc42069522dc"}, +] + +[[package]] +name = "nvidia-cuda-cupti-cu12" +version = "12.4.127" +description = "CUDA profiling tools runtime libs." +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:79279b35cf6f91da114182a5ce1864997fd52294a87a16179ce275773799458a"}, + {file = "nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:9dec60f5ac126f7bb551c055072b69d85392b13311fcc1bcda2202d172df30fb"}, + {file = "nvidia_cuda_cupti_cu12-12.4.127-py3-none-win_amd64.whl", hash = "sha256:5688d203301ab051449a2b1cb6690fbe90d2b372f411521c86018b950f3d7922"}, +] + +[[package]] +name = "nvidia-cuda-nvrtc-cu12" +version = "12.4.127" +description = "NVRTC native runtime libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0eedf14185e04b76aa05b1fea04133e59f465b6f960c0cbf4e37c3cb6b0ea198"}, + {file = "nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a178759ebb095827bd30ef56598ec182b85547f1508941a3d560eb7ea1fbf338"}, + {file = "nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-win_amd64.whl", hash = "sha256:a961b2f1d5f17b14867c619ceb99ef6fcec12e46612711bcec78eb05068a60ec"}, +] + +[[package]] +name = "nvidia-cuda-runtime-cu12" +version = "12.4.127" +description = "CUDA Runtime native Libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:961fe0e2e716a2a1d967aab7caee97512f71767f852f67432d572e36cb3a11f3"}, + {file = "nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:64403288fa2136ee8e467cdc9c9427e0434110899d07c779f25b5c068934faa5"}, + {file = "nvidia_cuda_runtime_cu12-12.4.127-py3-none-win_amd64.whl", hash = "sha256:09c2e35f48359752dfa822c09918211844a3d93c100a715d79b59591130c5e1e"}, +] + +[[package]] +name = "nvidia-cudnn-cu12" +version = "9.1.0.70" +description = "cuDNN runtime libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cudnn_cu12-9.1.0.70-py3-none-manylinux2014_x86_64.whl", hash = "sha256:165764f44ef8c61fcdfdfdbe769d687e06374059fbb388b6c89ecb0e28793a6f"}, + {file = "nvidia_cudnn_cu12-9.1.0.70-py3-none-win_amd64.whl", hash = "sha256:6278562929433d68365a07a4a1546c237ba2849852c0d4b2262a486e805b977a"}, +] + +[package.dependencies] +nvidia-cublas-cu12 = "*" + +[[package]] +name = "nvidia-cufft-cu12" +version = "11.2.1.3" +description = "CUFFT native runtime libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_aarch64.whl", hash = "sha256:5dad8008fc7f92f5ddfa2101430917ce2ffacd86824914c82e28990ad7f00399"}, + {file = "nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f083fc24912aa410be21fa16d157fed2055dab1cc4b6934a0e03cba69eb242b9"}, + {file = "nvidia_cufft_cu12-11.2.1.3-py3-none-win_amd64.whl", hash = "sha256:d802f4954291101186078ccbe22fc285a902136f974d369540fd4a5333d1440b"}, +] + +[package.dependencies] +nvidia-nvjitlink-cu12 = "*" + +[[package]] +name = "nvidia-curand-cu12" +version = "10.3.5.147" +description = "CURAND native runtime libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_aarch64.whl", hash = "sha256:1f173f09e3e3c76ab084aba0de819c49e56614feae5c12f69883f4ae9bb5fad9"}, + {file = "nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a88f583d4e0bb643c49743469964103aa59f7f708d862c3ddb0fc07f851e3b8b"}, + {file = "nvidia_curand_cu12-10.3.5.147-py3-none-win_amd64.whl", hash = "sha256:f307cc191f96efe9e8f05a87096abc20d08845a841889ef78cb06924437f6771"}, +] + +[[package]] +name = "nvidia-cusolver-cu12" +version = "11.6.1.9" +description = "CUDA solver native runtime libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cusolver_cu12-11.6.1.9-py3-none-manylinux2014_aarch64.whl", hash = "sha256:d338f155f174f90724bbde3758b7ac375a70ce8e706d70b018dd3375545fc84e"}, + {file = "nvidia_cusolver_cu12-11.6.1.9-py3-none-manylinux2014_x86_64.whl", hash = "sha256:19e33fa442bcfd085b3086c4ebf7e8debc07cfe01e11513cc6d332fd918ac260"}, + {file = "nvidia_cusolver_cu12-11.6.1.9-py3-none-win_amd64.whl", hash = "sha256:e77314c9d7b694fcebc84f58989f3aa4fb4cb442f12ca1a9bde50f5e8f6d1b9c"}, +] + +[package.dependencies] +nvidia-cublas-cu12 = "*" +nvidia-cusparse-cu12 = "*" +nvidia-nvjitlink-cu12 = "*" + +[[package]] +name = "nvidia-cusparse-cu12" +version = "12.3.1.170" +description = "CUSPARSE native runtime libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cusparse_cu12-12.3.1.170-py3-none-manylinux2014_aarch64.whl", hash = "sha256:9d32f62896231ebe0480efd8a7f702e143c98cfaa0e8a76df3386c1ba2b54df3"}, + {file = "nvidia_cusparse_cu12-12.3.1.170-py3-none-manylinux2014_x86_64.whl", hash = "sha256:ea4f11a2904e2a8dc4b1833cc1b5181cde564edd0d5cd33e3c168eff2d1863f1"}, + {file = "nvidia_cusparse_cu12-12.3.1.170-py3-none-win_amd64.whl", hash = "sha256:9bc90fb087bc7b4c15641521f31c0371e9a612fc2ba12c338d3ae032e6b6797f"}, +] + +[package.dependencies] +nvidia-nvjitlink-cu12 = "*" + +[[package]] +name = "nvidia-nccl-cu12" +version = "2.21.5" +description = "NVIDIA Collective Communication Library (NCCL) Runtime" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_nccl_cu12-2.21.5-py3-none-manylinux2014_x86_64.whl", hash = "sha256:8579076d30a8c24988834445f8d633c697d42397e92ffc3f63fa26766d25e0a0"}, +] + +[[package]] +name = "nvidia-nvjitlink-cu12" +version = "12.4.127" +description = "Nvidia JIT LTO Library" +optional = false +python-versions = ">=3" files = [ - {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, - {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, - {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, - {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, - {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, - {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, - {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, - {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, - {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, - {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, - {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, - {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, - {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, - {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, - {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, - {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, - {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, - {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, - {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, - {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, - {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, - {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, - {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, - {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, - {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, - {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, - {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, - {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, - {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, - {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, - {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, - {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, - {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, - {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, - {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, - {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, + {file = "nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:4abe7fef64914ccfa909bc2ba39739670ecc9e820c83ccc7a6ed414122599b83"}, + {file = "nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:06b3b9b25bf3f8af351d664978ca26a16d2c5127dbd53c0497e28d1fb9611d57"}, + {file = "nvidia_nvjitlink_cu12-12.4.127-py3-none-win_amd64.whl", hash = "sha256:fd9020c501d27d135f983c6d3e244b197a7ccad769e34df53a42e276b0e25fa1"}, +] + +[[package]] +name = "nvidia-nvtx-cu12" +version = "12.4.127" +description = "NVIDIA Tools Extension" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_nvtx_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7959ad635db13edf4fc65c06a6e9f9e55fc2f92596db928d169c0bb031e88ef3"}, + {file = "nvidia_nvtx_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:781e950d9b9f60d8241ccea575b32f5105a5baf4c2351cab5256a24869f12a1a"}, + {file = "nvidia_nvtx_cu12-12.4.127-py3-none-win_amd64.whl", hash = "sha256:641dccaaa1139f3ffb0d3164b4b84f9d253397e38246a4f2f36728b48566d485"}, ] [[package]] name = "opentelemetry-api" -version = "1.27.0" +version = "1.28.2" description = "OpenTelemetry Python API" optional = false python-versions = ">=3.8" files = [ - {file = "opentelemetry_api-1.27.0-py3-none-any.whl", hash = "sha256:953d5871815e7c30c81b56d910c707588000fff7a3ca1c73e6531911d53065e7"}, - {file = "opentelemetry_api-1.27.0.tar.gz", hash = "sha256:ed673583eaa5f81b5ce5e86ef7cdaf622f88ef65f0b9aab40b843dcae5bef342"}, + {file = "opentelemetry_api-1.28.2-py3-none-any.whl", hash = "sha256:6fcec89e265beb258fe6b1acaaa3c8c705a934bd977b9f534a2b7c0d2d4275a6"}, + {file = "opentelemetry_api-1.28.2.tar.gz", hash = "sha256:ecdc70c7139f17f9b0cf3742d57d7020e3e8315d6cffcdf1a12a905d45b19cc0"}, ] [package.dependencies] deprecated = ">=1.2.6" -importlib-metadata = ">=6.0,<=8.4.0" +importlib-metadata = ">=6.0,<=8.5.0" [[package]] name = "opentelemetry-sdk" -version = "1.27.0" +version = "1.28.2" description = "OpenTelemetry Python SDK" optional = false python-versions = ">=3.8" files = [ - {file = "opentelemetry_sdk-1.27.0-py3-none-any.whl", hash = "sha256:365f5e32f920faf0fd9e14fdfd92c086e317eaa5f860edba9cdc17a380d9197d"}, - {file = "opentelemetry_sdk-1.27.0.tar.gz", hash = "sha256:d525017dea0ccce9ba4e0245100ec46ecdc043f2d7b8315d56b19aff0904fa6f"}, + {file = "opentelemetry_sdk-1.28.2-py3-none-any.whl", hash = "sha256:93336c129556f1e3ccd21442b94d3521759541521861b2214c499571b85cb71b"}, + {file = "opentelemetry_sdk-1.28.2.tar.gz", hash = "sha256:5fed24c5497e10df30282456fe2910f83377797511de07d14cec0d3e0a1a3110"}, ] [package.dependencies] -opentelemetry-api = "1.27.0" -opentelemetry-semantic-conventions = "0.48b0" +opentelemetry-api = "1.28.2" +opentelemetry-semantic-conventions = "0.49b2" typing-extensions = ">=3.7.4" [[package]] name = "opentelemetry-semantic-conventions" -version = "0.48b0" +version = "0.49b2" description = "OpenTelemetry Semantic Conventions" optional = false python-versions = ">=3.8" files = [ - {file = "opentelemetry_semantic_conventions-0.48b0-py3-none-any.whl", hash = "sha256:a0de9f45c413a8669788a38569c7e0a11ce6ce97861a628cca785deecdc32a1f"}, - {file = "opentelemetry_semantic_conventions-0.48b0.tar.gz", hash = "sha256:12d74983783b6878162208be57c9effcb89dc88691c64992d70bb89dc00daa1a"}, + {file = "opentelemetry_semantic_conventions-0.49b2-py3-none-any.whl", hash = "sha256:51e7e1d0daa958782b6c2a8ed05e5f0e7dd0716fc327ac058777b8659649ee54"}, + {file = "opentelemetry_semantic_conventions-0.49b2.tar.gz", hash = "sha256:44e32ce6a5bb8d7c0c617f84b9dc1c8deda1045a07dc16a688cc7cbeab679997"}, ] [package.dependencies] deprecated = ">=1.2.6" -opentelemetry-api = "1.27.0" - -[[package]] -name = "opt-einsum" -version = "3.4.0" -description = "Path optimization of einsum functions." -optional = false -python-versions = ">=3.8" -files = [ - {file = "opt_einsum-3.4.0-py3-none-any.whl", hash = "sha256:69bb92469f86a1565195ece4ac0323943e83477171b91d24c35afe028a90d7cd"}, - {file = "opt_einsum-3.4.0.tar.gz", hash = "sha256:96ca72f1b886d148241348783498194c577fa30a8faac108586b14f1ba4473ac"}, -] +opentelemetry-api = "1.28.2" [[package]] name = "optree" -version = "0.13.0" +version = "0.13.1" description = "Optimized PyTree Utilities." optional = false python-versions = ">=3.7" files = [ - {file = "optree-0.13.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7b8fe0442ac5e50b5e6bceb37dcc2cd4908e7716b869cbe6b8901cc0b489884f"}, - {file = "optree-0.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1a1aab34de5ac7673fbfb94266bf10482be51985c7f899c3e767ce19d13ce3b4"}, - {file = "optree-0.13.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2c79961d5afeb20557c30a0ae899d14ff58cdf1c0e2c8aa3d6807600d00f619"}, - {file = "optree-0.13.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb55eb77541cf280a009829280d5844936dc8a2e4a3eb069c010a1f547dbfe97"}, - {file = "optree-0.13.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44449e3bc5e7530b50c9a1f5bcf2971ffe317e34edd74d8c9778c5d32078114d"}, - {file = "optree-0.13.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b4195a6ba2052c70bac6d73f19aa69644424c5a30fa09f7319cc1b59e15acb6"}, - {file = "optree-0.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7fecc701ece0500fe38fc671b5704d904e2dca9a9284b35263b0bd7e5c62527"}, - {file = "optree-0.13.0-cp310-cp310-win32.whl", hash = "sha256:46a9e66217fdf421e25c133089c94f8f99bc38a2b5a4a2c0c1e0c1b02b01dda4"}, - {file = "optree-0.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:ef68fdcb3b1743a46210f3c888cd15668a07422aa10b4d4130ba512aac595bf7"}, - {file = "optree-0.13.0-cp310-cp310-win_arm64.whl", hash = "sha256:d12a5665169abceb878d50b55571d6a7690bf97aaaf9a7f5438b10e474fde3f2"}, - {file = "optree-0.13.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:92d1c34b6022bedee4b3899f3a9a1105777da11a9abf1a51f4d84bed8f037fa1"}, - {file = "optree-0.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d05c320af21efbc132fe887640f7a2dbb36cfb38af6d4e62396fe104b78f7b72"}, - {file = "optree-0.13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a53ae0a0eb128a69a74db4165e7e5f24d54e2711678622198f7073dcb991962f"}, - {file = "optree-0.13.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89f08fc3724b2fe7a081b69dfd3ad6625960443e1f61a984cae7c627776f12f4"}, - {file = "optree-0.13.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f22f4e46d85f24b5bc49e68043dd754b258b880ac64d72f4f4b9ac1b11f0fb2f"}, - {file = "optree-0.13.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbc884f3eab894126398c120b7f92a72a5b9f92db6d8c27d39087da871c642cd"}, - {file = "optree-0.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c58b94669c9072d645e02c0c65c7455f8f136ef8f7b56a5d9123847421f95b"}, - {file = "optree-0.13.0-cp311-cp311-win32.whl", hash = "sha256:54be625517ef3cf52905da7fee63795b2f154dbdb02b37e8cfd63e7fb2f266ea"}, - {file = "optree-0.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:e3d100890a643e12f39de4226ab4f9d0a22842b4f34ae2964d0149419e4d7aff"}, - {file = "optree-0.13.0-cp311-cp311-win_arm64.whl", hash = "sha256:cb8d9a2cebc5fadde98773bb27809a72ff01d11f1037cb58f8e71e740586223e"}, - {file = "optree-0.13.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:abeb8acc83d168063b70168ccf8dfd55f5a7ce50f9af2ca025c41285781ecdd4"}, - {file = "optree-0.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4771266f05e99e94312add38d45bf97a4d98449aeab100f5c658c521152eb5e5"}, - {file = "optree-0.13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc95c1d0c7acd534184bf3ba243a454e0942e4a7c8b9edd32d939fc15e33d753"}, - {file = "optree-0.13.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e48491e042f956d4232ebc138e07074100878c0080e3ba10af4c2db1ba4df9f"}, - {file = "optree-0.13.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8e001d9c902e98912503eca66c93d4b4b22f5071e4ab777f4db9e140f35288f4"}, - {file = "optree-0.13.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87870346278f46a8c22866ff48716590be35b4aea16e1373e695fb6442c28c41"}, - {file = "optree-0.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7797c54a35e9d89b4664ec7d542745b87b5ffa9c1201c1062fdcd488eb583390"}, - {file = "optree-0.13.0-cp312-cp312-win32.whl", hash = "sha256:fc90a5373c92f4a9babb4c40fe148516f52160c0ba803bc9b2f936367f2f7437"}, - {file = "optree-0.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:1bc65743e8edb29e902cab894d1c4665a8fd6f8d10f75db68a2cef6c7246fa5c"}, - {file = "optree-0.13.0-cp312-cp312-win_arm64.whl", hash = "sha256:de2729e1e4ae47a07ac3c70ff977ed1ebe19e7b44d5089075c94f7a9a2dc6f4f"}, - {file = "optree-0.13.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dda6efabd0621f53eb46a3789ec89c6fd2c90dfb57aebfce3fcda6eab9ed6a7e"}, - {file = "optree-0.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5de8da9bbdd08b6200244ee818cd15d1da0f2b06ac926dba0e686260bac7fd40"}, - {file = "optree-0.13.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca1e4854134023ba687a7abf45ed3355f773ca7198b6895d88a89030446a9f2e"}, - {file = "optree-0.13.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1ac5343e921ce21f8f10f91158ad6404a1488c1cc22ddfa6b34cfb9d997cebd"}, - {file = "optree-0.13.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e282212ddf3aafb10ca6ca223772e06ea3c31687c9cae192467b8e0a7dafbfc"}, - {file = "optree-0.13.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:24fcd4cb659bcd9b675bc3401950de891b32a047c4787857fb870cd515fcc315"}, - {file = "optree-0.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d735a7d2d2e2eb9a88d932d35b335c10fae9038034f381b6d437dafed46497e"}, - {file = "optree-0.13.0-cp313-cp313-win32.whl", hash = "sha256:ef01e79224f0ee6cf2ca642884f0bc04e446227b96dc576c312717eb33552d57"}, - {file = "optree-0.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:d3f61fb669b36c1a714346b18c9c488ad33a58049b7b229785c241de18c005d7"}, - {file = "optree-0.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:695b3f1aab50519230e3d8d86abaedaadf91af105b569cce3b8ebe0dc612b312"}, - {file = "optree-0.13.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:1318434b0740a2325c197e191e6dd53d9df0a8ac0338c67d58b476aad9d07829"}, - {file = "optree-0.13.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d58c6e8d4c4fa4e0c31bc4b876960ccba94eb5fcfb045f2b064ce55707034be9"}, - {file = "optree-0.13.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6a290ba771cc9004f9fc194d23ab11ee4aae71550ca874c3dc985af5b5f910b"}, - {file = "optree-0.13.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c95488ecbab2916de094e68f2a2c55c9475b2e979c03d91a6cd3565f9e5ff2f9"}, - {file = "optree-0.13.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f76a65ff322b3d47af2a23f60409d6d8f184804da551c734e355834e69c0dfb"}, - {file = "optree-0.13.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:58cc303f982fb0f23644b7f8e98b4f64b0d031365fcc2284da896e96493176d2"}, - {file = "optree-0.13.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6866b6e4154303dc7c48c7ca3b867a8ce31d469334b67976dfc0513455aa1ca0"}, - {file = "optree-0.13.0-cp313-cp313t-win32.whl", hash = "sha256:f5ce67f81fe3d7ca5fed8fdaf93a762a63e1d125e20e425ca7200f9e54a3e3a6"}, - {file = "optree-0.13.0-cp313-cp313t-win_amd64.whl", hash = "sha256:0008cd39169c1fc10870528b2decfea8b79e61042c12d65a964f3b1cf41cc37d"}, - {file = "optree-0.13.0-cp313-cp313t-win_arm64.whl", hash = "sha256:539962675b547957c64b52b7f82178febb9c0f2d47438b810bbc23cfdcf84821"}, - {file = "optree-0.13.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b08e4873814d11aa25ef3927c848b9e5cf21215b925e83875b9fe11c7a035b0e"}, - {file = "optree-0.13.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6e236c6601480997c6e1dbbd4ab2b7ea0bc82a9a7baa1f681a1b072c9c02677"}, - {file = "optree-0.13.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:557b415b41006cca88d86ad190b795455e9334d3cf5838e63c4c668a65227ccb"}, - {file = "optree-0.13.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11b78c8a18894fe9503515d073a60ebaed366aeb3cfa65e61e7e71ae833f640b"}, - {file = "optree-0.13.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4207f6fa0bd4730f5496772c139f1444b2b69e4eeb0f454e2100b5a380648f70"}, - {file = "optree-0.13.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe9fd84b7d87f365f720699dedd254882ba7e5ef927d3ba1e13413d45963b691"}, - {file = "optree-0.13.0-cp37-cp37m-win32.whl", hash = "sha256:c0f9f250f617f114061ab718d460be6be8e0a1cbbfdbbfb5541ed1c8fefee150"}, - {file = "optree-0.13.0-cp37-cp37m-win_amd64.whl", hash = "sha256:5cf612aefe0201a2995763cce82b9cd03cbddd2bfd6f8975f910c091dfa7bb5f"}, - {file = "optree-0.13.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:46623259b10f6e3565ea0d37e0b313feb20484bccb005459b3504e1aa706b730"}, - {file = "optree-0.13.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e7f9184c6040365e79a0b900507c289b6a4e06ade3c9691e501d176d5cf775cf"}, - {file = "optree-0.13.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6201c065791422a73d5aeb4916e00879de9b097cf54526f82b5b3c297126d938"}, - {file = "optree-0.13.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a423897010c6d8490097671d907da1b6ee90d3fa783aaad5e36e46e0a73bc5e"}, - {file = "optree-0.13.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1fb74282fce108e07972e88dbc23f6b7650c2d3bbddbedc2002d3e0becb1c452"}, - {file = "optree-0.13.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94ecab158521225b20e44d67c8afc2d9af6760985a9f489d21bf2aa8bbe467f8"}, - {file = "optree-0.13.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8244d0fbfe1ef15ffb443f3d32a44aff062adbef0a7fd6db3f011999df966223"}, - {file = "optree-0.13.0-cp38-cp38-win32.whl", hash = "sha256:0a34c11d637cb01217828e28eef382c621c9ec53f981d8ccbfe56e0a11cda501"}, - {file = "optree-0.13.0-cp38-cp38-win_amd64.whl", hash = "sha256:ebe56c17bf3754335307b17be7f554c5eae47acf738471cf38dba0ec73a42c37"}, - {file = "optree-0.13.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e9c619a49984212e5f757e10d5e5f95888b0c08d67a7f2b9f395cede30712dc2"}, - {file = "optree-0.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:50a9e2d9ffff99d45b37289a3422ed3723a45225616f5b48cea606ff0f539c0f"}, - {file = "optree-0.13.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d702dbcafcd16e8925e30c0e780ab3dc81450e19008fd3e77494111fc161a2b2"}, - {file = "optree-0.13.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9f44a58f87059161f300e2be66ad3878fff540d27f5dcd69b21feae65c243a02"}, - {file = "optree-0.13.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:954899edc024f13079932418f59bbdadabc52d9dcb49c7b559c382c7be352dfc"}, - {file = "optree-0.13.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0c736ce6f4b8857bd171f3682ef849e3d67692c3fc4db42b99c5d2c7cc1bdf11"}, - {file = "optree-0.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7941d3bd48d860d0e17ca24827b5233ea27bb4227e822eafb3897df1f43f8342"}, - {file = "optree-0.13.0-cp39-cp39-win32.whl", hash = "sha256:9f6fc47c9b10d1a9e77163ebd6f2e251af41fab895475d2ce9643423a41899af"}, - {file = "optree-0.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:246020f0be50fb66791d8a25c4acb59ad0b4bbdea71c998e375eba4c58fbc3e0"}, - {file = "optree-0.13.0-cp39-cp39-win_arm64.whl", hash = "sha256:069bf166b7aa48ccf8dfe76b920d2115dd8261107c7895d02500b2ce39621b40"}, - {file = "optree-0.13.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:496170a3d093a7fb69be7ce847f5b5b3aa30a6da81457ba6b54268e6e97c6b13"}, - {file = "optree-0.13.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73543a82be71c041d5b169754089a58d02063eb72ac8688533b6fc26ab6beea8"}, - {file = "optree-0.13.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:278e2c620df99f5b1477b375b01cf9658528fa0332c0bc431d3ec65857244094"}, - {file = "optree-0.13.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36b32155dce29edb6f63a99a44d6da2d8fcd1c56353cc2f4af65f793a0b2712f"}, - {file = "optree-0.13.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c98a43204841cc4698155acb523d7b21a78f8b05666704359e0fddecd5d1043d"}, - {file = "optree-0.13.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5c2803d4ef257f2599cffd0e9d60cfb3d4c522abbe8f5a839bd48d8edd26dae7"}, - {file = "optree-0.13.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac3b454f98d28a89c15a1170e771c61902cbc53eed126db36138b684dba5a729"}, - {file = "optree-0.13.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b74afed3db289228e0f95a8909835365f644eb69ff31cd6c0b45608ca9e56d78"}, - {file = "optree-0.13.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc3cebfd7d0826d223662f01ed0fa25932edf3f62479be13c4d6ff0fab090c34"}, - {file = "optree-0.13.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:5703637ede6fba04cbeabbb47aada7d17606c2d4df73305063f4a3c829c21fc7"}, - {file = "optree-0.13.0.tar.gz", hash = "sha256:1ea493cde8c60f7950ccbd682bd67e787bf67ed2251d6d3e9ad7471b72d37538"}, + {file = "optree-0.13.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f8e2a546cecc5077ec7d4fe24ec8aede43ca8555b832d115f1ebbb4f3b35bc78"}, + {file = "optree-0.13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a3058e2d6a6a7d6362d40f7826258204d9fc2cc4cc8f72eaa3dbff14b6622025"}, + {file = "optree-0.13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34b4dd0f5d73170c7740726cadfca973220ccbed9559beb51fab446d9e584d0a"}, + {file = "optree-0.13.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1844b966bb5c95b64af5c6f92f99e4037452b92b18d060fbd80097b5b773d86"}, + {file = "optree-0.13.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d74ff3dfe8599935d52b26a2fe5a43242b4d3f47be6fc1c5ce34c25e116d616"}, + {file = "optree-0.13.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:940c739c9957404a9bbe40ed9289792adaf476cece59eca4fe2f32137fa15a8d"}, + {file = "optree-0.13.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cfdf7f5cfb5f9b1c0188c667a3dc56551e60a52a918cb8600f84e2f0ad882106"}, + {file = "optree-0.13.1-cp310-cp310-win32.whl", hash = "sha256:135e29e0a69149958003443d43f49af0ebb65f03ae52cddf4142e94d5a36b0c8"}, + {file = "optree-0.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:64032b77420410c3d315a4b9bcbece15853432c155613bb4261d87809b3ee357"}, + {file = "optree-0.13.1-cp310-cp310-win_arm64.whl", hash = "sha256:d0c5a389c108367007151bcfef494f8c2674e4aa23d80ac9163876f5b213dfb6"}, + {file = "optree-0.13.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c84ecb6977ba7f5d4ba24d0312cbffb74c6860237572701c2716bd811ca9b226"}, + {file = "optree-0.13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6bc9aae5ee17a38e3657c8c5db1a60923cc10debd177f6781f352362a846feeb"}, + {file = "optree-0.13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f94a627c5a2fb776bbfa8f7558db5b918916d37586ba943e74e5f22789c4301"}, + {file = "optree-0.13.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b21ac55473476007e317500fd5851d0a0d695a0c51742bd65fe7347d18530da2"}, + {file = "optree-0.13.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:360f2e8f7eb22ff131bc7e3e241035908e6b47d41372eb3d68d77bc7036ddb30"}, + {file = "optree-0.13.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5dec0785bc4bbcabecd7e82be3f189b21f3ce8a1244b243009736912a6d8f737"}, + {file = "optree-0.13.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efbffeec15e4a79ed9921dc2227cbba1b64db353c4b72ce4ce83e62fbce9e652"}, + {file = "optree-0.13.1-cp311-cp311-win32.whl", hash = "sha256:f74fb880472572d550d85d2f1563365b6f194e2157a7703790cbd54d9ab5cf29"}, + {file = "optree-0.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:0adc896018f34b5f37f6c92c35ae639877578725c5281cc9d4a0ac2ab2c46f77"}, + {file = "optree-0.13.1-cp311-cp311-win_arm64.whl", hash = "sha256:cf85ba1a7d80b6dc19ef5ca4c17d2ff0290dc9306c5b8b468d51cede287f3c8d"}, + {file = "optree-0.13.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0914ba436d6c0781dc9b04e3b95e06fe5c4fc6a87e94893da971805a3790efe8"}, + {file = "optree-0.13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:111172446e8a4f0d3be13a853fa28cb46b5679a1c7ca15b2e6db2b43dbbf9efb"}, + {file = "optree-0.13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28f083ede9be89503357a6b9e5d304826701596abe13d33e8f6fa2cd85b407fc"}, + {file = "optree-0.13.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0aec6da79a6130b4c76073241c0f31c11b96a38e70c7a00f9ed918d7464394ab"}, + {file = "optree-0.13.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a408a43f16840475612c7058eb80b53791bf8b8266c5b3cd07f69697958fd97d"}, + {file = "optree-0.13.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3da76fc43dcc22fe58d11634a04672ca7cc270aed469ac35fd5c78b7b9bc9125"}, + {file = "optree-0.13.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d866f707b9f3a9f0e670a73fe8feee4993b2dbdbf9eef598e1cf2e5cb2876413"}, + {file = "optree-0.13.1-cp312-cp312-win32.whl", hash = "sha256:bc9c396f64f9aacdf852713bd75f1b9a83f118660fd82e87c937c081b7ddccd1"}, + {file = "optree-0.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:587fb8de8e75e80fe7c7240e269630876bec3ee2038724893370976207813e4b"}, + {file = "optree-0.13.1-cp312-cp312-win_arm64.whl", hash = "sha256:5da0fd26325a07354915cc4e3a9aee797cb75dff07c60d24b3f309457069abd3"}, + {file = "optree-0.13.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f788b2ad120deb73b4908a74473cd6de79cfb9f33bbe9dcb59cea2e2477d4e28"}, + {file = "optree-0.13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2909cb42add6bb1a5a2b0243bdd8c4b861bf072f3741e26239481907ac8ad4e6"}, + {file = "optree-0.13.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbc5fa2ff5090389f3a906567446f01d692bd6fe5cfcc5ae2d5861f24e8e0e4d"}, + {file = "optree-0.13.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4711f5cac5a2a49c3d6c9f0eca7b77c22b452170bb33ea01c3214ebb17931db9"}, + {file = "optree-0.13.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c4ab1d391b89cb88eb3c63383d5eb0930bc21141de9d5acd277feed9e38eb65"}, + {file = "optree-0.13.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5e5f09c85ae558a6bdaea57e63168082e728e777391393e9e2792f0d15b7b59"}, + {file = "optree-0.13.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c8ee1e988c634a451146b87d9ebdbf650a75dc1f52a9cffcd89fabb7289321c"}, + {file = "optree-0.13.1-cp313-cp313-win32.whl", hash = "sha256:5b6531cd4eb23fadbbf77faf834e1119da06d7af3154f55786b59953cd87bb8a"}, + {file = "optree-0.13.1-cp313-cp313-win_amd64.whl", hash = "sha256:27d81dc43b522ba47ba7d2e7d91dbb486940348b1bf85caeb0afc2815c0aa492"}, + {file = "optree-0.13.1-cp313-cp313-win_arm64.whl", hash = "sha256:f39c7174a3f3cdc3f5fe6fb4b832f608c40ac174d7567ed6734b2ee952094631"}, + {file = "optree-0.13.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:3010ae24e994f6e00071098d34e98e78eb995b7454a2ef629a0bf7df17441b24"}, + {file = "optree-0.13.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5b5626c38d4a18a144063db5c1dbb558431d83ca10682324f74665a12214801f"}, + {file = "optree-0.13.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1935639dd498a42367633e3877797e1330e39d44d48bbca1a136bb4dbe4c1bc9"}, + {file = "optree-0.13.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01819c3df950696f32c91faf8d376ae6b695ffdba18f330f1cab6b8e314e4612"}, + {file = "optree-0.13.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:48c29d9c6c64c8dc48c8ee97f7c1d5cdb83e37320f0be0857c06ce4b97994aea"}, + {file = "optree-0.13.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:025d23400b8b579462a251420f0a9ae77d3d3593f84276f3465985731d79d722"}, + {file = "optree-0.13.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55e82426bef151149cfa41d68ac957730fcd420996c0db8324fca81aa6a810ba"}, + {file = "optree-0.13.1-cp313-cp313t-win32.whl", hash = "sha256:e40f018f522fcfd244688d1b3a360518e636ba7f636385aae0566eae3e7d29bc"}, + {file = "optree-0.13.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d580f1bf23bb352c4db6b3544f282f1ac08dcb0d9ab537d25e56220353438cf7"}, + {file = "optree-0.13.1-cp313-cp313t-win_arm64.whl", hash = "sha256:c4d13f55dbd509d27be3af54d53b4ca0751bc518244ced6d0567e518e51452a2"}, + {file = "optree-0.13.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9824a4258b058282eeaee1b388c8dfc704e49beda957b99177db8bd8249a3abe"}, + {file = "optree-0.13.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d21a8b449e47fdbf118ac1938cf6f97d8a60258bc45c6eba3e61f79feeb1ea8"}, + {file = "optree-0.13.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22ce30c9d733c2214fa321c8370e4dfc8c7829970364618b2b5cacffbc9e8949"}, + {file = "optree-0.13.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2521840d6aded4dac62c787f50bcb1cacbfcda86b9319d666b4025fa0ba5545a"}, + {file = "optree-0.13.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c99891c2ea6050738f7e3de5ab4038736cf33555a752b34a06922ebc9bf0488e"}, + {file = "optree-0.13.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1496f29d5b9633fed4b3f1fd4b7e772d77200eb2370c08ef8e14404309c669b9"}, + {file = "optree-0.13.1-cp37-cp37m-win32.whl", hash = "sha256:63b2749504fe0b9ac3892e26bf55a040ae2973bcf8da1476afe9266a4624be9d"}, + {file = "optree-0.13.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7e1c1da6574d59073b6a6b9a13633217f584ec271ddee4e014c7e422f171e9b4"}, + {file = "optree-0.13.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:50dd6a9c8ccef267ab4941f07eac53faf6a00666dce4d209da20525570ffaca3"}, + {file = "optree-0.13.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:536ecf0e555432cc939d958590e33e00e75cc254ab0dd269e84fc9de8352db61"}, + {file = "optree-0.13.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84a6a974aa9dc4119fe502865c8e1755090ac17dbb53a964619a8ece1130831e"}, + {file = "optree-0.13.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1891267f9dc76e9ddfed947ff7b755ad438ad483de0537a6b5bcf38478d5a33c"}, + {file = "optree-0.13.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de1ae16ea0410497e50fe2b4d48a83c37bfc87da76e1e82f9cc8c800b4fc8be6"}, + {file = "optree-0.13.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d89891e11a55ad83ab3e2810f8571774b2117a6198b4044fa44e0f37f72855e"}, + {file = "optree-0.13.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2063234ef4d58f11277e157d1cf066a8bd07be911da226bff84fc9761b8c1a25"}, + {file = "optree-0.13.1-cp38-cp38-win32.whl", hash = "sha256:5c950c85561c47efb3b1a3771ed1b2b2339bd5e28a0ca42bdcedadccc645eeac"}, + {file = "optree-0.13.1-cp38-cp38-win_amd64.whl", hash = "sha256:f2a9eadcab78ccc04114a6916e9decdbc886bbe04c1b7a7bb32e723209162998"}, + {file = "optree-0.13.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b94f9081cd810a59faae4dbac8f0447e59ce0fb2d70cfb388dc123c33a9fd1a8"}, + {file = "optree-0.13.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7abf1c6fe42cb112f0fb169f80d7b26476fa44226d2caf3727b49d210bdc3343"}, + {file = "optree-0.13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aee696272eece657c2b9e3cf079d8fc7cbbcc8a5c8199dbcd0960ddf7e672fe9"}, + {file = "optree-0.13.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5569b95e214d20a1b7acb7d9477fabbd709d334bc34f3257368ea1418b811a44"}, + {file = "optree-0.13.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:100d70cc57af5284649f881e6b266fee3a3e86e82024484eaa64ee18d1587e42"}, + {file = "optree-0.13.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:30b02951c48ecca6fbeb6a3cc7a858267c4d82d1c874481a639061e845168da5"}, + {file = "optree-0.13.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b291aed475ca5992a0c587ca4b72f074724209e01afca9d015c9a5b2089c68d"}, + {file = "optree-0.13.1-cp39-cp39-win32.whl", hash = "sha256:363939b255a9fa0e077d8297a8301857c859592fc581cee19ec9238e0c145c4a"}, + {file = "optree-0.13.1-cp39-cp39-win_amd64.whl", hash = "sha256:2cba7ca4cf991270a9fdd080b091d2cbdbcbf27858acebda6af40ff57312d1ea"}, + {file = "optree-0.13.1-cp39-cp39-win_arm64.whl", hash = "sha256:04252b5f24e5dae716647848b302f5f7849ecb028f8c617666d1b89a42eb988b"}, + {file = "optree-0.13.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:0f1bde49e41a158af28d99fae1bd425fbd664907c53cf595106fb5b35e5cbe26"}, + {file = "optree-0.13.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fafeda2e35e3270532132e27b471ea3e3aeac18f7966a4d0469137d1f36046ec"}, + {file = "optree-0.13.1-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce962f0dd387137817dcda600bd6cf2e1b65103411807b6cdbbd9ffddf1061f6"}, + {file = "optree-0.13.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f9707547635cfede8d79e4161c066021ffefc401d98bbf8eba452b1355a42c7"}, + {file = "optree-0.13.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5c6aed6c5eabda59a91376aca08ba508a06f1c68850216a98743b5f8f55af841"}, + {file = "optree-0.13.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:95298846c057cce2e7d114c03c645e86a5381b72388c8c390986bdefe69a759c"}, + {file = "optree-0.13.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37948e2d796db23d6ccd07105b709b827eba26549d34dd2149e95887c89fe9b4"}, + {file = "optree-0.13.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:395ac2eb69528613fd0f2ee8706890b7921b8ff3159df53b6e9f67eaf519c5cb"}, + {file = "optree-0.13.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:652287e43fcbb29b8d1821144987e3bc558be4e5eec0d42fce7007cc3ee8e574"}, + {file = "optree-0.13.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3d0161012d80e4865017e10298ac55652cc3ad9a3eae9440229d4bf00b140e01"}, + {file = "optree-0.13.1.tar.gz", hash = "sha256:af67856aa8073d237fe67313d84f8aeafac32c1cef7239c628a2768d02679c43"}, ] [package.dependencies] @@ -3060,13 +3032,13 @@ files = [ [[package]] name = "packaging" -version = "24.1" +version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, - {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, ] [[package]] @@ -3345,13 +3317,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "prometheus-client" -version = "0.21.0" +version = "0.21.1" description = "Python client for the Prometheus monitoring system." optional = false python-versions = ">=3.8" files = [ - {file = "prometheus_client-0.21.0-py3-none-any.whl", hash = "sha256:4fa6b4dd0ac16d58bb587c04b1caae65b8c5043e85f778f42f5f632f6af2e166"}, - {file = "prometheus_client-0.21.0.tar.gz", hash = "sha256:96c83c606b71ff2b0a433c98889d275f51ffec6c5e267de37c7a2b5c9aa9233e"}, + {file = "prometheus_client-0.21.1-py3-none-any.whl", hash = "sha256:594b45c410d6f4f8888940fe80b5cc2521b305a1fafe1c58609ef715a001f301"}, + {file = "prometheus_client-0.21.1.tar.gz", hash = "sha256:252505a722ac04b0456be05c05f75f45d760c2911ffc45f2a06bcaed9f3ae3fb"}, ] [package.extras] @@ -3388,22 +3360,22 @@ scipy = "*" [[package]] name = "protobuf" -version = "4.25.5" +version = "5.29.1" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "protobuf-4.25.5-cp310-abi3-win32.whl", hash = "sha256:5e61fd921603f58d2f5acb2806a929b4675f8874ff5f330b7d6f7e2e784bbcd8"}, - {file = "protobuf-4.25.5-cp310-abi3-win_amd64.whl", hash = "sha256:4be0571adcbe712b282a330c6e89eae24281344429ae95c6d85e79e84780f5ea"}, - {file = "protobuf-4.25.5-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:b2fde3d805354df675ea4c7c6338c1aecd254dfc9925e88c6d31a2bcb97eb173"}, - {file = "protobuf-4.25.5-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:919ad92d9b0310070f8356c24b855c98df2b8bd207ebc1c0c6fcc9ab1e007f3d"}, - {file = "protobuf-4.25.5-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:fe14e16c22be926d3abfcb500e60cab068baf10b542b8c858fa27e098123e331"}, - {file = "protobuf-4.25.5-cp38-cp38-win32.whl", hash = "sha256:98d8d8aa50de6a2747efd9cceba361c9034050ecce3e09136f90de37ddba66e1"}, - {file = "protobuf-4.25.5-cp38-cp38-win_amd64.whl", hash = "sha256:b0234dd5a03049e4ddd94b93400b67803c823cfc405689688f59b34e0742381a"}, - {file = "protobuf-4.25.5-cp39-cp39-win32.whl", hash = "sha256:abe32aad8561aa7cc94fc7ba4fdef646e576983edb94a73381b03c53728a626f"}, - {file = "protobuf-4.25.5-cp39-cp39-win_amd64.whl", hash = "sha256:7a183f592dc80aa7c8da7ad9e55091c4ffc9497b3054452d629bb85fa27c2a45"}, - {file = "protobuf-4.25.5-py3-none-any.whl", hash = "sha256:0aebecb809cae990f8129ada5ca273d9d670b76d9bfc9b1809f0a9c02b7dbf41"}, - {file = "protobuf-4.25.5.tar.gz", hash = "sha256:7f8249476b4a9473645db7f8ab42b02fe1488cbe5fb72fddd445e0665afd8584"}, + {file = "protobuf-5.29.1-cp310-abi3-win32.whl", hash = "sha256:22c1f539024241ee545cbcb00ee160ad1877975690b16656ff87dde107b5f110"}, + {file = "protobuf-5.29.1-cp310-abi3-win_amd64.whl", hash = "sha256:1fc55267f086dd4050d18ef839d7bd69300d0d08c2a53ca7df3920cc271a3c34"}, + {file = "protobuf-5.29.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:d473655e29c0c4bbf8b69e9a8fb54645bc289dead6d753b952e7aa660254ae18"}, + {file = "protobuf-5.29.1-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:b5ba1d0e4c8a40ae0496d0e2ecfdbb82e1776928a205106d14ad6985a09ec155"}, + {file = "protobuf-5.29.1-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:8ee1461b3af56145aca2800e6a3e2f928108c749ba8feccc6f5dd0062c410c0d"}, + {file = "protobuf-5.29.1-cp38-cp38-win32.whl", hash = "sha256:50879eb0eb1246e3a5eabbbe566b44b10348939b7cc1b267567e8c3d07213853"}, + {file = "protobuf-5.29.1-cp38-cp38-win_amd64.whl", hash = "sha256:027fbcc48cea65a6b17028510fdd054147057fa78f4772eb547b9274e5219331"}, + {file = "protobuf-5.29.1-cp39-cp39-win32.whl", hash = "sha256:5a41deccfa5e745cef5c65a560c76ec0ed8e70908a67cc8f4da5fce588b50d57"}, + {file = "protobuf-5.29.1-cp39-cp39-win_amd64.whl", hash = "sha256:012ce28d862ff417fd629285aca5d9772807f15ceb1a0dbd15b88f58c776c98c"}, + {file = "protobuf-5.29.1-py3-none-any.whl", hash = "sha256:32600ddb9c2a53dedc25b8581ea0f1fd8ea04956373c0c07577ce58d312522e0"}, + {file = "protobuf-5.29.1.tar.gz", hash = "sha256:683be02ca21a6ffe80db6dd02c0b5b2892322c59ca57fd6c872d652cb80549cb"}, ] [[package]] @@ -3463,52 +3435,55 @@ tests = ["pytest"] [[package]] name = "pyarrow" -version = "17.0.0" +version = "18.1.0" description = "Python library for Apache Arrow" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "pyarrow-17.0.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:a5c8b238d47e48812ee577ee20c9a2779e6a5904f1708ae240f53ecbee7c9f07"}, - {file = "pyarrow-17.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:db023dc4c6cae1015de9e198d41250688383c3f9af8f565370ab2b4cb5f62655"}, - {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da1e060b3876faa11cee287839f9cc7cdc00649f475714b8680a05fd9071d545"}, - {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75c06d4624c0ad6674364bb46ef38c3132768139ddec1c56582dbac54f2663e2"}, - {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:fa3c246cc58cb5a4a5cb407a18f193354ea47dd0648194e6265bd24177982fe8"}, - {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:f7ae2de664e0b158d1607699a16a488de3d008ba99b3a7aa5de1cbc13574d047"}, - {file = "pyarrow-17.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:5984f416552eea15fd9cee03da53542bf4cddaef5afecefb9aa8d1010c335087"}, - {file = "pyarrow-17.0.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:1c8856e2ef09eb87ecf937104aacfa0708f22dfeb039c363ec99735190ffb977"}, - {file = "pyarrow-17.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e19f569567efcbbd42084e87f948778eb371d308e137a0f97afe19bb860ccb3"}, - {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b244dc8e08a23b3e352899a006a26ae7b4d0da7bb636872fa8f5884e70acf15"}, - {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b72e87fe3e1db343995562f7fff8aee354b55ee83d13afba65400c178ab2597"}, - {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:dc5c31c37409dfbc5d014047817cb4ccd8c1ea25d19576acf1a001fe07f5b420"}, - {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:e3343cb1e88bc2ea605986d4b94948716edc7a8d14afd4e2c097232f729758b4"}, - {file = "pyarrow-17.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:a27532c38f3de9eb3e90ecab63dfda948a8ca859a66e3a47f5f42d1e403c4d03"}, - {file = "pyarrow-17.0.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:9b8a823cea605221e61f34859dcc03207e52e409ccf6354634143e23af7c8d22"}, - {file = "pyarrow-17.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f1e70de6cb5790a50b01d2b686d54aaf73da01266850b05e3af2a1bc89e16053"}, - {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0071ce35788c6f9077ff9ecba4858108eebe2ea5a3f7cf2cf55ebc1dbc6ee24a"}, - {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:757074882f844411fcca735e39aae74248a1531367a7c80799b4266390ae51cc"}, - {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:9ba11c4f16976e89146781a83833df7f82077cdab7dc6232c897789343f7891a"}, - {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b0c6ac301093b42d34410b187bba560b17c0330f64907bfa4f7f7f2444b0cf9b"}, - {file = "pyarrow-17.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:392bc9feabc647338e6c89267635e111d71edad5fcffba204425a7c8d13610d7"}, - {file = "pyarrow-17.0.0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:af5ff82a04b2171415f1410cff7ebb79861afc5dae50be73ce06d6e870615204"}, - {file = "pyarrow-17.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:edca18eaca89cd6382dfbcff3dd2d87633433043650c07375d095cd3517561d8"}, - {file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c7916bff914ac5d4a8fe25b7a25e432ff921e72f6f2b7547d1e325c1ad9d155"}, - {file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f553ca691b9e94b202ff741bdd40f6ccb70cdd5fbf65c187af132f1317de6145"}, - {file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:0cdb0e627c86c373205a2f94a510ac4376fdc523f8bb36beab2e7f204416163c"}, - {file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:d7d192305d9d8bc9082d10f361fc70a73590a4c65cf31c3e6926cd72b76bc35c"}, - {file = "pyarrow-17.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:02dae06ce212d8b3244dd3e7d12d9c4d3046945a5933d28026598e9dbbda1fca"}, - {file = "pyarrow-17.0.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:13d7a460b412f31e4c0efa1148e1d29bdf18ad1411eb6757d38f8fbdcc8645fb"}, - {file = "pyarrow-17.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9b564a51fbccfab5a04a80453e5ac6c9954a9c5ef2890d1bcf63741909c3f8df"}, - {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32503827abbc5aadedfa235f5ece8c4f8f8b0a3cf01066bc8d29de7539532687"}, - {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a155acc7f154b9ffcc85497509bcd0d43efb80d6f733b0dc3bb14e281f131c8b"}, - {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:dec8d129254d0188a49f8a1fc99e0560dc1b85f60af729f47de4046015f9b0a5"}, - {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:a48ddf5c3c6a6c505904545c25a4ae13646ae1f8ba703c4df4a1bfe4f4006bda"}, - {file = "pyarrow-17.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:42bf93249a083aca230ba7e2786c5f673507fa97bbd9725a1e2754715151a204"}, - {file = "pyarrow-17.0.0.tar.gz", hash = "sha256:4beca9521ed2c0921c1023e68d097d0299b62c362639ea315572a58f3f50fd28"}, + {file = "pyarrow-18.1.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:e21488d5cfd3d8b500b3238a6c4b075efabc18f0f6d80b29239737ebd69caa6c"}, + {file = "pyarrow-18.1.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:b516dad76f258a702f7ca0250885fc93d1fa5ac13ad51258e39d402bd9e2e1e4"}, + {file = "pyarrow-18.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f443122c8e31f4c9199cb23dca29ab9427cef990f283f80fe15b8e124bcc49b"}, + {file = "pyarrow-18.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0a03da7f2758645d17b7b4f83c8bffeae5bbb7f974523fe901f36288d2eab71"}, + {file = "pyarrow-18.1.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ba17845efe3aa358ec266cf9cc2800fa73038211fb27968bfa88acd09261a470"}, + {file = "pyarrow-18.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:3c35813c11a059056a22a3bef520461310f2f7eea5c8a11ef9de7062a23f8d56"}, + {file = "pyarrow-18.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:9736ba3c85129d72aefa21b4f3bd715bc4190fe4426715abfff90481e7d00812"}, + {file = "pyarrow-18.1.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:eaeabf638408de2772ce3d7793b2668d4bb93807deed1725413b70e3156a7854"}, + {file = "pyarrow-18.1.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:3b2e2239339c538f3464308fd345113f886ad031ef8266c6f004d49769bb074c"}, + {file = "pyarrow-18.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f39a2e0ed32a0970e4e46c262753417a60c43a3246972cfc2d3eb85aedd01b21"}, + {file = "pyarrow-18.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e31e9417ba9c42627574bdbfeada7217ad8a4cbbe45b9d6bdd4b62abbca4c6f6"}, + {file = "pyarrow-18.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:01c034b576ce0eef554f7c3d8c341714954be9b3f5d5bc7117006b85fcf302fe"}, + {file = "pyarrow-18.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:f266a2c0fc31995a06ebd30bcfdb7f615d7278035ec5b1cd71c48d56daaf30b0"}, + {file = "pyarrow-18.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:d4f13eee18433f99adefaeb7e01d83b59f73360c231d4782d9ddfaf1c3fbde0a"}, + {file = "pyarrow-18.1.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:9f3a76670b263dc41d0ae877f09124ab96ce10e4e48f3e3e4257273cee61ad0d"}, + {file = "pyarrow-18.1.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:da31fbca07c435be88a0c321402c4e31a2ba61593ec7473630769de8346b54ee"}, + {file = "pyarrow-18.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:543ad8459bc438efc46d29a759e1079436290bd583141384c6f7a1068ed6f992"}, + {file = "pyarrow-18.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0743e503c55be0fdb5c08e7d44853da27f19dc854531c0570f9f394ec9671d54"}, + {file = "pyarrow-18.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d4b3d2a34780645bed6414e22dda55a92e0fcd1b8a637fba86800ad737057e33"}, + {file = "pyarrow-18.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c52f81aa6f6575058d8e2c782bf79d4f9fdc89887f16825ec3a66607a5dd8e30"}, + {file = "pyarrow-18.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:0ad4892617e1a6c7a551cfc827e072a633eaff758fa09f21c4ee548c30bcaf99"}, + {file = "pyarrow-18.1.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:84e314d22231357d473eabec709d0ba285fa706a72377f9cc8e1cb3c8013813b"}, + {file = "pyarrow-18.1.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:f591704ac05dfd0477bb8f8e0bd4b5dc52c1cadf50503858dce3a15db6e46ff2"}, + {file = "pyarrow-18.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:acb7564204d3c40babf93a05624fc6a8ec1ab1def295c363afc40b0c9e66c191"}, + {file = "pyarrow-18.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:74de649d1d2ccb778f7c3afff6085bd5092aed4c23df9feeb45dd6b16f3811aa"}, + {file = "pyarrow-18.1.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:f96bd502cb11abb08efea6dab09c003305161cb6c9eafd432e35e76e7fa9b90c"}, + {file = "pyarrow-18.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:36ac22d7782554754a3b50201b607d553a8d71b78cdf03b33c1125be4b52397c"}, + {file = "pyarrow-18.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:25dbacab8c5952df0ca6ca0af28f50d45bd31c1ff6fcf79e2d120b4a65ee7181"}, + {file = "pyarrow-18.1.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6a276190309aba7bc9d5bd2933230458b3521a4317acfefe69a354f2fe59f2bc"}, + {file = "pyarrow-18.1.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:ad514dbfcffe30124ce655d72771ae070f30bf850b48bc4d9d3b25993ee0e386"}, + {file = "pyarrow-18.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aebc13a11ed3032d8dd6e7171eb6e86d40d67a5639d96c35142bd568b9299324"}, + {file = "pyarrow-18.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6cf5c05f3cee251d80e98726b5c7cc9f21bab9e9783673bac58e6dfab57ecc8"}, + {file = "pyarrow-18.1.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:11b676cd410cf162d3f6a70b43fb9e1e40affbc542a1e9ed3681895f2962d3d9"}, + {file = "pyarrow-18.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:b76130d835261b38f14fc41fdfb39ad8d672afb84c447126b84d5472244cfaba"}, + {file = "pyarrow-18.1.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:0b331e477e40f07238adc7ba7469c36b908f07c89b95dd4bd3a0ec84a3d1e21e"}, + {file = "pyarrow-18.1.0-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:2c4dd0c9010a25ba03e198fe743b1cc03cd33c08190afff371749c52ccbbaf76"}, + {file = "pyarrow-18.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f97b31b4c4e21ff58c6f330235ff893cc81e23da081b1a4b1c982075e0ed4e9"}, + {file = "pyarrow-18.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a4813cb8ecf1809871fd2d64a8eff740a1bd3691bbe55f01a3cf6c5ec869754"}, + {file = "pyarrow-18.1.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:05a5636ec3eb5cc2a36c6edb534a38ef57b2ab127292a716d00eabb887835f1e"}, + {file = "pyarrow-18.1.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:73eeed32e724ea3568bb06161cad5fa7751e45bc2228e33dcb10c614044165c7"}, + {file = "pyarrow-18.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:a1880dd6772b685e803011a6b43a230c23b566859a6e0c9a276c1e0faf4f4052"}, + {file = "pyarrow-18.1.0.tar.gz", hash = "sha256:9386d3ca9c145b5539a1cfc75df07757dff870168c959b473a0bccbc3abc8c73"}, ] -[package.dependencies] -numpy = ">=1.16.6" - [package.extras] test = ["cffi", "hypothesis", "pandas", "pytest", "pytz"] @@ -3578,13 +3553,13 @@ diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pytest" -version = "8.3.3" +version = "8.3.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, - {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, + {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, + {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, ] [package.dependencies] @@ -3922,13 +3897,13 @@ files = [ [[package]] name = "rich" -version = "13.9.3" +version = "13.9.4" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.8.0" files = [ - {file = "rich-13.9.3-py3-none-any.whl", hash = "sha256:9836f5096eb2172c9e77df411c1b009bace4193d6a481d534fea75ebba758283"}, - {file = "rich-13.9.3.tar.gz", hash = "sha256:bc1e01b899537598cf02579d2b9f4a415104d3fc439313a7a2c165d76557a08e"}, + {file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"}, + {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"}, ] [package.dependencies] @@ -3941,114 +3916,114 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "rpds-py" -version = "0.20.1" +version = "0.22.3" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "rpds_py-0.20.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a649dfd735fff086e8a9d0503a9f0c7d01b7912a333c7ae77e1515c08c146dad"}, - {file = "rpds_py-0.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f16bc1334853e91ddaaa1217045dd7be166170beec337576818461268a3de67f"}, - {file = "rpds_py-0.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14511a539afee6f9ab492b543060c7491c99924314977a55c98bfa2ee29ce78c"}, - {file = "rpds_py-0.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3ccb8ac2d3c71cda472b75af42818981bdacf48d2e21c36331b50b4f16930163"}, - {file = "rpds_py-0.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c142b88039b92e7e0cb2552e8967077e3179b22359e945574f5e2764c3953dcf"}, - {file = "rpds_py-0.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f19169781dddae7478a32301b499b2858bc52fc45a112955e798ee307e294977"}, - {file = "rpds_py-0.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13c56de6518e14b9bf6edde23c4c39dac5b48dcf04160ea7bce8fca8397cdf86"}, - {file = "rpds_py-0.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:925d176a549f4832c6f69fa6026071294ab5910e82a0fe6c6228fce17b0706bd"}, - {file = "rpds_py-0.20.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:78f0b6877bfce7a3d1ff150391354a410c55d3cdce386f862926a4958ad5ab7e"}, - {file = "rpds_py-0.20.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3dd645e2b0dcb0fd05bf58e2e54c13875847687d0b71941ad2e757e5d89d4356"}, - {file = "rpds_py-0.20.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4f676e21db2f8c72ff0936f895271e7a700aa1f8d31b40e4e43442ba94973899"}, - {file = "rpds_py-0.20.1-cp310-none-win32.whl", hash = "sha256:648386ddd1e19b4a6abab69139b002bc49ebf065b596119f8f37c38e9ecee8ff"}, - {file = "rpds_py-0.20.1-cp310-none-win_amd64.whl", hash = "sha256:d9ecb51120de61e4604650666d1f2b68444d46ae18fd492245a08f53ad2b7711"}, - {file = "rpds_py-0.20.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:762703bdd2b30983c1d9e62b4c88664df4a8a4d5ec0e9253b0231171f18f6d75"}, - {file = "rpds_py-0.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0b581f47257a9fce535c4567782a8976002d6b8afa2c39ff616edf87cbeff712"}, - {file = "rpds_py-0.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:842c19a6ce894493563c3bd00d81d5100e8e57d70209e84d5491940fdb8b9e3a"}, - {file = "rpds_py-0.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42cbde7789f5c0bcd6816cb29808e36c01b960fb5d29f11e052215aa85497c93"}, - {file = "rpds_py-0.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c8e9340ce5a52f95fa7d3b552b35c7e8f3874d74a03a8a69279fd5fca5dc751"}, - {file = "rpds_py-0.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ba6f89cac95c0900d932c9efb7f0fb6ca47f6687feec41abcb1bd5e2bd45535"}, - {file = "rpds_py-0.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a916087371afd9648e1962e67403c53f9c49ca47b9680adbeef79da3a7811b0"}, - {file = "rpds_py-0.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:200a23239781f46149e6a415f1e870c5ef1e712939fe8fa63035cd053ac2638e"}, - {file = "rpds_py-0.20.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:58b1d5dd591973d426cbb2da5e27ba0339209832b2f3315928c9790e13f159e8"}, - {file = "rpds_py-0.20.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6b73c67850ca7cae0f6c56f71e356d7e9fa25958d3e18a64927c2d930859b8e4"}, - {file = "rpds_py-0.20.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d8761c3c891cc51e90bc9926d6d2f59b27beaf86c74622c8979380a29cc23ac3"}, - {file = "rpds_py-0.20.1-cp311-none-win32.whl", hash = "sha256:cd945871335a639275eee904caef90041568ce3b42f402c6959b460d25ae8732"}, - {file = "rpds_py-0.20.1-cp311-none-win_amd64.whl", hash = "sha256:7e21b7031e17c6b0e445f42ccc77f79a97e2687023c5746bfb7a9e45e0921b84"}, - {file = "rpds_py-0.20.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:36785be22066966a27348444b40389f8444671630063edfb1a2eb04318721e17"}, - {file = "rpds_py-0.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:142c0a5124d9bd0e2976089484af5c74f47bd3298f2ed651ef54ea728d2ea42c"}, - {file = "rpds_py-0.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbddc10776ca7ebf2a299c41a4dde8ea0d8e3547bfd731cb87af2e8f5bf8962d"}, - {file = "rpds_py-0.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:15a842bb369e00295392e7ce192de9dcbf136954614124a667f9f9f17d6a216f"}, - {file = "rpds_py-0.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be5ef2f1fc586a7372bfc355986226484e06d1dc4f9402539872c8bb99e34b01"}, - {file = "rpds_py-0.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbcf360c9e3399b056a238523146ea77eeb2a596ce263b8814c900263e46031a"}, - {file = "rpds_py-0.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecd27a66740ffd621d20b9a2f2b5ee4129a56e27bfb9458a3bcc2e45794c96cb"}, - {file = "rpds_py-0.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0b937b2a1988f184a3e9e577adaa8aede21ec0b38320d6009e02bd026db04fa"}, - {file = "rpds_py-0.20.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6889469bfdc1eddf489729b471303739bf04555bb151fe8875931f8564309afc"}, - {file = "rpds_py-0.20.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:19b73643c802f4eaf13d97f7855d0fb527fbc92ab7013c4ad0e13a6ae0ed23bd"}, - {file = "rpds_py-0.20.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3c6afcf2338e7f374e8edc765c79fbcb4061d02b15dd5f8f314a4af2bdc7feb5"}, - {file = "rpds_py-0.20.1-cp312-none-win32.whl", hash = "sha256:dc73505153798c6f74854aba69cc75953888cf9866465196889c7cdd351e720c"}, - {file = "rpds_py-0.20.1-cp312-none-win_amd64.whl", hash = "sha256:8bbe951244a838a51289ee53a6bae3a07f26d4e179b96fc7ddd3301caf0518eb"}, - {file = "rpds_py-0.20.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:6ca91093a4a8da4afae7fe6a222c3b53ee4eef433ebfee4d54978a103435159e"}, - {file = "rpds_py-0.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b9c2fe36d1f758b28121bef29ed1dee9b7a2453e997528e7d1ac99b94892527c"}, - {file = "rpds_py-0.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f009c69bc8c53db5dfab72ac760895dc1f2bc1b62ab7408b253c8d1ec52459fc"}, - {file = "rpds_py-0.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6740a3e8d43a32629bb9b009017ea5b9e713b7210ba48ac8d4cb6d99d86c8ee8"}, - {file = "rpds_py-0.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:32b922e13d4c0080d03e7b62991ad7f5007d9cd74e239c4b16bc85ae8b70252d"}, - {file = "rpds_py-0.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe00a9057d100e69b4ae4a094203a708d65b0f345ed546fdef86498bf5390982"}, - {file = "rpds_py-0.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49fe9b04b6fa685bd39237d45fad89ba19e9163a1ccaa16611a812e682913496"}, - {file = "rpds_py-0.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aa7ac11e294304e615b43f8c441fee5d40094275ed7311f3420d805fde9b07b4"}, - {file = "rpds_py-0.20.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aa97af1558a9bef4025f8f5d8c60d712e0a3b13a2fe875511defc6ee77a1ab7"}, - {file = "rpds_py-0.20.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:483b29f6f7ffa6af845107d4efe2e3fa8fb2693de8657bc1849f674296ff6a5a"}, - {file = "rpds_py-0.20.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:37fe0f12aebb6a0e3e17bb4cd356b1286d2d18d2e93b2d39fe647138458b4bcb"}, - {file = "rpds_py-0.20.1-cp313-none-win32.whl", hash = "sha256:a624cc00ef2158e04188df5e3016385b9353638139a06fb77057b3498f794782"}, - {file = "rpds_py-0.20.1-cp313-none-win_amd64.whl", hash = "sha256:b71b8666eeea69d6363248822078c075bac6ed135faa9216aa85f295ff009b1e"}, - {file = "rpds_py-0.20.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:5b48e790e0355865197ad0aca8cde3d8ede347831e1959e158369eb3493d2191"}, - {file = "rpds_py-0.20.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3e310838a5801795207c66c73ea903deda321e6146d6f282e85fa7e3e4854804"}, - {file = "rpds_py-0.20.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249280b870e6a42c0d972339e9cc22ee98730a99cd7f2f727549af80dd5a963"}, - {file = "rpds_py-0.20.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e79059d67bea28b53d255c1437b25391653263f0e69cd7dec170d778fdbca95e"}, - {file = "rpds_py-0.20.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b431c777c9653e569986ecf69ff4a5dba281cded16043d348bf9ba505486f36"}, - {file = "rpds_py-0.20.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da584ff96ec95e97925174eb8237e32f626e7a1a97888cdd27ee2f1f24dd0ad8"}, - {file = "rpds_py-0.20.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02a0629ec053fc013808a85178524e3cb63a61dbc35b22499870194a63578fb9"}, - {file = "rpds_py-0.20.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fbf15aff64a163db29a91ed0868af181d6f68ec1a3a7d5afcfe4501252840bad"}, - {file = "rpds_py-0.20.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:07924c1b938798797d60c6308fa8ad3b3f0201802f82e4a2c41bb3fafb44cc28"}, - {file = "rpds_py-0.20.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:4a5a844f68776a7715ecb30843b453f07ac89bad393431efbf7accca3ef599c1"}, - {file = "rpds_py-0.20.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:518d2ca43c358929bf08f9079b617f1c2ca6e8848f83c1225c88caeac46e6cbc"}, - {file = "rpds_py-0.20.1-cp38-none-win32.whl", hash = "sha256:3aea7eed3e55119635a74bbeb80b35e776bafccb70d97e8ff838816c124539f1"}, - {file = "rpds_py-0.20.1-cp38-none-win_amd64.whl", hash = "sha256:7dca7081e9a0c3b6490a145593f6fe3173a94197f2cb9891183ef75e9d64c425"}, - {file = "rpds_py-0.20.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b41b6321805c472f66990c2849e152aff7bc359eb92f781e3f606609eac877ad"}, - {file = "rpds_py-0.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a90c373ea2975519b58dece25853dbcb9779b05cc46b4819cb1917e3b3215b6"}, - {file = "rpds_py-0.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16d4477bcb9fbbd7b5b0e4a5d9b493e42026c0bf1f06f723a9353f5153e75d30"}, - {file = "rpds_py-0.20.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:84b8382a90539910b53a6307f7c35697bc7e6ffb25d9c1d4e998a13e842a5e83"}, - {file = "rpds_py-0.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4888e117dd41b9d34194d9e31631af70d3d526efc363085e3089ab1a62c32ed1"}, - {file = "rpds_py-0.20.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5265505b3d61a0f56618c9b941dc54dc334dc6e660f1592d112cd103d914a6db"}, - {file = "rpds_py-0.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e75ba609dba23f2c95b776efb9dd3f0b78a76a151e96f96cc5b6b1b0004de66f"}, - {file = "rpds_py-0.20.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1791ff70bc975b098fe6ecf04356a10e9e2bd7dc21fa7351c1742fdeb9b4966f"}, - {file = "rpds_py-0.20.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d126b52e4a473d40232ec2052a8b232270ed1f8c9571aaf33f73a14cc298c24f"}, - {file = "rpds_py-0.20.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:c14937af98c4cc362a1d4374806204dd51b1e12dded1ae30645c298e5a5c4cb1"}, - {file = "rpds_py-0.20.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3d089d0b88996df627693639d123c8158cff41c0651f646cd8fd292c7da90eaf"}, - {file = "rpds_py-0.20.1-cp39-none-win32.whl", hash = "sha256:653647b8838cf83b2e7e6a0364f49af96deec64d2a6578324db58380cff82aca"}, - {file = "rpds_py-0.20.1-cp39-none-win_amd64.whl", hash = "sha256:fa41a64ac5b08b292906e248549ab48b69c5428f3987b09689ab2441f267d04d"}, - {file = "rpds_py-0.20.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7a07ced2b22f0cf0b55a6a510078174c31b6d8544f3bc00c2bcee52b3d613f74"}, - {file = "rpds_py-0.20.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:68cb0a499f2c4a088fd2f521453e22ed3527154136a855c62e148b7883b99f9a"}, - {file = "rpds_py-0.20.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa3060d885657abc549b2a0f8e1b79699290e5d83845141717c6c90c2df38311"}, - {file = "rpds_py-0.20.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:95f3b65d2392e1c5cec27cff08fdc0080270d5a1a4b2ea1d51d5f4a2620ff08d"}, - {file = "rpds_py-0.20.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2cc3712a4b0b76a1d45a9302dd2f53ff339614b1c29603a911318f2357b04dd2"}, - {file = "rpds_py-0.20.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d4eea0761e37485c9b81400437adb11c40e13ef513375bbd6973e34100aeb06"}, - {file = "rpds_py-0.20.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f5179583d7a6cdb981151dd349786cbc318bab54963a192692d945dd3f6435d"}, - {file = "rpds_py-0.20.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2fbb0ffc754490aff6dabbf28064be47f0f9ca0b9755976f945214965b3ace7e"}, - {file = "rpds_py-0.20.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:a94e52537a0e0a85429eda9e49f272ada715506d3b2431f64b8a3e34eb5f3e75"}, - {file = "rpds_py-0.20.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:92b68b79c0da2a980b1c4197e56ac3dd0c8a149b4603747c4378914a68706979"}, - {file = "rpds_py-0.20.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:93da1d3db08a827eda74356f9f58884adb254e59b6664f64cc04cdff2cc19b0d"}, - {file = "rpds_py-0.20.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:754bbed1a4ca48479e9d4182a561d001bbf81543876cdded6f695ec3d465846b"}, - {file = "rpds_py-0.20.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ca449520e7484534a2a44faf629362cae62b660601432d04c482283c47eaebab"}, - {file = "rpds_py-0.20.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:9c4cb04a16b0f199a8c9bf807269b2f63b7b5b11425e4a6bd44bd6961d28282c"}, - {file = "rpds_py-0.20.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb63804105143c7e24cee7db89e37cb3f3941f8e80c4379a0b355c52a52b6780"}, - {file = "rpds_py-0.20.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:55cd1fa4ecfa6d9f14fbd97ac24803e6f73e897c738f771a9fe038f2f11ff07c"}, - {file = "rpds_py-0.20.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f8f741b6292c86059ed175d80eefa80997125b7c478fb8769fd9ac8943a16c0"}, - {file = "rpds_py-0.20.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fc212779bf8411667234b3cdd34d53de6c2b8b8b958e1e12cb473a5f367c338"}, - {file = "rpds_py-0.20.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ad56edabcdb428c2e33bbf24f255fe2b43253b7d13a2cdbf05de955217313e6"}, - {file = "rpds_py-0.20.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0a3a1e9ee9728b2c1734f65d6a1d376c6f2f6fdcc13bb007a08cc4b1ff576dc5"}, - {file = "rpds_py-0.20.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:e13de156137b7095442b288e72f33503a469aa1980ed856b43c353ac86390519"}, - {file = "rpds_py-0.20.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:07f59760ef99f31422c49038964b31c4dfcfeb5d2384ebfc71058a7c9adae2d2"}, - {file = "rpds_py-0.20.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:59240685e7da61fb78f65a9f07f8108e36a83317c53f7b276b4175dc44151684"}, - {file = "rpds_py-0.20.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:83cba698cfb3c2c5a7c3c6bac12fe6c6a51aae69513726be6411076185a8b24a"}, - {file = "rpds_py-0.20.1.tar.gz", hash = "sha256:e1791c4aabd117653530dccd24108fa03cc6baf21f58b950d0a73c3b3b29a350"}, + {file = "rpds_py-0.22.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:6c7b99ca52c2c1752b544e310101b98a659b720b21db00e65edca34483259967"}, + {file = "rpds_py-0.22.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:be2eb3f2495ba669d2a985f9b426c1797b7d48d6963899276d22f23e33d47e37"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70eb60b3ae9245ddea20f8a4190bd79c705a22f8028aaf8bbdebe4716c3fab24"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4041711832360a9b75cfb11b25a6a97c8fb49c07b8bd43d0d02b45d0b499a4ff"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64607d4cbf1b7e3c3c8a14948b99345eda0e161b852e122c6bb71aab6d1d798c"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e69b0a0e2537f26d73b4e43ad7bc8c8efb39621639b4434b76a3de50c6966e"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc27863442d388870c1809a87507727b799c8460573cfbb6dc0eeaef5a11b5ec"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e79dd39f1e8c3504be0607e5fc6e86bb60fe3584bec8b782578c3b0fde8d932c"}, + {file = "rpds_py-0.22.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e0fa2d4ec53dc51cf7d3bb22e0aa0143966119f42a0c3e4998293a3dd2856b09"}, + {file = "rpds_py-0.22.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fda7cb070f442bf80b642cd56483b5548e43d366fe3f39b98e67cce780cded00"}, + {file = "rpds_py-0.22.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cff63a0272fcd259dcc3be1657b07c929c466b067ceb1c20060e8d10af56f5bf"}, + {file = "rpds_py-0.22.3-cp310-cp310-win32.whl", hash = "sha256:9bd7228827ec7bb817089e2eb301d907c0d9827a9e558f22f762bb690b131652"}, + {file = "rpds_py-0.22.3-cp310-cp310-win_amd64.whl", hash = "sha256:9beeb01d8c190d7581a4d59522cd3d4b6887040dcfc744af99aa59fef3e041a8"}, + {file = "rpds_py-0.22.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d20cfb4e099748ea39e6f7b16c91ab057989712d31761d3300d43134e26e165f"}, + {file = "rpds_py-0.22.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:68049202f67380ff9aa52f12e92b1c30115f32e6895cd7198fa2a7961621fc5a"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb4f868f712b2dd4bcc538b0a0c1f63a2b1d584c925e69a224d759e7070a12d5"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bc51abd01f08117283c5ebf64844a35144a0843ff7b2983e0648e4d3d9f10dbb"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f3cec041684de9a4684b1572fe28c7267410e02450f4561700ca5a3bc6695a2"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7ef9d9da710be50ff6809fed8f1963fecdfecc8b86656cadfca3bc24289414b0"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59f4a79c19232a5774aee369a0c296712ad0e77f24e62cad53160312b1c1eaa1"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a60bce91f81ddaac922a40bbb571a12c1070cb20ebd6d49c48e0b101d87300d"}, + {file = "rpds_py-0.22.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e89391e6d60251560f0a8f4bd32137b077a80d9b7dbe6d5cab1cd80d2746f648"}, + {file = "rpds_py-0.22.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e3fb866d9932a3d7d0c82da76d816996d1667c44891bd861a0f97ba27e84fc74"}, + {file = "rpds_py-0.22.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1352ae4f7c717ae8cba93421a63373e582d19d55d2ee2cbb184344c82d2ae55a"}, + {file = "rpds_py-0.22.3-cp311-cp311-win32.whl", hash = "sha256:b0b4136a252cadfa1adb705bb81524eee47d9f6aab4f2ee4fa1e9d3cd4581f64"}, + {file = "rpds_py-0.22.3-cp311-cp311-win_amd64.whl", hash = "sha256:8bd7c8cfc0b8247c8799080fbff54e0b9619e17cdfeb0478ba7295d43f635d7c"}, + {file = "rpds_py-0.22.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:27e98004595899949bd7a7b34e91fa7c44d7a97c40fcaf1d874168bb652ec67e"}, + {file = "rpds_py-0.22.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1978d0021e943aae58b9b0b196fb4895a25cc53d3956b8e35e0b7682eefb6d56"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:655ca44a831ecb238d124e0402d98f6212ac527a0ba6c55ca26f616604e60a45"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:feea821ee2a9273771bae61194004ee2fc33f8ec7db08117ef9147d4bbcbca8e"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22bebe05a9ffc70ebfa127efbc429bc26ec9e9b4ee4d15a740033efda515cf3d"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3af6e48651c4e0d2d166dc1b033b7042ea3f871504b6805ba5f4fe31581d8d38"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67ba3c290821343c192f7eae1d8fd5999ca2dc99994114643e2f2d3e6138b15"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:02fbb9c288ae08bcb34fb41d516d5eeb0455ac35b5512d03181d755d80810059"}, + {file = "rpds_py-0.22.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f56a6b404f74ab372da986d240e2e002769a7d7102cc73eb238a4f72eec5284e"}, + {file = "rpds_py-0.22.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0a0461200769ab3b9ab7e513f6013b7a97fdeee41c29b9db343f3c5a8e2b9e61"}, + {file = "rpds_py-0.22.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8633e471c6207a039eff6aa116e35f69f3156b3989ea3e2d755f7bc41754a4a7"}, + {file = "rpds_py-0.22.3-cp312-cp312-win32.whl", hash = "sha256:593eba61ba0c3baae5bc9be2f5232430453fb4432048de28399ca7376de9c627"}, + {file = "rpds_py-0.22.3-cp312-cp312-win_amd64.whl", hash = "sha256:d115bffdd417c6d806ea9069237a4ae02f513b778e3789a359bc5856e0404cc4"}, + {file = "rpds_py-0.22.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ea7433ce7e4bfc3a85654aeb6747babe3f66eaf9a1d0c1e7a4435bbdf27fea84"}, + {file = "rpds_py-0.22.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6dd9412824c4ce1aca56c47b0991e65bebb7ac3f4edccfd3f156150c96a7bf25"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20070c65396f7373f5df4005862fa162db5d25d56150bddd0b3e8214e8ef45b4"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0b09865a9abc0ddff4e50b5ef65467cd94176bf1e0004184eb915cbc10fc05c5"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3453e8d41fe5f17d1f8e9c383a7473cd46a63661628ec58e07777c2fff7196dc"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5d36399a1b96e1a5fdc91e0522544580dbebeb1f77f27b2b0ab25559e103b8b"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009de23c9c9ee54bf11303a966edf4d9087cd43a6003672e6aa7def643d06518"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1aef18820ef3e4587ebe8b3bc9ba6e55892a6d7b93bac6d29d9f631a3b4befbd"}, + {file = "rpds_py-0.22.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f60bd8423be1d9d833f230fdbccf8f57af322d96bcad6599e5a771b151398eb2"}, + {file = "rpds_py-0.22.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:62d9cfcf4948683a18a9aff0ab7e1474d407b7bab2ca03116109f8464698ab16"}, + {file = "rpds_py-0.22.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9253fc214112405f0afa7db88739294295f0e08466987f1d70e29930262b4c8f"}, + {file = "rpds_py-0.22.3-cp313-cp313-win32.whl", hash = "sha256:fb0ba113b4983beac1a2eb16faffd76cb41e176bf58c4afe3e14b9c681f702de"}, + {file = "rpds_py-0.22.3-cp313-cp313-win_amd64.whl", hash = "sha256:c58e2339def52ef6b71b8f36d13c3688ea23fa093353f3a4fee2556e62086ec9"}, + {file = "rpds_py-0.22.3-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:f82a116a1d03628a8ace4859556fb39fd1424c933341a08ea3ed6de1edb0283b"}, + {file = "rpds_py-0.22.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3dfcbc95bd7992b16f3f7ba05af8a64ca694331bd24f9157b49dadeeb287493b"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59259dc58e57b10e7e18ce02c311804c10c5a793e6568f8af4dead03264584d1"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5725dd9cc02068996d4438d397e255dcb1df776b7ceea3b9cb972bdb11260a83"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99b37292234e61325e7a5bb9689e55e48c3f5f603af88b1642666277a81f1fbd"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:27b1d3b3915a99208fee9ab092b8184c420f2905b7d7feb4aeb5e4a9c509b8a1"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f612463ac081803f243ff13cccc648578e2279295048f2a8d5eb430af2bae6e3"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f73d3fef726b3243a811121de45193c0ca75f6407fe66f3f4e183c983573e130"}, + {file = "rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3f21f0495edea7fdbaaa87e633a8689cd285f8f4af5c869f27bc8074638ad69c"}, + {file = "rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:1e9663daaf7a63ceccbbb8e3808fe90415b0757e2abddbfc2e06c857bf8c5e2b"}, + {file = "rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a76e42402542b1fae59798fab64432b2d015ab9d0c8c47ba7addddbaf7952333"}, + {file = "rpds_py-0.22.3-cp313-cp313t-win32.whl", hash = "sha256:69803198097467ee7282750acb507fba35ca22cc3b85f16cf45fb01cb9097730"}, + {file = "rpds_py-0.22.3-cp313-cp313t-win_amd64.whl", hash = "sha256:f5cf2a0c2bdadf3791b5c205d55a37a54025c6e18a71c71f82bb536cf9a454bf"}, + {file = "rpds_py-0.22.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:378753b4a4de2a7b34063d6f95ae81bfa7b15f2c1a04a9518e8644e81807ebea"}, + {file = "rpds_py-0.22.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3445e07bf2e8ecfeef6ef67ac83de670358abf2996916039b16a218e3d95e97e"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b2513ba235829860b13faa931f3b6846548021846ac808455301c23a101689d"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eaf16ae9ae519a0e237a0f528fd9f0197b9bb70f40263ee57ae53c2b8d48aeb3"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:583f6a1993ca3369e0f80ba99d796d8e6b1a3a2a442dd4e1a79e652116413091"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4617e1915a539a0d9a9567795023de41a87106522ff83fbfaf1f6baf8e85437e"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c150c7a61ed4a4f4955a96626574e9baf1adf772c2fb61ef6a5027e52803543"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2fa4331c200c2521512595253f5bb70858b90f750d39b8cbfd67465f8d1b596d"}, + {file = "rpds_py-0.22.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:214b7a953d73b5e87f0ebece4a32a5bd83c60a3ecc9d4ec8f1dca968a2d91e99"}, + {file = "rpds_py-0.22.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f47ad3d5f3258bd7058d2d506852217865afefe6153a36eb4b6928758041d831"}, + {file = "rpds_py-0.22.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f276b245347e6e36526cbd4a266a417796fc531ddf391e43574cf6466c492520"}, + {file = "rpds_py-0.22.3-cp39-cp39-win32.whl", hash = "sha256:bbb232860e3d03d544bc03ac57855cd82ddf19c7a07651a7c0fdb95e9efea8b9"}, + {file = "rpds_py-0.22.3-cp39-cp39-win_amd64.whl", hash = "sha256:cfbc454a2880389dbb9b5b398e50d439e2e58669160f27b60e5eca11f68ae17c"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d48424e39c2611ee1b84ad0f44fb3b2b53d473e65de061e3f460fc0be5f1939d"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:24e8abb5878e250f2eb0d7859a8e561846f98910326d06c0d51381fed59357bd"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b232061ca880db21fa14defe219840ad9b74b6158adb52ddf0e87bead9e8493"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac0a03221cdb5058ce0167ecc92a8c89e8d0decdc9e99a2ec23380793c4dcb96"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb0c341fa71df5a4595f9501df4ac5abfb5a09580081dffbd1ddd4654e6e9123"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf9db5488121b596dbfc6718c76092fda77b703c1f7533a226a5a9f65248f8ad"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b8db6b5b2d4491ad5b6bdc2bc7c017eec108acbf4e6785f42a9eb0ba234f4c9"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b3d504047aba448d70cf6fa22e06cb09f7cbd761939fdd47604f5e007675c24e"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:e61b02c3f7a1e0b75e20c3978f7135fd13cb6cf551bf4a6d29b999a88830a338"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:e35ba67d65d49080e8e5a1dd40101fccdd9798adb9b050ff670b7d74fa41c566"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:26fd7cac7dd51011a245f29a2cc6489c4608b5a8ce8d75661bb4a1066c52dfbe"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:177c7c0fce2855833819c98e43c262007f42ce86651ffbb84f37883308cb0e7d"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bb47271f60660803ad11f4c61b42242b8c1312a31c98c578f79ef9387bbde21c"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:70fb28128acbfd264eda9bf47015537ba3fe86e40d046eb2963d75024be4d055"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44d61b4b7d0c2c9ac019c314e52d7cbda0ae31078aabd0f22e583af3e0d79723"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f0e260eaf54380380ac3808aa4ebe2d8ca28b9087cf411649f96bad6900c728"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b25bc607423935079e05619d7de556c91fb6adeae9d5f80868dde3468657994b"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fb6116dfb8d1925cbdb52595560584db42a7f664617a1f7d7f6e32f138cdf37d"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a63cbdd98acef6570c62b92a1e43266f9e8b21e699c363c0fef13bd530799c11"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2b8f60e1b739a74bab7e01fcbe3dddd4657ec685caa04681df9d562ef15b625f"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:2e8b55d8517a2fda8d95cb45d62a5a8bbf9dd0ad39c5b25c8833efea07b880ca"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:2de29005e11637e7a2361fa151f780ff8eb2543a0da1413bb951e9f14b699ef3"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:666ecce376999bf619756a24ce15bb14c5bfaf04bf00abc7e663ce17c3f34fe7"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:5246b14ca64a8675e0a7161f7af68fe3e910e6b90542b4bfb5439ba752191df6"}, + {file = "rpds_py-0.22.3.tar.gz", hash = "sha256:e32fee8ab45d3c2db6da19a5323bc3362237c8b653c70194414b892fd06a080d"}, ] [[package]] @@ -4067,37 +4042,41 @@ pyasn1 = ">=0.1.3" [[package]] name = "scikit-learn" -version = "1.5.2" +version = "1.6.0" description = "A set of python modules for machine learning and data mining" optional = false python-versions = ">=3.9" files = [ - {file = "scikit_learn-1.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:299406827fb9a4f862626d0fe6c122f5f87f8910b86fe5daa4c32dcd742139b6"}, - {file = "scikit_learn-1.5.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:2d4cad1119c77930b235579ad0dc25e65c917e756fe80cab96aa3b9428bd3fb0"}, - {file = "scikit_learn-1.5.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c412ccc2ad9bf3755915e3908e677b367ebc8d010acbb3f182814524f2e5540"}, - {file = "scikit_learn-1.5.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a686885a4b3818d9e62904d91b57fa757fc2bed3e465c8b177be652f4dd37c8"}, - {file = "scikit_learn-1.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:c15b1ca23d7c5f33cc2cb0a0d6aaacf893792271cddff0edbd6a40e8319bc113"}, - {file = "scikit_learn-1.5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:03b6158efa3faaf1feea3faa884c840ebd61b6484167c711548fce208ea09445"}, - {file = "scikit_learn-1.5.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:1ff45e26928d3b4eb767a8f14a9a6efbf1cbff7c05d1fb0f95f211a89fd4f5de"}, - {file = "scikit_learn-1.5.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f763897fe92d0e903aa4847b0aec0e68cadfff77e8a0687cabd946c89d17e675"}, - {file = "scikit_learn-1.5.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8b0ccd4a902836493e026c03256e8b206656f91fbcc4fde28c57a5b752561f1"}, - {file = "scikit_learn-1.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:6c16d84a0d45e4894832b3c4d0bf73050939e21b99b01b6fd59cbb0cf39163b6"}, - {file = "scikit_learn-1.5.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f932a02c3f4956dfb981391ab24bda1dbd90fe3d628e4b42caef3e041c67707a"}, - {file = "scikit_learn-1.5.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:3b923d119d65b7bd555c73be5423bf06c0105678ce7e1f558cb4b40b0a5502b1"}, - {file = "scikit_learn-1.5.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f60021ec1574e56632be2a36b946f8143bf4e5e6af4a06d85281adc22938e0dd"}, - {file = "scikit_learn-1.5.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:394397841449853c2290a32050382edaec3da89e35b3e03d6cc966aebc6a8ae6"}, - {file = "scikit_learn-1.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:57cc1786cfd6bd118220a92ede80270132aa353647684efa385a74244a41e3b1"}, - {file = "scikit_learn-1.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9a702e2de732bbb20d3bad29ebd77fc05a6b427dc49964300340e4c9328b3f5"}, - {file = "scikit_learn-1.5.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:b0768ad641981f5d3a198430a1d31c3e044ed2e8a6f22166b4d546a5116d7908"}, - {file = "scikit_learn-1.5.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:178ddd0a5cb0044464fc1bfc4cca5b1833bfc7bb022d70b05db8530da4bb3dd3"}, - {file = "scikit_learn-1.5.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7284ade780084d94505632241bf78c44ab3b6f1e8ccab3d2af58e0e950f9c12"}, - {file = "scikit_learn-1.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:b7b0f9a0b1040830d38c39b91b3a44e1b643f4b36e36567b80b7c6bd2202a27f"}, - {file = "scikit_learn-1.5.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:757c7d514ddb00ae249832fe87100d9c73c6ea91423802872d9e74970a0e40b9"}, - {file = "scikit_learn-1.5.2-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:52788f48b5d8bca5c0736c175fa6bdaab2ef00a8f536cda698db61bd89c551c1"}, - {file = "scikit_learn-1.5.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:643964678f4b5fbdc95cbf8aec638acc7aa70f5f79ee2cdad1eec3df4ba6ead8"}, - {file = "scikit_learn-1.5.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca64b3089a6d9b9363cd3546f8978229dcbb737aceb2c12144ee3f70f95684b7"}, - {file = "scikit_learn-1.5.2-cp39-cp39-win_amd64.whl", hash = "sha256:3bed4909ba187aca80580fe2ef370d9180dcf18e621a27c4cf2ef10d279a7efe"}, - {file = "scikit_learn-1.5.2.tar.gz", hash = "sha256:b4237ed7b3fdd0a4882792e68ef2545d5baa50aca3bb45aa7df468138ad8f94d"}, + {file = "scikit_learn-1.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:366fb3fa47dce90afed3d6106183f4978d6f24cfd595c2373424171b915ee718"}, + {file = "scikit_learn-1.6.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:59cd96a8d9f8dfd546f5d6e9787e1b989e981388d7803abbc9efdcde61e47460"}, + {file = "scikit_learn-1.6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efa7a579606c73a0b3d210e33ea410ea9e1af7933fe324cb7e6fbafae4ea5948"}, + {file = "scikit_learn-1.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a46d3ca0f11a540b8eaddaf5e38172d8cd65a86cb3e3632161ec96c0cffb774c"}, + {file = "scikit_learn-1.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:5be4577769c5dde6e1b53de8e6520f9b664ab5861dd57acee47ad119fd7405d6"}, + {file = "scikit_learn-1.6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1f50b4f24cf12a81c3c09958ae3b864d7534934ca66ded3822de4996d25d7285"}, + {file = "scikit_learn-1.6.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:eb9ae21f387826da14b0b9cb1034f5048ddb9182da429c689f5f4a87dc96930b"}, + {file = "scikit_learn-1.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0baa91eeb8c32632628874a5c91885eaedd23b71504d24227925080da075837a"}, + {file = "scikit_learn-1.6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c716d13ba0a2f8762d96ff78d3e0cde90bc9c9b5c13d6ab6bb9b2d6ca6705fd"}, + {file = "scikit_learn-1.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:9aafd94bafc841b626681e626be27bf1233d5a0f20f0a6fdb4bee1a1963c6643"}, + {file = "scikit_learn-1.6.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:04a5ba45c12a5ff81518aa4f1604e826a45d20e53da47b15871526cda4ff5174"}, + {file = "scikit_learn-1.6.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:21fadfc2ad7a1ce8bd1d90f23d17875b84ec765eecbbfc924ff11fb73db582ce"}, + {file = "scikit_learn-1.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30f34bb5fde90e020653bb84dcb38b6c83f90c70680dbd8c38bd9becbad7a127"}, + {file = "scikit_learn-1.6.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1dad624cffe3062276a0881d4e441bc9e3b19d02d17757cd6ae79a9d192a0027"}, + {file = "scikit_learn-1.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:2fce7950a3fad85e0a61dc403df0f9345b53432ac0e47c50da210d22c60b6d85"}, + {file = "scikit_learn-1.6.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e5453b2e87ef8accedc5a8a4e6709f887ca01896cd7cc8a174fe39bd4bb00aef"}, + {file = "scikit_learn-1.6.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:5fe11794236fb83bead2af26a87ced5d26e3370b8487430818b915dafab1724e"}, + {file = "scikit_learn-1.6.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61fe3dcec0d82ae280877a818ab652f4988371e32dd5451e75251bece79668b1"}, + {file = "scikit_learn-1.6.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b44e3a51e181933bdf9a4953cc69c6025b40d2b49e238233f149b98849beb4bf"}, + {file = "scikit_learn-1.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:a17860a562bac54384454d40b3f6155200c1c737c9399e6a97962c63fce503ac"}, + {file = "scikit_learn-1.6.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:98717d3c152f6842d36a70f21e1468fb2f1a2f8f2624d9a3f382211798516426"}, + {file = "scikit_learn-1.6.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:34e20bfac8ff0ebe0ff20fb16a4d6df5dc4cc9ce383e00c2ab67a526a3c67b18"}, + {file = "scikit_learn-1.6.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eba06d75815406091419e06dd650b91ebd1c5f836392a0d833ff36447c2b1bfa"}, + {file = "scikit_learn-1.6.0-cp313-cp313t-win_amd64.whl", hash = "sha256:b6916d1cec1ff163c7d281e699d7a6a709da2f2c5ec7b10547e08cc788ddd3ae"}, + {file = "scikit_learn-1.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:66b1cf721a9f07f518eb545098226796c399c64abdcbf91c2b95d625068363da"}, + {file = "scikit_learn-1.6.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:7b35b60cf4cd6564b636e4a40516b3c61a4fa7a8b1f7a3ce80c38ebe04750bc3"}, + {file = "scikit_learn-1.6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a73b1c2038c93bc7f4bf21f6c9828d5116c5d2268f7a20cfbbd41d3074d52083"}, + {file = "scikit_learn-1.6.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c3fa7d3dd5a0ec2d0baba0d644916fa2ab180ee37850c5d536245df916946bd"}, + {file = "scikit_learn-1.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:df778486a32518cda33818b7e3ce48c78cef1d5f640a6bc9d97c6d2e71449a51"}, + {file = "scikit_learn-1.6.0.tar.gz", hash = "sha256:9d58481f9f7499dff4196927aedd4285a0baec8caa3790efbe205f13de37dd6e"}, ] [package.dependencies] @@ -4109,53 +4088,82 @@ threadpoolctl = ">=3.1.0" [package.extras] benchmark = ["matplotlib (>=3.3.4)", "memory_profiler (>=0.57.0)", "pandas (>=1.1.5)"] build = ["cython (>=3.0.10)", "meson-python (>=0.16.0)", "numpy (>=1.19.5)", "scipy (>=1.6.0)"] -docs = ["Pillow (>=7.1.2)", "matplotlib (>=3.3.4)", "memory_profiler (>=0.57.0)", "numpydoc (>=1.2.0)", "pandas (>=1.1.5)", "plotly (>=5.14.0)", "polars (>=0.20.30)", "pooch (>=1.6.0)", "pydata-sphinx-theme (>=0.15.3)", "scikit-image (>=0.17.2)", "seaborn (>=0.9.0)", "sphinx (>=7.3.7)", "sphinx-copybutton (>=0.5.2)", "sphinx-design (>=0.5.0)", "sphinx-design (>=0.6.0)", "sphinx-gallery (>=0.16.0)", "sphinx-prompt (>=1.4.0)", "sphinx-remove-toctrees (>=1.0.0.post1)", "sphinxcontrib-sass (>=0.3.4)", "sphinxext-opengraph (>=0.9.1)"] +docs = ["Pillow (>=7.1.2)", "matplotlib (>=3.3.4)", "memory_profiler (>=0.57.0)", "numpydoc (>=1.2.0)", "pandas (>=1.1.5)", "plotly (>=5.14.0)", "polars (>=0.20.30)", "pooch (>=1.6.0)", "pydata-sphinx-theme (>=0.15.3)", "scikit-image (>=0.17.2)", "seaborn (>=0.9.0)", "sphinx (>=7.3.7)", "sphinx-copybutton (>=0.5.2)", "sphinx-design (>=0.5.0)", "sphinx-design (>=0.6.0)", "sphinx-gallery (>=0.17.1)", "sphinx-prompt (>=1.4.0)", "sphinx-remove-toctrees (>=1.0.0.post1)", "sphinxcontrib-sass (>=0.3.4)", "sphinxext-opengraph (>=0.9.1)", "towncrier (>=24.8.0)"] examples = ["matplotlib (>=3.3.4)", "pandas (>=1.1.5)", "plotly (>=5.14.0)", "pooch (>=1.6.0)", "scikit-image (>=0.17.2)", "seaborn (>=0.9.0)"] install = ["joblib (>=1.2.0)", "numpy (>=1.19.5)", "scipy (>=1.6.0)", "threadpoolctl (>=3.1.0)"] maintenance = ["conda-lock (==2.5.6)"] -tests = ["black (>=24.3.0)", "matplotlib (>=3.3.4)", "mypy (>=1.9)", "numpydoc (>=1.2.0)", "pandas (>=1.1.5)", "polars (>=0.20.30)", "pooch (>=1.6.0)", "pyamg (>=4.0.0)", "pyarrow (>=12.0.0)", "pytest (>=7.1.2)", "pytest-cov (>=2.9.0)", "ruff (>=0.2.1)", "scikit-image (>=0.17.2)"] +tests = ["black (>=24.3.0)", "matplotlib (>=3.3.4)", "mypy (>=1.9)", "numpydoc (>=1.2.0)", "pandas (>=1.1.5)", "polars (>=0.20.30)", "pooch (>=1.6.0)", "pyamg (>=4.0.0)", "pyarrow (>=12.0.0)", "pytest (>=7.1.2)", "pytest-cov (>=2.9.0)", "ruff (>=0.5.1)", "scikit-image (>=0.17.2)"] [[package]] name = "scipy" -version = "1.13.1" +version = "1.14.1" description = "Fundamental algorithms for scientific computing in Python" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" +files = [ + {file = "scipy-1.14.1-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:b28d2ca4add7ac16ae8bb6632a3c86e4b9e4d52d3e34267f6e1b0c1f8d87e389"}, + {file = "scipy-1.14.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d0d2821003174de06b69e58cef2316a6622b60ee613121199cb2852a873f8cf3"}, + {file = "scipy-1.14.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8bddf15838ba768bb5f5083c1ea012d64c9a444e16192762bd858f1e126196d0"}, + {file = "scipy-1.14.1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:97c5dddd5932bd2a1a31c927ba5e1463a53b87ca96b5c9bdf5dfd6096e27efc3"}, + {file = "scipy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ff0a7e01e422c15739ecd64432743cf7aae2b03f3084288f399affcefe5222d"}, + {file = "scipy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e32dced201274bf96899e6491d9ba3e9a5f6b336708656466ad0522d8528f69"}, + {file = "scipy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8426251ad1e4ad903a4514712d2fa8fdd5382c978010d1c6f5f37ef286a713ad"}, + {file = "scipy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:a49f6ed96f83966f576b33a44257d869756df6cf1ef4934f59dd58b25e0327e5"}, + {file = "scipy-1.14.1-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:2da0469a4ef0ecd3693761acbdc20f2fdeafb69e6819cc081308cc978153c675"}, + {file = "scipy-1.14.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:c0ee987efa6737242745f347835da2cc5bb9f1b42996a4d97d5c7ff7928cb6f2"}, + {file = "scipy-1.14.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3a1b111fac6baec1c1d92f27e76511c9e7218f1695d61b59e05e0fe04dc59617"}, + {file = "scipy-1.14.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8475230e55549ab3f207bff11ebfc91c805dc3463ef62eda3ccf593254524ce8"}, + {file = "scipy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:278266012eb69f4a720827bdd2dc54b2271c97d84255b2faaa8f161a158c3b37"}, + {file = "scipy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fef8c87f8abfb884dac04e97824b61299880c43f4ce675dd2cbeadd3c9b466d2"}, + {file = "scipy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b05d43735bb2f07d689f56f7b474788a13ed8adc484a85aa65c0fd931cf9ccd2"}, + {file = "scipy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:716e389b694c4bb564b4fc0c51bc84d381735e0d39d3f26ec1af2556ec6aad94"}, + {file = "scipy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:631f07b3734d34aced009aaf6fedfd0eb3498a97e581c3b1e5f14a04164a456d"}, + {file = "scipy-1.14.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:af29a935803cc707ab2ed7791c44288a682f9c8107bc00f0eccc4f92c08d6e07"}, + {file = "scipy-1.14.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:2843f2d527d9eebec9a43e6b406fb7266f3af25a751aa91d62ff416f54170bc5"}, + {file = "scipy-1.14.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:eb58ca0abd96911932f688528977858681a59d61a7ce908ffd355957f7025cfc"}, + {file = "scipy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30ac8812c1d2aab7131a79ba62933a2a76f582d5dbbc695192453dae67ad6310"}, + {file = "scipy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f9ea80f2e65bdaa0b7627fb00cbeb2daf163caa015e59b7516395fe3bd1e066"}, + {file = "scipy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:edaf02b82cd7639db00dbff629995ef185c8df4c3ffa71a5562a595765a06ce1"}, + {file = "scipy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:2ff38e22128e6c03ff73b6bb0f85f897d2362f8c052e3b8ad00532198fbdae3f"}, + {file = "scipy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1729560c906963fc8389f6aac023739ff3983e727b1a4d87696b7bf108316a79"}, + {file = "scipy-1.14.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:4079b90df244709e675cdc8b93bfd8a395d59af40b72e339c2287c91860deb8e"}, + {file = "scipy-1.14.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e0cf28db0f24a38b2a0ca33a85a54852586e43cf6fd876365c86e0657cfe7d73"}, + {file = "scipy-1.14.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0c2f95de3b04e26f5f3ad5bb05e74ba7f68b837133a4492414b3afd79dfe540e"}, + {file = "scipy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b99722ea48b7ea25e8e015e8341ae74624f72e5f21fc2abd45f3a93266de4c5d"}, + {file = "scipy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5149e3fd2d686e42144a093b206aef01932a0059c2a33ddfa67f5f035bdfe13e"}, + {file = "scipy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4f5a7c49323533f9103d4dacf4e4f07078f360743dec7f7596949149efeec06"}, + {file = "scipy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:baff393942b550823bfce952bb62270ee17504d02a1801d7fd0719534dfb9c84"}, + {file = "scipy-1.14.1.tar.gz", hash = "sha256:5a275584e726026a5699459aa72f828a610821006228e841b94275c4a7c08417"}, +] + +[package.dependencies] +numpy = ">=1.23.5,<2.3" + +[package.extras] +dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy (==1.10.0)", "pycodestyle", "pydevtool", "rich-click", "ruff (>=0.0.292)", "types-psutil", "typing_extensions"] +doc = ["jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.13.1)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0,<=7.3.7)", "sphinx-design (>=0.4.0)"] +test = ["Cython", "array-api-strict (>=2.0)", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] + +[[package]] +name = "scoringrules" +version = "0.7.1" +description = "Scoring rules for probabilistic forecast evaluation." +optional = false +python-versions = ">=3.10" files = [ - {file = "scipy-1.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:20335853b85e9a49ff7572ab453794298bcf0354d8068c5f6775a0eabf350aca"}, - {file = "scipy-1.13.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d605e9c23906d1994f55ace80e0125c587f96c020037ea6aa98d01b4bd2e222f"}, - {file = "scipy-1.13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfa31f1def5c819b19ecc3a8b52d28ffdcc7ed52bb20c9a7589669dd3c250989"}, - {file = "scipy-1.13.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26264b282b9da0952a024ae34710c2aff7d27480ee91a2e82b7b7073c24722f"}, - {file = "scipy-1.13.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:eccfa1906eacc02de42d70ef4aecea45415f5be17e72b61bafcfd329bdc52e94"}, - {file = "scipy-1.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:2831f0dc9c5ea9edd6e51e6e769b655f08ec6db6e2e10f86ef39bd32eb11da54"}, - {file = "scipy-1.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:27e52b09c0d3a1d5b63e1105f24177e544a222b43611aaf5bc44d4a0979e32f9"}, - {file = "scipy-1.13.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:54f430b00f0133e2224c3ba42b805bfd0086fe488835effa33fa291561932326"}, - {file = "scipy-1.13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e89369d27f9e7b0884ae559a3a956e77c02114cc60a6058b4e5011572eea9299"}, - {file = "scipy-1.13.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a78b4b3345f1b6f68a763c6e25c0c9a23a9fd0f39f5f3d200efe8feda560a5fa"}, - {file = "scipy-1.13.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:45484bee6d65633752c490404513b9ef02475b4284c4cfab0ef946def50b3f59"}, - {file = "scipy-1.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:5713f62f781eebd8d597eb3f88b8bf9274e79eeabf63afb4a737abc6c84ad37b"}, - {file = "scipy-1.13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5d72782f39716b2b3509cd7c33cdc08c96f2f4d2b06d51e52fb45a19ca0c86a1"}, - {file = "scipy-1.13.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:017367484ce5498445aade74b1d5ab377acdc65e27095155e448c88497755a5d"}, - {file = "scipy-1.13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:949ae67db5fa78a86e8fa644b9a6b07252f449dcf74247108c50e1d20d2b4627"}, - {file = "scipy-1.13.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de3ade0e53bc1f21358aa74ff4830235d716211d7d077e340c7349bc3542e884"}, - {file = "scipy-1.13.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2ac65fb503dad64218c228e2dc2d0a0193f7904747db43014645ae139c8fad16"}, - {file = "scipy-1.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:cdd7dacfb95fea358916410ec61bbc20440f7860333aee6d882bb8046264e949"}, - {file = "scipy-1.13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:436bbb42a94a8aeef855d755ce5a465479c721e9d684de76bf61a62e7c2b81d5"}, - {file = "scipy-1.13.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:8335549ebbca860c52bf3d02f80784e91a004b71b059e3eea9678ba994796a24"}, - {file = "scipy-1.13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d533654b7d221a6a97304ab63c41c96473ff04459e404b83275b60aa8f4b7004"}, - {file = "scipy-1.13.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637e98dcf185ba7f8e663e122ebf908c4702420477ae52a04f9908707456ba4d"}, - {file = "scipy-1.13.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a014c2b3697bde71724244f63de2476925596c24285c7a637364761f8710891c"}, - {file = "scipy-1.13.1-cp39-cp39-win_amd64.whl", hash = "sha256:392e4ec766654852c25ebad4f64e4e584cf19820b980bc04960bca0b0cd6eaa2"}, - {file = "scipy-1.13.1.tar.gz", hash = "sha256:095a87a0312b08dfd6a6155cbbd310a8c51800fc931b8c0b84003014b874ed3c"}, + {file = "scoringrules-0.7.1-py3-none-any.whl", hash = "sha256:94f253ac4196c98773adf8024fa6f56c79c3b67c691412da19675c8c16b3f61e"}, + {file = "scoringrules-0.7.1.tar.gz", hash = "sha256:de60b9174174975d1ced5de3bd62822ef51b3eb0fa8b2a0866068b9df0296e2c"}, ] [package.dependencies] -numpy = ">=1.22.4,<2.3" +numpy = ">=1.25.0" +scipy = ">=1.10.0" [package.extras] -dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy", "pycodestyle", "pydevtool", "rich-click", "ruff", "types-psutil", "typing_extensions"] -doc = ["jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.12.0)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0)", "sphinx-design (>=0.4.0)"] -test = ["array-api-strict", "asv", "gmpy2", "hypothesis (>=6.30)", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] +jax = ["jax (>=0.4.31)"] +numba = ["numba (>=0.60.0)"] +tensorflow = ["tensorflow (>=2.17.0)"] +torch = ["torch (>=2.4.1)"] [[package]] name = "send2trash" @@ -4175,33 +4183,33 @@ win32 = ["pywin32"] [[package]] name = "setuptools" -version = "75.3.0" +version = "75.6.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "setuptools-75.3.0-py3-none-any.whl", hash = "sha256:f2504966861356aa38616760c0f66568e535562374995367b4e69c7143cf6bcd"}, - {file = "setuptools-75.3.0.tar.gz", hash = "sha256:fba5dd4d766e97be1b1681d98712680ae8f2f26d7881245f2ce9e40714f1a686"}, + {file = "setuptools-75.6.0-py3-none-any.whl", hash = "sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d"}, + {file = "setuptools-75.6.0.tar.gz", hash = "sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"] -core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.7.0)"] +core = ["importlib_metadata (>=6)", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.12.*)", "pytest-mypy"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (>=1.12,<1.14)", "pytest-mypy"] [[package]] name = "six" -version = "1.16.0" +version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, ] [[package]] @@ -4334,13 +4342,13 @@ sqlcipher = ["sqlcipher3_binary"] [[package]] name = "sqlparse" -version = "0.5.1" +version = "0.5.2" description = "A non-validating SQL parser." optional = false python-versions = ">=3.8" files = [ - {file = "sqlparse-0.5.1-py3-none-any.whl", hash = "sha256:773dcbf9a5ab44a090f3441e2180efe2560220203dc2f8c0b0fa141e18b505e4"}, - {file = "sqlparse-0.5.1.tar.gz", hash = "sha256:bb6b4df465655ef332548e24f08e205afc81b9ab86cb1c45657a7ff173a3a00e"}, + {file = "sqlparse-0.5.2-py3-none-any.whl", hash = "sha256:e99bc85c78160918c3e1d9230834ab8d80fc06c59d03f8db2618f65f65dda55e"}, + {file = "sqlparse-0.5.2.tar.gz", hash = "sha256:9e37b35e16d1cc652a2545f0997c1deb23ea28fa1f3eefe609eee3063c3b105f"}, ] [package.extras] @@ -4367,211 +4375,21 @@ pure-eval = "*" tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] [[package]] -name = "tensorboard" -version = "2.16.2" -description = "TensorBoard lets you watch Tensors Flow" -optional = false -python-versions = ">=3.9" -files = [ - {file = "tensorboard-2.16.2-py3-none-any.whl", hash = "sha256:9f2b4e7dad86667615c0e5cd072f1ea8403fc032a299f0072d6f74855775cc45"}, -] - -[package.dependencies] -absl-py = ">=0.4" -grpcio = ">=1.48.2" -markdown = ">=2.6.8" -numpy = ">=1.12.0" -protobuf = ">=3.19.6,<4.24.0 || >4.24.0" -setuptools = ">=41.0.0" -six = ">1.9" -tensorboard-data-server = ">=0.7.0,<0.8.0" -werkzeug = ">=1.0.1" - -[[package]] -name = "tensorboard-data-server" -version = "0.7.2" -description = "Fast data loading for TensorBoard" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tensorboard_data_server-0.7.2-py3-none-any.whl", hash = "sha256:7e0610d205889588983836ec05dc098e80f97b7e7bbff7e994ebb78f578d0ddb"}, - {file = "tensorboard_data_server-0.7.2-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:9fe5d24221b29625dbc7328b0436ca7fc1c23de4acf4d272f1180856e32f9f60"}, - {file = "tensorboard_data_server-0.7.2-py3-none-manylinux_2_31_x86_64.whl", hash = "sha256:ef687163c24185ae9754ed5650eb5bc4d84ff257aabdc33f0cc6f74d8ba54530"}, -] - -[[package]] -name = "tensorflow" -version = "2.16.2" -description = "TensorFlow is an open source machine learning framework for everyone." -optional = false -python-versions = ">=3.9" -files = [ - {file = "tensorflow-2.16.2-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:546dc68d0740fb4b75593a6bfa308da9526fe31f65c2181d48c8551c4a0ad02f"}, - {file = "tensorflow-2.16.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:72c84f0e0f8ad0e7cb7b4b3fe9d1c899e6cbebc51c0e64df42a2a32a904aacd7"}, - {file = "tensorflow-2.16.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a0aee52797cd58870e3bb9c2b4bc0fc2a57eae29a334282bcc08943ca582718"}, - {file = "tensorflow-2.16.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ed24662a3625b2eaa89a02ea177aadad840d6eb91445091fe1f7ad5fa528db3"}, - {file = "tensorflow-2.16.2-cp310-cp310-win_amd64.whl", hash = "sha256:e340de5abf4d7dc1d8a5782559aa41757f8a84aeb2d4c490c0fa538a7521fae6"}, - {file = "tensorflow-2.16.2-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:ec06570d57bfa0e2be804405e3cdc2960e94887e7619ffb6bc053e9775b695aa"}, - {file = "tensorflow-2.16.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:2c8a0e79395639b762e62002db99b2f6cc608f744312c9940899c1128f325331"}, - {file = "tensorflow-2.16.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8728b12bc86941d90d0a927c40d4b21f8820964a80439a7c45f850eb37d57067"}, - {file = "tensorflow-2.16.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8798dea8e2281b4a0b569d9c00e7949c0090509be363da271e1ef21828bffae"}, - {file = "tensorflow-2.16.2-cp311-cp311-win_amd64.whl", hash = "sha256:1da04e39834cdba509b4dd5ac5c71c3a1d1ffe6bc03e6970e65791b9a4071340"}, - {file = "tensorflow-2.16.2-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:912b8cd1f88fd1ef32b8db54f0193ad0a3f057691324436ba82c5f74a63a17dd"}, - {file = "tensorflow-2.16.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:917366179b596d0dae13e194a26965229b09fef946e4a5892a47fa9b4f7e4ba1"}, - {file = "tensorflow-2.16.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7df529f8db271d3def80538aa7fcd6f5abe306f7b01cb5b580138df68afb499"}, - {file = "tensorflow-2.16.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5badc6744672a3181c012b6ab2815975be34d0573db3b561383634acc0d46a55"}, - {file = "tensorflow-2.16.2-cp312-cp312-win_amd64.whl", hash = "sha256:505df82fde3b9c6a2a78bf679efb4d0a2e84f4f925202130477ca519ae1514e4"}, - {file = "tensorflow-2.16.2-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:2528a162e879b40d81db3568c08256718cec4a0356580badbd362cd8af02a41b"}, - {file = "tensorflow-2.16.2-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:4c94106b73ecd044b7772e4338f8aa65a43ef2e290fe3fc27cc094138f50a341"}, - {file = "tensorflow-2.16.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec5c57e6828b074ddb460aa69fbaa2cd502c6080a4e200e0163f2a2c9e20acfc"}, - {file = "tensorflow-2.16.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b085fc4b296e0daf2e8a8b71bf433acba0ba30d6c30f3d07ad05f10477c7762c"}, - {file = "tensorflow-2.16.2-cp39-cp39-win_amd64.whl", hash = "sha256:5d5951e91435909d6023f8c5afcfde9cee946a65ed03020fc8b87e627c04c6d1"}, -] - -[package.dependencies] -absl-py = ">=1.0.0" -astunparse = ">=1.6.0" -flatbuffers = ">=23.5.26" -gast = ">=0.2.1,<0.5.0 || >0.5.0,<0.5.1 || >0.5.1,<0.5.2 || >0.5.2" -google-pasta = ">=0.1.1" -grpcio = ">=1.24.3,<2.0" -h5py = ">=3.10.0" -keras = ">=3.0.0" -libclang = ">=13.0.0" -ml-dtypes = ">=0.3.1,<0.4.0" -numpy = {version = ">=1.23.5,<2.0.0", markers = "python_version <= \"3.11\""} -opt-einsum = ">=2.3.2" -packaging = "*" -protobuf = ">=3.20.3,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" -requests = ">=2.21.0,<3" -setuptools = "*" -six = ">=1.12.0" -tensorboard = ">=2.16,<2.17" -tensorflow-io-gcs-filesystem = {version = ">=0.23.1", markers = "python_version < \"3.12\""} -termcolor = ">=1.1.0" -typing-extensions = ">=3.6.6" -wrapt = ">=1.11.0" - -[package.extras] -and-cuda = ["nvidia-cublas-cu12 (==12.3.4.1)", "nvidia-cuda-cupti-cu12 (==12.3.101)", "nvidia-cuda-nvcc-cu12 (==12.3.107)", "nvidia-cuda-nvrtc-cu12 (==12.3.107)", "nvidia-cuda-runtime-cu12 (==12.3.101)", "nvidia-cudnn-cu12 (==8.9.7.29)", "nvidia-cufft-cu12 (==11.0.12.1)", "nvidia-curand-cu12 (==10.3.4.107)", "nvidia-cusolver-cu12 (==11.5.4.101)", "nvidia-cusparse-cu12 (==12.2.0.103)", "nvidia-nccl-cu12 (==2.19.3)", "nvidia-nvjitlink-cu12 (==12.3.101)"] - -[[package]] -name = "tensorflow-cpu" -version = "2.16.2" -description = "TensorFlow is an open source machine learning framework for everyone." -optional = true -python-versions = ">=3.9" -files = [ - {file = "tensorflow_cpu-2.16.2-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:fe1a52b0303486fc20b9832592f336c3851c41ca4a4cf9dfce1616f0c60a40c2"}, - {file = "tensorflow_cpu-2.16.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58f4f8e3a3304110b9d3d24d3b1f73e690f9b5bf88738b88d2f1e63ba337fb8c"}, - {file = "tensorflow_cpu-2.16.2-cp310-cp310-win_amd64.whl", hash = "sha256:53d19979e7cd32b81540925d13cb3a392c6d20cd69ad54366207a96a06eeaa7c"}, - {file = "tensorflow_cpu-2.16.2-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:62e1f998e6a1ca8e6ca96087e623b9ce2e0587d5f7b72ae90b03cdf2e82013c6"}, - {file = "tensorflow_cpu-2.16.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c855cb6bf05d32dd5928086b03a5f6b4189b63c22b95e017703cbee4291023ca"}, - {file = "tensorflow_cpu-2.16.2-cp311-cp311-win_amd64.whl", hash = "sha256:0a1497628f4251a6e6670c4fa6b84a8530030163ab5e77791f204e3a2ac220ca"}, - {file = "tensorflow_cpu-2.16.2-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:f8d3004d6bfe76fa1f25a01635bcebc743e5eed6f7d6e9c45d9edff4b3c04427"}, - {file = "tensorflow_cpu-2.16.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ec09acc09534017e02318456f77e96947b2ab4d24d2eb580541405c45ea3b70"}, - {file = "tensorflow_cpu-2.16.2-cp312-cp312-win_amd64.whl", hash = "sha256:11df7d715b42eb5ef13138a75c8744052a05e8b50761606a3ca219943fa2e874"}, - {file = "tensorflow_cpu-2.16.2-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:dd50ed785c64f1ab6de7fd78a99340cb484679a446f845400f75b661a38ad1b1"}, - {file = "tensorflow_cpu-2.16.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a7e0db8a1aa67255cdadac48cb8117fe09b3742d8397998f69bf01905c1f2de"}, - {file = "tensorflow_cpu-2.16.2-cp39-cp39-win_amd64.whl", hash = "sha256:e117649db32dfdb920e0ba63fb899a8fb1f184a314723ca2399a16750dda9e3e"}, -] - -[package.dependencies] -absl-py = ">=1.0.0" -astunparse = ">=1.6.0" -flatbuffers = ">=23.5.26" -gast = ">=0.2.1,<0.5.0 || >0.5.0,<0.5.1 || >0.5.1,<0.5.2 || >0.5.2" -google-pasta = ">=0.1.1" -grpcio = ">=1.24.3,<2.0" -h5py = ">=3.10.0" -keras = ">=3.0.0" -libclang = ">=13.0.0" -ml-dtypes = ">=0.3.1,<0.4.0" -numpy = {version = ">=1.23.5,<2.0.0", markers = "python_version <= \"3.11\""} -opt-einsum = ">=2.3.2" -packaging = "*" -protobuf = ">=3.20.3,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" -requests = ">=2.21.0,<3" -setuptools = "*" -six = ">=1.12.0" -tensorboard = ">=2.16,<2.17" -tensorflow-io-gcs-filesystem = {version = ">=0.23.1", markers = "python_version < \"3.12\""} -termcolor = ">=1.1.0" -typing-extensions = ">=3.6.6" -wrapt = ">=1.11.0" - -[package.extras] -and-cuda = ["nvidia-cublas-cu12 (==12.3.4.1)", "nvidia-cuda-cupti-cu12 (==12.3.101)", "nvidia-cuda-nvcc-cu12 (==12.3.107)", "nvidia-cuda-nvrtc-cu12 (==12.3.107)", "nvidia-cuda-runtime-cu12 (==12.3.101)", "nvidia-cudnn-cu12 (==8.9.7.29)", "nvidia-cufft-cu12 (==11.0.12.1)", "nvidia-curand-cu12 (==10.3.4.107)", "nvidia-cusolver-cu12 (==11.5.4.101)", "nvidia-cusparse-cu12 (==12.2.0.103)", "nvidia-nccl-cu12 (==2.19.3)", "nvidia-nvjitlink-cu12 (==12.3.101)"] - -[[package]] -name = "tensorflow-io-gcs-filesystem" -version = "0.37.1" -description = "TensorFlow IO" -optional = false -python-versions = "<3.13,>=3.7" -files = [ - {file = "tensorflow_io_gcs_filesystem-0.37.1-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:249c12b830165841411ba71e08215d0e94277a49c551e6dd5d72aab54fe5491b"}, - {file = "tensorflow_io_gcs_filesystem-0.37.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:257aab23470a0796978efc9c2bcf8b0bc80f22e6298612a4c0a50d3f4e88060c"}, - {file = "tensorflow_io_gcs_filesystem-0.37.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8febbfcc67c61e542a5ac1a98c7c20a91a5e1afc2e14b1ef0cb7c28bc3b6aa70"}, - {file = "tensorflow_io_gcs_filesystem-0.37.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9679b36e3a80921876f31685ab6f7270f3411a4cc51bc2847e80d0e4b5291e27"}, - {file = "tensorflow_io_gcs_filesystem-0.37.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:32c50ab4e29a23c1f91cd0f9ab8c381a0ab10f45ef5c5252e94965916041737c"}, - {file = "tensorflow_io_gcs_filesystem-0.37.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:b02f9c5f94fd62773954a04f69b68c4d576d076fd0db4ca25d5479f0fbfcdbad"}, - {file = "tensorflow_io_gcs_filesystem-0.37.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e1f2796b57e799a8ca1b75bf47c2aaa437c968408cc1a402a9862929e104cda"}, - {file = "tensorflow_io_gcs_filesystem-0.37.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee7c8ee5fe2fd8cb6392669ef16e71841133041fee8a330eff519ad9b36e4556"}, - {file = "tensorflow_io_gcs_filesystem-0.37.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:ffebb6666a7bfc28005f4fbbb111a455b5e7d6cd3b12752b7050863ecb27d5cc"}, - {file = "tensorflow_io_gcs_filesystem-0.37.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:fe8dcc6d222258a080ac3dfcaaaa347325ce36a7a046277f6b3e19abc1efb3c5"}, - {file = "tensorflow_io_gcs_filesystem-0.37.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fbb33f1745f218464a59cecd9a18e32ca927b0f4d77abd8f8671b645cc1a182f"}, - {file = "tensorflow_io_gcs_filesystem-0.37.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:286389a203a5aee1a4fa2e53718c661091aa5fea797ff4fa6715ab8436b02e6c"}, - {file = "tensorflow_io_gcs_filesystem-0.37.1-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:ee5da49019670ed364f3e5fb86b46420841a6c3cb52a300553c63841671b3e6d"}, - {file = "tensorflow_io_gcs_filesystem-0.37.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:8943036bbf84e7a2be3705cb56f9c9df7c48c9e614bb941f0936c58e3ca89d6f"}, - {file = "tensorflow_io_gcs_filesystem-0.37.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:426de1173cb81fbd62becec2012fc00322a295326d90eb6c737fab636f182aed"}, - {file = "tensorflow_io_gcs_filesystem-0.37.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0df00891669390078a003cedbdd3b8e645c718b111917535fa1d7725e95cdb95"}, -] - -[package.extras] -tensorflow = ["tensorflow (>=2.16.0,<2.17.0)"] -tensorflow-aarch64 = ["tensorflow-aarch64 (>=2.16.0,<2.17.0)"] -tensorflow-cpu = ["tensorflow-cpu (>=2.16.0,<2.17.0)"] -tensorflow-gpu = ["tensorflow-gpu (>=2.16.0,<2.17.0)"] -tensorflow-rocm = ["tensorflow-rocm (>=2.16.0,<2.17.0)"] - -[[package]] -name = "tensorflow-probability" -version = "0.24.0" -description = "Probabilistic modeling and statistical inference in TensorFlow" +name = "sympy" +version = "1.13.1" +description = "Computer algebra system (CAS) in Python" optional = false -python-versions = ">=3.9" +python-versions = ">=3.8" files = [ - {file = "tensorflow_probability-0.24.0-py2.py3-none-any.whl", hash = "sha256:8c1774683e38359dbcaf3697e79b7e6a4e69b9c7b3679e78ee18f43e59e5759b"}, + {file = "sympy-1.13.1-py3-none-any.whl", hash = "sha256:db36cdc64bf61b9b24578b6f7bab1ecdd2452cf008f34faa33776680c26d66f8"}, + {file = "sympy-1.13.1.tar.gz", hash = "sha256:9cebf7e04ff162015ce31c9c6c9144daa34a93bd082f54fd8f12deca4f47515f"}, ] [package.dependencies] -absl-py = "*" -cloudpickle = ">=1.3" -decorator = "*" -dm-tree = "*" -gast = ">=0.3.2" -numpy = ">=1.13.3" -six = ">=1.10.0" +mpmath = ">=1.1.0,<1.4" [package.extras] -jax = ["jax", "jaxlib"] -tf = ["tensorflow (>=2.16)", "tf-keras (>=2.16)"] -tfds = ["tensorflow-datasets (>=2.2.0)"] - -[[package]] -name = "termcolor" -version = "2.5.0" -description = "ANSI color formatting for output in terminal" -optional = false -python-versions = ">=3.9" -files = [ - {file = "termcolor-2.5.0-py3-none-any.whl", hash = "sha256:37b17b5fc1e604945c2642c872a3764b5d547a48009871aea3edd3afa180afb8"}, - {file = "termcolor-2.5.0.tar.gz", hash = "sha256:998d8d27da6d48442e8e1f016119076b690d962507531df4890fcd2db2ef8a6f"}, -] - -[package.extras] -tests = ["pytest", "pytest-cov"] +dev = ["hypothesis (>=6.70.0)", "pytest (>=7.1.0)"] [[package]] name = "terminado" @@ -4594,20 +4412,6 @@ docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] test = ["pre-commit", "pytest (>=7.0)", "pytest-timeout"] typing = ["mypy (>=1.6,<2.0)", "traitlets (>=5.11.1)"] -[[package]] -name = "tf-keras" -version = "2.16.0" -description = "Deep learning for humans." -optional = false -python-versions = ">=3.9" -files = [ - {file = "tf_keras-2.16.0-py3-none-any.whl", hash = "sha256:b2ad0541fa7d9e92c4b7a1b96593377afb58aaff374299a6ca6be1a42f51d899"}, - {file = "tf_keras-2.16.0.tar.gz", hash = "sha256:db53891f1ac98197c2acced98cdca8c06ba8255655a6cb7eb95ed49676118280"}, -] - -[package.dependencies] -tensorflow = ">=2.16,<2.17" - [[package]] name = "threadpoolctl" version = "3.5.0" @@ -4639,13 +4443,43 @@ test = ["pytest", "ruff"] [[package]] name = "tomli" -version = "2.0.2" +version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" files = [ - {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, - {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, + {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, + {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, + {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, + {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, + {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, + {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, + {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, + {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, ] [[package]] @@ -4659,24 +4493,75 @@ files = [ {file = "toolz-1.0.0.tar.gz", hash = "sha256:2c86e3d9a04798ac556793bced838816296a2f085017664e4995cb40a1047a02"}, ] +[[package]] +name = "torch" +version = "2.5.1" +description = "Tensors and Dynamic neural networks in Python with strong GPU acceleration" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "torch-2.5.1-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:71328e1bbe39d213b8721678f9dcac30dfc452a46d586f1d514a6aa0a99d4744"}, + {file = "torch-2.5.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:34bfa1a852e5714cbfa17f27c49d8ce35e1b7af5608c4bc6e81392c352dbc601"}, + {file = "torch-2.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:32a037bd98a241df6c93e4c789b683335da76a2ac142c0973675b715102dc5fa"}, + {file = "torch-2.5.1-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:23d062bf70776a3d04dbe74db950db2a5245e1ba4f27208a87f0d743b0d06e86"}, + {file = "torch-2.5.1-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:de5b7d6740c4b636ef4db92be922f0edc425b65ed78c5076c43c42d362a45457"}, + {file = "torch-2.5.1-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:340ce0432cad0d37f5a31be666896e16788f1adf8ad7be481196b503dad675b9"}, + {file = "torch-2.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:603c52d2fe06433c18b747d25f5c333f9c1d58615620578c326d66f258686f9a"}, + {file = "torch-2.5.1-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:31f8c39660962f9ae4eeec995e3049b5492eb7360dd4f07377658ef4d728fa4c"}, + {file = "torch-2.5.1-cp312-cp312-manylinux1_x86_64.whl", hash = "sha256:ed231a4b3a5952177fafb661213d690a72caaad97d5824dd4fc17ab9e15cec03"}, + {file = "torch-2.5.1-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:3f4b7f10a247e0dcd7ea97dc2d3bfbfc90302ed36d7f3952b0008d0df264e697"}, + {file = "torch-2.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:73e58e78f7d220917c5dbfad1a40e09df9929d3b95d25e57d9f8558f84c9a11c"}, + {file = "torch-2.5.1-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:8c712df61101964eb11910a846514011f0b6f5920c55dbf567bff8a34163d5b1"}, + {file = "torch-2.5.1-cp313-cp313-manylinux1_x86_64.whl", hash = "sha256:9b61edf3b4f6e3b0e0adda8b3960266b9009d02b37555971f4d1c8f7a05afed7"}, + {file = "torch-2.5.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:1f3b7fb3cf7ab97fae52161423f81be8c6b8afac8d9760823fd623994581e1a3"}, + {file = "torch-2.5.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:7974e3dce28b5a21fb554b73e1bc9072c25dde873fa00d54280861e7a009d7dc"}, + {file = "torch-2.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:46c817d3ea33696ad3b9df5e774dba2257e9a4cd3c4a3afbf92f6bb13ac5ce2d"}, + {file = "torch-2.5.1-cp39-none-macosx_11_0_arm64.whl", hash = "sha256:8046768b7f6d35b85d101b4b38cba8aa2f3cd51952bc4c06a49580f2ce682291"}, +] + +[package.dependencies] +filelock = "*" +fsspec = "*" +jinja2 = "*" +networkx = "*" +nvidia-cublas-cu12 = {version = "12.4.5.8", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cuda-cupti-cu12 = {version = "12.4.127", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cuda-nvrtc-cu12 = {version = "12.4.127", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cuda-runtime-cu12 = {version = "12.4.127", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cudnn-cu12 = {version = "9.1.0.70", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cufft-cu12 = {version = "11.2.1.3", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-curand-cu12 = {version = "10.3.5.147", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cusolver-cu12 = {version = "11.6.1.9", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cusparse-cu12 = {version = "12.3.1.170", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-nccl-cu12 = {version = "2.21.5", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-nvjitlink-cu12 = {version = "12.4.127", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-nvtx-cu12 = {version = "12.4.127", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +sympy = {version = "1.13.1", markers = "python_version >= \"3.9\""} +triton = {version = "3.1.0", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version < \"3.13\""} +typing-extensions = ">=4.8.0" + +[package.extras] +opt-einsum = ["opt-einsum (>=3.3)"] +optree = ["optree (>=0.12.0)"] + [[package]] name = "tornado" -version = "6.4.1" +version = "6.4.2" description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." optional = false python-versions = ">=3.8" files = [ - {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:163b0aafc8e23d8cdc3c9dfb24c5368af84a81e3364745ccb4427669bf84aec8"}, - {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6d5ce3437e18a2b66fbadb183c1d3364fb03f2be71299e7d10dbeeb69f4b2a14"}, - {file = "tornado-6.4.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e20b9113cd7293f164dc46fffb13535266e713cdb87bd2d15ddb336e96cfc4"}, - {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ae50a504a740365267b2a8d1a90c9fbc86b780a39170feca9bcc1787ff80842"}, - {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:613bf4ddf5c7a95509218b149b555621497a6cc0d46ac341b30bd9ec19eac7f3"}, - {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25486eb223babe3eed4b8aecbac33b37e3dd6d776bc730ca14e1bf93888b979f"}, - {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:454db8a7ecfcf2ff6042dde58404164d969b6f5d58b926da15e6b23817950fc4"}, - {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a02a08cc7a9314b006f653ce40483b9b3c12cda222d6a46d4ac63bb6c9057698"}, - {file = "tornado-6.4.1-cp38-abi3-win32.whl", hash = "sha256:d9a566c40b89757c9aa8e6f032bcdb8ca8795d7c1a9762910c722b1635c9de4d"}, - {file = "tornado-6.4.1-cp38-abi3-win_amd64.whl", hash = "sha256:b24b8982ed444378d7f21d563f4180a2de31ced9d8d84443907a0a64da2072e7"}, - {file = "tornado-6.4.1.tar.gz", hash = "sha256:92d3ab53183d8c50f8204a51e6f91d18a15d5ef261e84d452800d4ff6fc504e9"}, + {file = "tornado-6.4.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e828cce1123e9e44ae2a50a9de3055497ab1d0aeb440c5ac23064d9e44880da1"}, + {file = "tornado-6.4.2-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:072ce12ada169c5b00b7d92a99ba089447ccc993ea2143c9ede887e0937aa803"}, + {file = "tornado-6.4.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a017d239bd1bb0919f72af256a970624241f070496635784d9bf0db640d3fec"}, + {file = "tornado-6.4.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c36e62ce8f63409301537222faffcef7dfc5284f27eec227389f2ad11b09d946"}, + {file = "tornado-6.4.2-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca9eb02196e789c9cb5c3c7c0f04fb447dc2adffd95265b2c7223a8a615ccbf"}, + {file = "tornado-6.4.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:304463bd0772442ff4d0f5149c6f1c2135a1fae045adf070821c6cdc76980634"}, + {file = "tornado-6.4.2-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:c82c46813ba483a385ab2a99caeaedf92585a1f90defb5693351fa7e4ea0bf73"}, + {file = "tornado-6.4.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:932d195ca9015956fa502c6b56af9eb06106140d844a335590c1ec7f5277d10c"}, + {file = "tornado-6.4.2-cp38-abi3-win32.whl", hash = "sha256:2876cef82e6c5978fde1e0d5b1f919d756968d5b4282418f3146b79b58556482"}, + {file = "tornado-6.4.2-cp38-abi3-win_amd64.whl", hash = "sha256:908b71bf3ff37d81073356a5fadcc660eb10c1476ee6e2725588626ce7e5ca38"}, + {file = "tornado-6.4.2.tar.gz", hash = "sha256:92bad5b4746e9879fd7bf1eb21dce4e3fc5128d71601f80005afa39237ad620b"}, ] [[package]] @@ -4694,15 +4579,37 @@ files = [ docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"] +[[package]] +name = "triton" +version = "3.1.0" +description = "A language and compiler for custom Deep Learning operations" +optional = false +python-versions = "*" +files = [ + {file = "triton-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b0dd10a925263abbe9fa37dcde67a5e9b2383fc269fdf59f5657cac38c5d1d8"}, + {file = "triton-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f34f6e7885d1bf0eaaf7ba875a5f0ce6f3c13ba98f9503651c1e6dc6757ed5c"}, + {file = "triton-3.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8182f42fd8080a7d39d666814fa36c5e30cc00ea7eeeb1a2983dbb4c99a0fdc"}, + {file = "triton-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dadaca7fc24de34e180271b5cf864c16755702e9f63a16f62df714a8099126a"}, + {file = "triton-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aafa9a20cd0d9fee523cd4504aa7131807a864cd77dcf6efe7e981f18b8c6c11"}, +] + +[package.dependencies] +filelock = "*" + +[package.extras] +build = ["cmake (>=3.20)", "lit"] +tests = ["autopep8", "flake8", "isort", "llnl-hatchet", "numpy", "pytest", "scipy (>=1.7.1)"] +tutorials = ["matplotlib", "pandas", "tabulate"] + [[package]] name = "types-python-dateutil" -version = "2.9.0.20241003" +version = "2.9.0.20241206" description = "Typing stubs for python-dateutil" optional = false python-versions = ">=3.8" files = [ - {file = "types-python-dateutil-2.9.0.20241003.tar.gz", hash = "sha256:58cb85449b2a56d6684e41aeefb4c4280631246a0da1a719bdbe6f3fb0317446"}, - {file = "types_python_dateutil-2.9.0.20241003-py3-none-any.whl", hash = "sha256:250e1d8e80e7bbc3a6c99b907762711d1a1cdd00e978ad39cb5940f6f0a87f3d"}, + {file = "types_python_dateutil-2.9.0.20241206-py3-none-any.whl", hash = "sha256:e248a4bc70a486d3e3ec84d0dc30eec3a5f979d6e7ee4123ae043eedbb987f53"}, + {file = "types_python_dateutil-2.9.0.20241206.tar.gz", hash = "sha256:18f493414c26ffba692a72369fea7a154c502646301ebfe3d56a04b3767284cb"}, ] [[package]] @@ -4760,18 +4667,18 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "waitress" -version = "3.0.1" +version = "3.0.2" description = "Waitress WSGI server" optional = false python-versions = ">=3.9.0" files = [ - {file = "waitress-3.0.1-py3-none-any.whl", hash = "sha256:26cdbc593093a15119351690752c99adc13cbc6786d75f7b6341d1234a3730ac"}, - {file = "waitress-3.0.1.tar.gz", hash = "sha256:ef0c1f020d9f12a515c4ec65c07920a702613afcad1dbfdc3bcec256b6c072b3"}, + {file = "waitress-3.0.2-py3-none-any.whl", hash = "sha256:c56d67fd6e87c2ee598b76abdd4e96cfad1f24cacdea5078d382b1f9d7b5ed2e"}, + {file = "waitress-3.0.2.tar.gz", hash = "sha256:682aaaf2af0c44ada4abfb70ded36393f0e307f4ab9456a215ce0020baefc31f"}, ] [package.extras] docs = ["Sphinx (>=1.8.1)", "docutils", "pylons-sphinx-themes (>=1.0.9)"] -testing = ["coverage (>=5.0)", "pytest", "pytest-cov"] +testing = ["coverage (>=7.6.0)", "pytest", "pytest-cov"] [[package]] name = "wcwidth" @@ -4786,19 +4693,15 @@ files = [ [[package]] name = "webcolors" -version = "24.8.0" +version = "24.11.1" description = "A library for working with the color formats defined by HTML and CSS." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "webcolors-24.8.0-py3-none-any.whl", hash = "sha256:fc4c3b59358ada164552084a8ebee637c221e4059267d0f8325b3b560f6c7f0a"}, - {file = "webcolors-24.8.0.tar.gz", hash = "sha256:08b07af286a01bcd30d583a7acadf629583d1f79bfef27dd2c2c5c263817277d"}, + {file = "webcolors-24.11.1-py3-none-any.whl", hash = "sha256:515291393b4cdf0eb19c155749a096f779f7d909f7cceea072791cb9095b92e9"}, + {file = "webcolors-24.11.1.tar.gz", hash = "sha256:ecb3d768f32202af770477b8b65f318fa4f566c22948673a977b00d589dd80f6"}, ] -[package.extras] -docs = ["furo", "sphinx", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-notfound-page", "sphinxext-opengraph"] -tests = ["coverage[toml]"] - [[package]] name = "webencodings" version = "0.5.1" @@ -4828,13 +4731,13 @@ test = ["websockets"] [[package]] name = "werkzeug" -version = "3.1.0" +version = "3.1.3" description = "The comprehensive WSGI web application library." optional = false python-versions = ">=3.9" files = [ - {file = "werkzeug-3.1.0-py3-none-any.whl", hash = "sha256:208a2e31a4a54c8b3d2244f2079ca1d3851629a7a7d546646059c64fb746023a"}, - {file = "werkzeug-3.1.0.tar.gz", hash = "sha256:6f2a0d38f25ba5a75c36c45b4ae350c7a23b57e3b974e9eb2d6851f2c648c00d"}, + {file = "werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e"}, + {file = "werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746"}, ] [package.dependencies] @@ -4843,20 +4746,6 @@ MarkupSafe = ">=2.1.1" [package.extras] watchdog = ["watchdog (>=2.3)"] -[[package]] -name = "wheel" -version = "0.44.0" -description = "A built-package format for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "wheel-0.44.0-py3-none-any.whl", hash = "sha256:2376a90c98cc337d18623527a97c31797bd02bad0033d41547043a1cbfbe448f"}, - {file = "wheel-0.44.0.tar.gz", hash = "sha256:a29c3f2817e95ab89aa4660681ad547c0e9547f20e75b0562fe7723c9a2a9d49"}, -] - -[package.extras] -test = ["pytest (>=6.0.0)", "setuptools (>=65)"] - [[package]] name = "widgetsnbextension" version = "4.0.13" @@ -4870,123 +4759,119 @@ files = [ [[package]] name = "wrapt" -version = "1.16.0" +version = "1.17.0" description = "Module for decorators, wrappers and monkey patching." optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"}, - {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"}, - {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"}, - {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"}, - {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"}, - {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"}, - {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"}, - {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"}, - {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"}, - {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"}, - {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"}, - {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"}, - {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"}, - {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"}, - {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"}, - {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"}, - {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"}, - {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"}, - {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"}, - {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"}, - {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"}, - {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"}, - {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"}, - {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"}, - {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"}, - {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"}, - {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"}, - {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"}, - {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"}, - {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"}, - {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"}, - {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"}, - {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"}, - {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"}, - {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"}, - {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"}, - {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"}, - {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"}, - {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"}, - {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"}, - {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"}, - {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"}, - {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"}, - {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"}, - {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"}, - {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"}, - {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"}, - {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"}, - {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"}, - {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"}, - {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"}, - {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"}, - {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"}, - {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"}, - {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"}, - {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"}, - {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"}, - {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"}, + {file = "wrapt-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a0c23b8319848426f305f9cb0c98a6e32ee68a36264f45948ccf8e7d2b941f8"}, + {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1ca5f060e205f72bec57faae5bd817a1560fcfc4af03f414b08fa29106b7e2d"}, + {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e185ec6060e301a7e5f8461c86fb3640a7beb1a0f0208ffde7a65ec4074931df"}, + {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb90765dd91aed05b53cd7a87bd7f5c188fcd95960914bae0d32c5e7f899719d"}, + {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:879591c2b5ab0a7184258274c42a126b74a2c3d5a329df16d69f9cee07bba6ea"}, + {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fce6fee67c318fdfb7f285c29a82d84782ae2579c0e1b385b7f36c6e8074fffb"}, + {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0698d3a86f68abc894d537887b9bbf84d29bcfbc759e23f4644be27acf6da301"}, + {file = "wrapt-1.17.0-cp310-cp310-win32.whl", hash = "sha256:69d093792dc34a9c4c8a70e4973a3361c7a7578e9cd86961b2bbf38ca71e4e22"}, + {file = "wrapt-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:f28b29dc158ca5d6ac396c8e0a2ef45c4e97bb7e65522bfc04c989e6fe814575"}, + {file = "wrapt-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:74bf625b1b4caaa7bad51d9003f8b07a468a704e0644a700e936c357c17dd45a"}, + {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f2a28eb35cf99d5f5bd12f5dd44a0f41d206db226535b37b0c60e9da162c3ed"}, + {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:81b1289e99cf4bad07c23393ab447e5e96db0ab50974a280f7954b071d41b489"}, + {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f2939cd4a2a52ca32bc0b359015718472d7f6de870760342e7ba295be9ebaf9"}, + {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6a9653131bda68a1f029c52157fd81e11f07d485df55410401f745007bd6d339"}, + {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4e4b4385363de9052dac1a67bfb535c376f3d19c238b5f36bddc95efae15e12d"}, + {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bdf62d25234290db1837875d4dceb2151e4ea7f9fff2ed41c0fde23ed542eb5b"}, + {file = "wrapt-1.17.0-cp311-cp311-win32.whl", hash = "sha256:5d8fd17635b262448ab8f99230fe4dac991af1dabdbb92f7a70a6afac8a7e346"}, + {file = "wrapt-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:92a3d214d5e53cb1db8b015f30d544bc9d3f7179a05feb8f16df713cecc2620a"}, + {file = "wrapt-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:89fc28495896097622c3fc238915c79365dd0ede02f9a82ce436b13bd0ab7569"}, + {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:875d240fdbdbe9e11f9831901fb8719da0bd4e6131f83aa9f69b96d18fae7504"}, + {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5ed16d95fd142e9c72b6c10b06514ad30e846a0d0917ab406186541fe68b451"}, + {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18b956061b8db634120b58f668592a772e87e2e78bc1f6a906cfcaa0cc7991c1"}, + {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:daba396199399ccabafbfc509037ac635a6bc18510ad1add8fd16d4739cdd106"}, + {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4d63f4d446e10ad19ed01188d6c1e1bb134cde8c18b0aa2acfd973d41fcc5ada"}, + {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8a5e7cc39a45fc430af1aefc4d77ee6bad72c5bcdb1322cfde852c15192b8bd4"}, + {file = "wrapt-1.17.0-cp312-cp312-win32.whl", hash = "sha256:0a0a1a1ec28b641f2a3a2c35cbe86c00051c04fffcfcc577ffcdd707df3f8635"}, + {file = "wrapt-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:3c34f6896a01b84bab196f7119770fd8466c8ae3dfa73c59c0bb281e7b588ce7"}, + {file = "wrapt-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:714c12485aa52efbc0fc0ade1e9ab3a70343db82627f90f2ecbc898fdf0bb181"}, + {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da427d311782324a376cacb47c1a4adc43f99fd9d996ffc1b3e8529c4074d393"}, + {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba1739fb38441a27a676f4de4123d3e858e494fac05868b7a281c0a383c098f4"}, + {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e711fc1acc7468463bc084d1b68561e40d1eaa135d8c509a65dd534403d83d7b"}, + {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:140ea00c87fafc42739bd74a94a5a9003f8e72c27c47cd4f61d8e05e6dec8721"}, + {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:73a96fd11d2b2e77d623a7f26e004cc31f131a365add1ce1ce9a19e55a1eef90"}, + {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0b48554952f0f387984da81ccfa73b62e52817a4386d070c75e4db7d43a28c4a"}, + {file = "wrapt-1.17.0-cp313-cp313-win32.whl", hash = "sha256:498fec8da10e3e62edd1e7368f4b24aa362ac0ad931e678332d1b209aec93045"}, + {file = "wrapt-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:fd136bb85f4568fffca995bd3c8d52080b1e5b225dbf1c2b17b66b4c5fa02838"}, + {file = "wrapt-1.17.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:17fcf043d0b4724858f25b8826c36e08f9fb2e475410bece0ec44a22d533da9b"}, + {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4a557d97f12813dc5e18dad9fa765ae44ddd56a672bb5de4825527c847d6379"}, + {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0229b247b0fc7dee0d36176cbb79dbaf2a9eb7ecc50ec3121f40ef443155fb1d"}, + {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8425cfce27b8b20c9b89d77fb50e368d8306a90bf2b6eef2cdf5cd5083adf83f"}, + {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9c900108df470060174108012de06d45f514aa4ec21a191e7ab42988ff42a86c"}, + {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:4e547b447073fc0dbfcbff15154c1be8823d10dab4ad401bdb1575e3fdedff1b"}, + {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:914f66f3b6fc7b915d46c1cc424bc2441841083de01b90f9e81109c9759e43ab"}, + {file = "wrapt-1.17.0-cp313-cp313t-win32.whl", hash = "sha256:a4192b45dff127c7d69b3bdfb4d3e47b64179a0b9900b6351859f3001397dabf"}, + {file = "wrapt-1.17.0-cp313-cp313t-win_amd64.whl", hash = "sha256:4f643df3d4419ea3f856c5c3f40fec1d65ea2e89ec812c83f7767c8730f9827a"}, + {file = "wrapt-1.17.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:69c40d4655e078ede067a7095544bcec5a963566e17503e75a3a3e0fe2803b13"}, + {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f495b6754358979379f84534f8dd7a43ff8cff2558dcdea4a148a6e713a758f"}, + {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:baa7ef4e0886a6f482e00d1d5bcd37c201b383f1d314643dfb0367169f94f04c"}, + {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fc931382e56627ec4acb01e09ce66e5c03c384ca52606111cee50d931a342d"}, + {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8f8909cdb9f1b237786c09a810e24ee5e15ef17019f7cecb207ce205b9b5fcce"}, + {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ad47b095f0bdc5585bced35bd088cbfe4177236c7df9984b3cc46b391cc60627"}, + {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:948a9bd0fb2c5120457b07e59c8d7210cbc8703243225dbd78f4dfc13c8d2d1f"}, + {file = "wrapt-1.17.0-cp38-cp38-win32.whl", hash = "sha256:5ae271862b2142f4bc687bdbfcc942e2473a89999a54231aa1c2c676e28f29ea"}, + {file = "wrapt-1.17.0-cp38-cp38-win_amd64.whl", hash = "sha256:f335579a1b485c834849e9075191c9898e0731af45705c2ebf70e0cd5d58beed"}, + {file = "wrapt-1.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d751300b94e35b6016d4b1e7d0e7bbc3b5e1751e2405ef908316c2a9024008a1"}, + {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7264cbb4a18dc4acfd73b63e4bcfec9c9802614572025bdd44d0721983fc1d9c"}, + {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33539c6f5b96cf0b1105a0ff4cf5db9332e773bb521cc804a90e58dc49b10578"}, + {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c30970bdee1cad6a8da2044febd824ef6dc4cc0b19e39af3085c763fdec7de33"}, + {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bc7f729a72b16ee21795a943f85c6244971724819819a41ddbaeb691b2dd85ad"}, + {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6ff02a91c4fc9b6a94e1c9c20f62ea06a7e375f42fe57587f004d1078ac86ca9"}, + {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2dfb7cff84e72e7bf975b06b4989477873dcf160b2fd89959c629535df53d4e0"}, + {file = "wrapt-1.17.0-cp39-cp39-win32.whl", hash = "sha256:2399408ac33ffd5b200480ee858baa58d77dd30e0dd0cab6a8a9547135f30a88"}, + {file = "wrapt-1.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:4f763a29ee6a20c529496a20a7bcb16a73de27f5da6a843249c7047daf135977"}, + {file = "wrapt-1.17.0-py3-none-any.whl", hash = "sha256:d2c63b93548eda58abf5188e505ffed0229bf675f7c3090f8e36ad55b8cbc371"}, + {file = "wrapt-1.17.0.tar.gz", hash = "sha256:16187aa2317c731170a88ef35e8937ae0f533c402872c1ee5e6d079fcf320801"}, ] [[package]] name = "xarray" -version = "2024.7.0" +version = "2024.11.0" description = "N-D labeled arrays and datasets in Python" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" files = [ - {file = "xarray-2024.7.0-py3-none-any.whl", hash = "sha256:1b0fd51ec408474aa1f4a355d75c00cc1c02bd425d97b2c2e551fd21810e7f64"}, - {file = "xarray-2024.7.0.tar.gz", hash = "sha256:4cae512d121a8522d41e66d942fb06c526bc1fd32c2c181d5fe62fe65b671638"}, + {file = "xarray-2024.11.0-py3-none-any.whl", hash = "sha256:6ee94f63ddcbdd0cf3909d1177f78cdac756640279c0e32ae36819a89cdaba37"}, + {file = "xarray-2024.11.0.tar.gz", hash = "sha256:1ccace44573ddb862e210ad3ec204210654d2c750bec11bbe7d842dfc298591f"}, ] [package.dependencies] -numpy = ">=1.23" -packaging = ">=23.1" -pandas = ">=2.0" +numpy = ">=1.24" +packaging = ">=23.2" +pandas = ">=2.1" [package.extras] -accel = ["bottleneck", "flox", "numbagg", "opt-einsum", "scipy"] -complete = ["xarray[accel,dev,io,parallel,viz]"] -dev = ["hypothesis", "mypy", "pre-commit", "pytest", "pytest-cov", "pytest-env", "pytest-timeout", "pytest-xdist", "ruff", "xarray[complete]"] +accel = ["bottleneck", "flox", "numba (>=0.54)", "numbagg", "opt_einsum", "scipy"] +complete = ["xarray[accel,etc,io,parallel,viz]"] +dev = ["hypothesis", "jinja2", "mypy", "pre-commit", "pytest", "pytest-cov", "pytest-env", "pytest-timeout", "pytest-xdist", "ruff", "sphinx", "sphinx_autosummary_accessors", "xarray[complete]"] +etc = ["sparse"] io = ["cftime", "fsspec", "h5netcdf", "netCDF4", "pooch", "pydap", "scipy", "zarr"] parallel = ["dask[complete]"] -viz = ["matplotlib", "nc-time-axis", "seaborn"] +viz = ["cartopy", "matplotlib", "nc-time-axis", "seaborn"] [[package]] name = "zarr" -version = "2.18.2" +version = "2.18.3" description = "An implementation of chunked, compressed, N-dimensional arrays for Python" optional = true -python-versions = ">=3.9" +python-versions = ">=3.10" files = [ - {file = "zarr-2.18.2-py3-none-any.whl", hash = "sha256:a638754902f97efa99b406083fdc807a0e2ccf12a949117389d2a4ba9b05df38"}, - {file = "zarr-2.18.2.tar.gz", hash = "sha256:9bb393b8a0a38fb121dbb913b047d75db28de9890f6d644a217a73cf4ae74f47"}, + {file = "zarr-2.18.3-py3-none-any.whl", hash = "sha256:b1f7dfd2496f436745cdd4c7bcf8d3b4bc1dceef5fdd0d589c87130d842496dd"}, + {file = "zarr-2.18.3.tar.gz", hash = "sha256:2580d8cb6dd84621771a10d31c4d777dca8a27706a1a89b29f42d2d37e2df5ce"}, ] [package.dependencies] asciitree = "*" fasteners = {version = "*", markers = "sys_platform != \"emscripten\""} numcodecs = ">=0.10.0" -numpy = ">=1.23" +numpy = ">=1.24" [package.extras] docs = ["numcodecs[msgpack]", "numpydoc", "pydata-sphinx-theme", "sphinx", "sphinx-automodapi", "sphinx-copybutton", "sphinx-design", "sphinx-issues"] @@ -4994,13 +4879,13 @@ jupyter = ["ipytree (>=0.2.2)", "ipywidgets (>=8.0.0)", "notebook"] [[package]] name = "zipp" -version = "3.20.2" +version = "3.21.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, - {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, + {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, + {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, ] [package.extras] @@ -5011,12 +4896,7 @@ enabler = ["pytest-enabler (>=2.2)"] test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] type = ["pytest-mypy"] -[extras] -extended = ["numba", "tensorflow", "zarr"] -tensorflow = ["tensorflow"] -tensorflow-cpu = ["tensorflow-cpu"] - [metadata] lock-version = "2.0" -python-versions = ">=3.9, <3.11" -content-hash = "a73fcce7ce161dfcb7576571b7d90229d2ce24af065016a80cada0eddfe8760f" +python-versions = ">=3.10, <3.11" +content-hash = "041814b095370fdfc93753c950a638e77d1b3ddc68d74c363c8f6e745912271c" diff --git a/pyproject.toml b/pyproject.toml index 3f0ed16..0c35bfd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,12 +6,11 @@ authors = ["Daniele Nerini "] include = ["LICENSE", "README.md"] [tool.poetry.dependencies] -python = ">=3.9, <3.11" +python = ">=3.10, <3.11" numpy = "*" -tensorflow = {version = "~2.16.1", optional = true} -tensorflow-cpu = {version = "~2.16.1", optional = true} -tensorflow-probability = "~0.24.0" -tf-keras = "~2.16.0" +keras = "^3.0.0" +torch = "~2.5.0" +scoringrules = "~0.7.0" pandas = "*" scipy = "*" xarray = "*" @@ -21,12 +20,9 @@ numba = {version = "*", optional = true} zarr = {version = "*", optional = true} [tool.poetry.extras] -tensorflow = ["tensorflow"] -tensorflow-cpu = ["tensorflow-cpu"] -extended = ["zarr", "numba", "tensorflow"] + [tool.poetry.group.dev.dependencies] -tensorflow = "~2.16.1" pytest = "*" black = "*" mlflow = "*" diff --git a/tests/__init__.py b/tests/__init__.py index 7c96756..a3f6fc5 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,3 +1,5 @@ import os -os.environ["TF_USE_LEGACY_KERAS"] = "1" +import os + +os.environ["KERAS_BACKEND"] = "torch" diff --git a/tests/conftest.py b/tests/conftest.py index 13be07d..09bf450 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,9 +1,8 @@ import numpy as np import pandas as pd import pytest -import tensorflow as tf import xarray as xr -from tensorflow import keras +import keras from mlpp_lib.utils import get_loss, get_model @@ -146,12 +145,12 @@ def splits_train_val() -> dict: @pytest.fixture -def get_dummy_keras_model() -> tf.keras.Model: +def get_dummy_keras_model() -> keras.Model: def _model(n_inputs, n_outpus): - inputs = tf.keras.Input(shape=(n_inputs,)) - x = tf.keras.layers.Dense(4, activation=tf.nn.relu)(inputs) - outputs = tf.keras.layers.Dense(n_outpus, activation=tf.nn.softmax)(x) - model = tf.keras.Model(inputs=inputs, outputs=outputs) + inputs = keras.Input(shape=(n_inputs,)) + x = keras.layers.Dense(4, activation=keras.ops.relu)(inputs) + outputs = keras.layers.Dense(n_outpus, activation=keras.ops.softmax)(x) + model = keras.Model(inputs=inputs, outputs=outputs) model.compile( optimizer=keras.optimizers.RMSprop(learning_rate=1e-3), loss=keras.losses.CategoricalCrossentropy(), @@ -163,7 +162,7 @@ def _model(n_inputs, n_outpus): @pytest.fixture -def get_prob_model() -> tf.keras.Model: +def get_prob_model() -> keras.Model: def _model(n_inputs, n_outputs): model_config = { "fully_connected_network": { @@ -172,7 +171,7 @@ def _model(n_inputs, n_outputs): } } model = get_model(n_inputs, n_outputs, model_config) - loss = get_loss("crps_energy") + loss = get_loss({'DistributionLossWrapper': 'scoringrules.crps_normal'}) model.compile( optimizer=keras.optimizers.RMSprop(learning_rate=0.1), loss=loss, diff --git a/tests/test_custom_distributions.py b/tests/test_custom_distributions.py new file mode 100644 index 0000000..71d54e2 --- /dev/null +++ b/tests/test_custom_distributions.py @@ -0,0 +1,53 @@ +import pytest +import torch + +from mlpp_lib.custom_distributions import TruncatedNormalDistribution, CensoredNormalDistribution + +@pytest.mark.parametrize("ab", [(4.8, 6.2), (4.0, 5.5), (4.2, 5.8)], ids=['left capped', 'right capped', 'centered']) +def test_truncated_normal(ab): + + a,b = ab + + tn = TruncatedNormalDistribution(mu_bar=torch.tensor(5.0), sigma_bar=torch.tensor(0.5), a=torch.tensor(a), b=torch.tensor(b)) + + + samples = tn.sample((50000,)) + + empirical_mean = samples.mean() + empirical_var = samples.var() + + + decimal_places = 2 + tolerance = 10**-decimal_places + + # test mean and variance + assert torch.allclose(empirical_mean, tn.mean(), atol=tolerance) + assert torch.allclose(empirical_var, tn.variance(), atol=tolerance) + + # test first moment + decimal_places = 8 + tolerance = 10**-decimal_places + assert torch.allclose(tn.mean(), tn.moment(1)) + + +@pytest.mark.parametrize("ab", [(4.8, 6.2), (4.0, 5.5), (4.2, 5.8)], ids=['left capped', 'right capped', 'centered']) +def test_censored_normal(ab): + + a,b = ab + + cn = CensoredNormalDistribution(mu_bar=torch.tensor(5.0), sigma_bar=torch.tensor(0.5), a=torch.tensor(a), b=torch.tensor(b)) + + + samples = cn.sample((50000,)) + + empirical_mean = samples.mean() + empirical_var = samples.var() + + + decimal_places = 2 + tolerance = 10**-decimal_places + + # test mean and variance + assert torch.allclose(empirical_mean, cn.mean(), atol=tolerance) + assert torch.allclose(empirical_var, cn.variance(), atol=tolerance) + diff --git a/tests/test_datasets.py b/tests/test_datasets.py index ae1c217..6f50d95 100644 --- a/tests/test_datasets.py +++ b/tests/test_datasets.py @@ -1,303 +1,303 @@ -import os -from pathlib import Path - -import pytest -import xarray as xr -import numpy as np - -from mlpp_lib.datasets import Dataset, DataModule -from mlpp_lib.model_selection import DataSplitter -from mlpp_lib.normalizers import DataTransformer -from .test_model_selection import ValidDataSplitterOptions - -ZARR_MISSING = "zarr" not in xr.backends.list_engines() - - -class TestDataModule: - - features = ["coe:x1", "obs:x3", "dem:x4"] - targets = ["obs:y1"] - batch_dims = ["forecast_reference_time", "t", "station"] - - splitter_options = ValidDataSplitterOptions(time="lists", station="lists") - splitter = DataSplitter(splitter_options.time_split, splitter_options.station_split) - - @pytest.fixture - def data_transformer(self, features_dataset): - data_transformer = DataTransformer() - data_transformer.fit(features_dataset) - return data_transformer - - @pytest.fixture # https://docs.pytest.org/en/6.2.x/tmpdir.html - def write_datasets_zarr(self, tmp_path, features_dataset, targets_dataset): - features_dataset.to_zarr(tmp_path / "features.zarr", mode="w") - targets_dataset.to_zarr(tmp_path / "targets.zarr", mode="w") - - @pytest.mark.skipif(ZARR_MISSING, reason="missing zarr") - @pytest.mark.usefixtures("write_datasets_zarr") - def test_setup_fit_default_fromfile(self, tmp_path: Path): - dm = DataModule( - self.features, - self.targets, - self.batch_dims, - self.splitter, - data_dir=tmp_path.as_posix() + "/", - ) - dm.setup("fit") - - def test_setup_fit_default_fromds(self, features_dataset, targets_dataset): - dm = DataModule( - features_dataset, - targets_dataset, - self.batch_dims, - self.splitter, - ) - dm.setup("fit") - - @pytest.mark.skipif(ZARR_MISSING, reason="missing zarr") - @pytest.mark.usefixtures("write_datasets_zarr") - def test_setup_test_default_fromfile(self, tmp_path: Path, data_transformer): - dm = DataModule( - self.features, - self.targets, - self.batch_dims, - self.splitter, - data_dir=tmp_path.as_posix() + "/", - normalizer=data_transformer, - ) - dm.setup("test") - - @pytest.mark.skipif(ZARR_MISSING, reason="missing zarr") - @pytest.mark.usefixtures("write_datasets_zarr") - def test_setup_fit_thinning(self, tmp_path: Path): - dm = DataModule( - self.features, - self.targets, - self.batch_dims, - self.splitter, - data_dir=tmp_path.as_posix() + "/", - thinning={"forecast_reference_time": 2}, - ) - dm.setup("fit") - - @pytest.mark.skipif(ZARR_MISSING, reason="missing zarr") - @pytest.mark.usefixtures("write_datasets_zarr") - def test_setup_fit_weights(self, tmp_path: Path): - dm = DataModule( - self.features, - self.targets, - self.batch_dims, - self.splitter, - data_dir=tmp_path.as_posix() + "/", - sample_weighting=["coe:x1"], - ) - dm.setup("fit") - - -class TestDataset: - @pytest.fixture - def dataset(self, features_dataset: xr.Dataset, targets_dataset: xr.Dataset): - return Dataset.from_xarray_datasets(features_dataset, targets_dataset) - - @pytest.fixture - def dataset_only_x(self, features_dataset: xr.Dataset): - return Dataset.from_xarray_datasets(features_dataset) - - @pytest.fixture - def coords(self, features_dataset): - dims = list(features_dataset.dims) - return {dim: features_dataset.coords[dim] for dim in dims} - - @pytest.fixture - def dims(self, features_dataset): - return list(features_dataset.dims) - - @pytest.fixture - def features(self, features_dataset): - return list(features_dataset.data_vars) - - @pytest.fixture - def targets(self, targets_dataset): - return list(targets_dataset.data_vars) - - def test_from_xarray_datasets(self, dataset, dims, coords, features, targets): - assert dataset.x.shape == ( - *tuple(len(c) for c in coords.values()), - len(features), - ) - assert dataset.y.shape == ( - *tuple(len(c) for c in coords.values()), - len(targets), - ) - assert dataset.dims == [*dims, "v"] - assert dataset.coords.keys() == coords.keys() - assert [len(c) for c in dataset.coords] == [len(c) for c in coords] - assert dataset.features == features - assert dataset.targets == targets - - def test_from_xarray_datasets_only_x(self, dataset_only_x, dims, coords, features): - assert dataset_only_x.x.shape == ( - *tuple(len(c) for c in coords.values()), - len(features), - ) - assert dataset_only_x.y is None - assert dataset_only_x.dims == [*dims, "v"] - assert dataset_only_x.coords.keys() == coords.keys() - assert [len(c) for c in dataset_only_x.coords] == [len(c) for c in coords] - assert dataset_only_x.features == features - - @pytest.mark.parametrize( - "batch_dims", - [ - ("forecast_reference_time", "t", "station"), - ("forecast_reference_time", "station"), - ], - ids=lambda x: repr(x), - ) - def test_stack(self, dataset, dims, coords, features, targets, batch_dims): - ds = dataset.stack(batch_dims) - event_dims = tuple(set(dims) - set(batch_dims)) - n_samples = np.prod([len(c) for d, c in coords.items() if d in batch_dims]) - assert ds.x.shape == ( - n_samples, - *tuple(len(coords[d]) for d in event_dims), - len(features), - ) - assert ds.y.shape == ( - n_samples, - *tuple(len(coords[d]) for d in event_dims), - len(targets), - ) - assert ds.dims == ["s", *event_dims, "v"] - assert ds.coords.keys() == coords.keys() - assert [len(c) for c in ds.coords] == [len(c) for c in coords] - - @pytest.mark.parametrize( - "batch_dims", - [ - ("forecast_reference_time", "t", "station"), - ("forecast_reference_time", "station"), - ], - ids=lambda x: repr(x), - ) - def test_stack_only_x(self, dataset_only_x, dims, coords, features, batch_dims): - ds = dataset_only_x.stack(batch_dims) - event_dims = tuple(set(dims) - set(batch_dims)) - n_samples = np.prod([len(c) for d, c in coords.items() if d in batch_dims]) - assert ds.x.shape == ( - n_samples, - *tuple(len(coords[d]) for d in event_dims), - len(features), - ) - assert ds.y is None - assert ds.dims == ["s", *event_dims, "v"] - assert ds.coords.keys() == coords.keys() - assert [len(c) for c in ds.coords] == [len(c) for c in coords] - - @pytest.mark.parametrize("drop_nans", [True, False], ids=lambda x: f"drop_nans={x}") - def test_unstack(self, dataset, dims, coords, features, targets, drop_nans): - batch_dims = ("forecast_reference_time", "t", "station") - if drop_nans: - ds = dataset.stack(batch_dims).drop_nans() - else: - ds = dataset.stack(batch_dims) - - ds = ds.unstack() - assert ds.x.shape == (*tuple(len(c) for c in coords.values()), len(features)) - assert ds.y.shape == (*tuple(len(c) for c in coords.values()), len(targets)) - assert ds.dims == (*dims, "v") - assert ds.coords.keys() == coords.keys() - assert [len(c) for c in ds.coords] == [len(c) for c in coords] - assert ds.features == features - assert ds.targets == targets - - @pytest.mark.parametrize("drop_nans", [True, False], ids=lambda x: f"drop_nans={x}") - def test_unstack_only_x(self, dataset_only_x, dims, coords, features, drop_nans): - batch_dims = ("forecast_reference_time", "t", "station") - if drop_nans: - ds = dataset_only_x.stack(batch_dims).drop_nans() - else: - ds = dataset_only_x.stack(batch_dims) - - ds = ds.unstack() - assert ds.x.shape == (*tuple(len(c) for c in coords.values()), len(features)) - assert ds.y is None - assert ds.dims == (*dims, "v") - assert ds.coords.keys() == coords.keys() - assert [len(c) for c in ds.coords] == [len(c) for c in coords] - assert ds.features == features - - def test_drop_nans(self, dataset, dims, coords, features, targets): - batch_dims = ("forecast_reference_time", "t", "station") - ds = dataset.stack(batch_dims).drop_nans() - event_dims = tuple(set(dims) - set(batch_dims)) - n_samples = np.prod([len(c) for d, c in coords.items() if d in batch_dims]) - assert ds.x.shape == ( - ds.mask.sum(), - *tuple(len(coords[d]) for d in event_dims), - len(features), - ) - assert ds.y.shape == ( - ds.mask.sum(), - *tuple(len(coords[d]) for d in event_dims), - len(targets), - ) - assert len(ds.mask) == n_samples - assert ds.dims == ["s", *event_dims, "v"] - assert ds.coords.keys() == coords.keys() - - def test_drop_nans_only_x(self, dataset_only_x, dims, coords, features): - batch_dims = ("forecast_reference_time", "t", "station") - ds = dataset_only_x.stack(batch_dims).drop_nans() - event_dims = tuple(set(dims) - set(batch_dims)) - n_samples = np.prod([len(c) for d, c in coords.items() if d in batch_dims]) - assert ds.x.shape == ( - ds.mask.sum(), - *tuple(len(coords[d]) for d in event_dims), - len(features), - ) - assert ds.y is None - assert len(ds.mask) == n_samples - assert ds.dims == ["s", *event_dims, "v"] - assert ds.coords.keys() == coords.keys() - - @pytest.mark.parametrize( - "batch_dims", - [ - ("forecast_reference_time", "t", "station"), - ("forecast_reference_time", "station"), - ], - ids=lambda x: repr(x), - ) - def test_dataset_from_predictions(self, dataset, batch_dims): - n_samples = 3 - ds = dataset.stack(batch_dims) - ds = ds.drop_nans() - predictions = np.random.randn(n_samples, *ds.y.shape) - ds_pred = ds.dataset_from_predictions(predictions, ensemble_axis=0) - assert isinstance(ds_pred, xr.Dataset) - assert ds_pred.sizes["realization"] == n_samples - assert all([ds_pred.sizes[c] == ds.coords[c].size for c in ds.coords]) - assert list(ds_pred.data_vars) == ds.targets - - @pytest.mark.parametrize( - "batch_dims", - [ - ("forecast_reference_time", "t", "station"), - ("forecast_reference_time", "station"), - ], - ids=lambda x: repr(x), - ) - def test_dataset_from_predictions_only_x(self, dataset_only_x, batch_dims): - n_samples = 3 - targets = ["obs:y1", "obs:y2"] - ds = dataset_only_x.stack(batch_dims) - # Note that here we do not drop nan, hence the mask is not created! - predictions = np.random.randn(n_samples, *ds.x.shape[:-1], len(targets)) - ds_pred = ds.dataset_from_predictions( - predictions, ensemble_axis=0, targets=targets - ) - assert isinstance(ds_pred, xr.Dataset) - assert ds_pred.sizes["realization"] == n_samples - assert all([ds_pred.sizes[c] == ds.coords[c].size for c in ds.coords]) - assert list(ds_pred.data_vars) == targets +# import os +# from pathlib import Path + +# import pytest +# import xarray as xr +# import numpy as np + +# from mlpp_lib.datasets import Dataset, DataModule +# from mlpp_lib.model_selection import DataSplitter +# from mlpp_lib.normalizers import DataTransformer +# from .test_model_selection import ValidDataSplitterOptions + +# ZARR_MISSING = "zarr" not in xr.backends.list_engines() + + +# class TestDataModule: + +# features = ["coe:x1", "obs:x3", "dem:x4"] +# targets = ["obs:y1"] +# batch_dims = ["forecast_reference_time", "t", "station"] + +# splitter_options = ValidDataSplitterOptions(time="lists", station="lists") +# splitter = DataSplitter(splitter_options.time_split, splitter_options.station_split) + +# @pytest.fixture +# def data_transformer(self, features_dataset): +# data_transformer = DataTransformer() +# data_transformer.fit(features_dataset) +# return data_transformer + +# @pytest.fixture # https://docs.pytest.org/en/6.2.x/tmpdir.html +# def write_datasets_zarr(self, tmp_path, features_dataset, targets_dataset): +# features_dataset.to_zarr(tmp_path / "features.zarr", mode="w") +# targets_dataset.to_zarr(tmp_path / "targets.zarr", mode="w") + +# @pytest.mark.skipif(ZARR_MISSING, reason="missing zarr") +# @pytest.mark.usefixtures("write_datasets_zarr") +# def test_setup_fit_default_fromfile(self, tmp_path: Path): +# dm = DataModule( +# self.features, +# self.targets, +# self.batch_dims, +# self.splitter, +# data_dir=tmp_path.as_posix() + "/", +# ) +# dm.setup("fit") + +# def test_setup_fit_default_fromds(self, features_dataset, targets_dataset): +# dm = DataModule( +# features_dataset, +# targets_dataset, +# self.batch_dims, +# self.splitter, +# ) +# dm.setup("fit") + +# @pytest.mark.skipif(ZARR_MISSING, reason="missing zarr") +# @pytest.mark.usefixtures("write_datasets_zarr") +# def test_setup_test_default_fromfile(self, tmp_path: Path, data_transformer): +# dm = DataModule( +# self.features, +# self.targets, +# self.batch_dims, +# self.splitter, +# data_dir=tmp_path.as_posix() + "/", +# normalizer=data_transformer, +# ) +# dm.setup("test") + +# @pytest.mark.skipif(ZARR_MISSING, reason="missing zarr") +# @pytest.mark.usefixtures("write_datasets_zarr") +# def test_setup_fit_thinning(self, tmp_path: Path): +# dm = DataModule( +# self.features, +# self.targets, +# self.batch_dims, +# self.splitter, +# data_dir=tmp_path.as_posix() + "/", +# thinning={"forecast_reference_time": 2}, +# ) +# dm.setup("fit") + +# @pytest.mark.skipif(ZARR_MISSING, reason="missing zarr") +# @pytest.mark.usefixtures("write_datasets_zarr") +# def test_setup_fit_weights(self, tmp_path: Path): +# dm = DataModule( +# self.features, +# self.targets, +# self.batch_dims, +# self.splitter, +# data_dir=tmp_path.as_posix() + "/", +# sample_weighting=["coe:x1"], +# ) +# dm.setup("fit") + + +# class TestDataset: +# @pytest.fixture +# def dataset(self, features_dataset: xr.Dataset, targets_dataset: xr.Dataset): +# return Dataset.from_xarray_datasets(features_dataset, targets_dataset) + +# @pytest.fixture +# def dataset_only_x(self, features_dataset: xr.Dataset): +# return Dataset.from_xarray_datasets(features_dataset) + +# @pytest.fixture +# def coords(self, features_dataset): +# dims = list(features_dataset.dims) +# return {dim: features_dataset.coords[dim] for dim in dims} + +# @pytest.fixture +# def dims(self, features_dataset): +# return list(features_dataset.dims) + +# @pytest.fixture +# def features(self, features_dataset): +# return list(features_dataset.data_vars) + +# @pytest.fixture +# def targets(self, targets_dataset): +# return list(targets_dataset.data_vars) + +# def test_from_xarray_datasets(self, dataset, dims, coords, features, targets): +# assert dataset.x.shape == ( +# *tuple(len(c) for c in coords.values()), +# len(features), +# ) +# assert dataset.y.shape == ( +# *tuple(len(c) for c in coords.values()), +# len(targets), +# ) +# assert dataset.dims == [*dims, "v"] +# assert dataset.coords.keys() == coords.keys() +# assert [len(c) for c in dataset.coords] == [len(c) for c in coords] +# assert dataset.features == features +# assert dataset.targets == targets + +# def test_from_xarray_datasets_only_x(self, dataset_only_x, dims, coords, features): +# assert dataset_only_x.x.shape == ( +# *tuple(len(c) for c in coords.values()), +# len(features), +# ) +# assert dataset_only_x.y is None +# assert dataset_only_x.dims == [*dims, "v"] +# assert dataset_only_x.coords.keys() == coords.keys() +# assert [len(c) for c in dataset_only_x.coords] == [len(c) for c in coords] +# assert dataset_only_x.features == features + +# @pytest.mark.parametrize( +# "batch_dims", +# [ +# ("forecast_reference_time", "t", "station"), +# ("forecast_reference_time", "station"), +# ], +# ids=lambda x: repr(x), +# ) +# def test_stack(self, dataset, dims, coords, features, targets, batch_dims): +# ds = dataset.stack(batch_dims) +# event_dims = tuple(set(dims) - set(batch_dims)) +# n_samples = np.prod([len(c) for d, c in coords.items() if d in batch_dims]) +# assert ds.x.shape == ( +# n_samples, +# *tuple(len(coords[d]) for d in event_dims), +# len(features), +# ) +# assert ds.y.shape == ( +# n_samples, +# *tuple(len(coords[d]) for d in event_dims), +# len(targets), +# ) +# assert ds.dims == ["s", *event_dims, "v"] +# assert ds.coords.keys() == coords.keys() +# assert [len(c) for c in ds.coords] == [len(c) for c in coords] + +# @pytest.mark.parametrize( +# "batch_dims", +# [ +# ("forecast_reference_time", "t", "station"), +# ("forecast_reference_time", "station"), +# ], +# ids=lambda x: repr(x), +# ) +# def test_stack_only_x(self, dataset_only_x, dims, coords, features, batch_dims): +# ds = dataset_only_x.stack(batch_dims) +# event_dims = tuple(set(dims) - set(batch_dims)) +# n_samples = np.prod([len(c) for d, c in coords.items() if d in batch_dims]) +# assert ds.x.shape == ( +# n_samples, +# *tuple(len(coords[d]) for d in event_dims), +# len(features), +# ) +# assert ds.y is None +# assert ds.dims == ["s", *event_dims, "v"] +# assert ds.coords.keys() == coords.keys() +# assert [len(c) for c in ds.coords] == [len(c) for c in coords] + +# @pytest.mark.parametrize("drop_nans", [True, False], ids=lambda x: f"drop_nans={x}") +# def test_unstack(self, dataset, dims, coords, features, targets, drop_nans): +# batch_dims = ("forecast_reference_time", "t", "station") +# if drop_nans: +# ds = dataset.stack(batch_dims).drop_nans() +# else: +# ds = dataset.stack(batch_dims) + +# ds = ds.unstack() +# assert ds.x.shape == (*tuple(len(c) for c in coords.values()), len(features)) +# assert ds.y.shape == (*tuple(len(c) for c in coords.values()), len(targets)) +# assert ds.dims == (*dims, "v") +# assert ds.coords.keys() == coords.keys() +# assert [len(c) for c in ds.coords] == [len(c) for c in coords] +# assert ds.features == features +# assert ds.targets == targets + +# @pytest.mark.parametrize("drop_nans", [True, False], ids=lambda x: f"drop_nans={x}") +# def test_unstack_only_x(self, dataset_only_x, dims, coords, features, drop_nans): +# batch_dims = ("forecast_reference_time", "t", "station") +# if drop_nans: +# ds = dataset_only_x.stack(batch_dims).drop_nans() +# else: +# ds = dataset_only_x.stack(batch_dims) + +# ds = ds.unstack() +# assert ds.x.shape == (*tuple(len(c) for c in coords.values()), len(features)) +# assert ds.y is None +# assert ds.dims == (*dims, "v") +# assert ds.coords.keys() == coords.keys() +# assert [len(c) for c in ds.coords] == [len(c) for c in coords] +# assert ds.features == features + +# def test_drop_nans(self, dataset, dims, coords, features, targets): +# batch_dims = ("forecast_reference_time", "t", "station") +# ds = dataset.stack(batch_dims).drop_nans() +# event_dims = tuple(set(dims) - set(batch_dims)) +# n_samples = np.prod([len(c) for d, c in coords.items() if d in batch_dims]) +# assert ds.x.shape == ( +# ds.mask.sum(), +# *tuple(len(coords[d]) for d in event_dims), +# len(features), +# ) +# assert ds.y.shape == ( +# ds.mask.sum(), +# *tuple(len(coords[d]) for d in event_dims), +# len(targets), +# ) +# assert len(ds.mask) == n_samples +# assert ds.dims == ["s", *event_dims, "v"] +# assert ds.coords.keys() == coords.keys() + +# def test_drop_nans_only_x(self, dataset_only_x, dims, coords, features): +# batch_dims = ("forecast_reference_time", "t", "station") +# ds = dataset_only_x.stack(batch_dims).drop_nans() +# event_dims = tuple(set(dims) - set(batch_dims)) +# n_samples = np.prod([len(c) for d, c in coords.items() if d in batch_dims]) +# assert ds.x.shape == ( +# ds.mask.sum(), +# *tuple(len(coords[d]) for d in event_dims), +# len(features), +# ) +# assert ds.y is None +# assert len(ds.mask) == n_samples +# assert ds.dims == ["s", *event_dims, "v"] +# assert ds.coords.keys() == coords.keys() + +# @pytest.mark.parametrize( +# "batch_dims", +# [ +# ("forecast_reference_time", "t", "station"), +# ("forecast_reference_time", "station"), +# ], +# ids=lambda x: repr(x), +# ) +# def test_dataset_from_predictions(self, dataset, batch_dims): +# n_samples = 3 +# ds = dataset.stack(batch_dims) +# ds = ds.drop_nans() +# predictions = np.random.randn(n_samples, *ds.y.shape) +# ds_pred = ds.dataset_from_predictions(predictions, ensemble_axis=0) +# assert isinstance(ds_pred, xr.Dataset) +# assert ds_pred.sizes["realization"] == n_samples +# assert all([ds_pred.sizes[c] == ds.coords[c].size for c in ds.coords]) +# assert list(ds_pred.data_vars) == ds.targets + +# @pytest.mark.parametrize( +# "batch_dims", +# [ +# ("forecast_reference_time", "t", "station"), +# ("forecast_reference_time", "station"), +# ], +# ids=lambda x: repr(x), +# ) +# def test_dataset_from_predictions_only_x(self, dataset_only_x, batch_dims): +# n_samples = 3 +# targets = ["obs:y1", "obs:y2"] +# ds = dataset_only_x.stack(batch_dims) +# # Note that here we do not drop nan, hence the mask is not created! +# predictions = np.random.randn(n_samples, *ds.x.shape[:-1], len(targets)) +# ds_pred = ds.dataset_from_predictions( +# predictions, ensemble_axis=0, targets=targets +# ) +# assert isinstance(ds_pred, xr.Dataset) +# assert ds_pred.sizes["realization"] == n_samples +# assert all([ds_pred.sizes[c] == ds.coords[c].size for c in ds.coords]) +# assert list(ds_pred.data_vars) == targets diff --git a/tests/test_ensemble.py b/tests/test_ensemble.py index 7da144d..cf4e252 100644 --- a/tests/test_ensemble.py +++ b/tests/test_ensemble.py @@ -1,45 +1,45 @@ -import numpy as np -import numpy as np -import xarray as xr - -import mlpp_lib.ensemble as ens - - -def test_sortby(): - """For a standard case, sortby should return the same results as numpy sort.""" - test_ds = xr.DataArray(np.random.random((3, 3, 3)), dims=("a", "b", "c")) - out = ens.sortby(test_ds, "a") - assert isinstance(out, xr.DataArray) - assert out.dims[0] == "rank" - ref = np.sort(test_ds, axis=0) - assert np.allclose(out, ref) - - -def test_equidistant_resampling(): - test_ds = xr.DataArray( - np.random.random((3, 20)), dims=("a", "realization") - ).to_dataset(name="var") - out = ens.equidistant_resampling(test_ds, 5) - assert isinstance(out, xr.Dataset) - assert out.sizes["realization"] == 5 - - -def test_equidistant_resampling_circular(): - test_ds = xr.DataArray( - np.random.randint(0, 360, (3, 20)), dims=("a", "realization") - ).to_dataset(name="var_direction") - out = ens.equidistant_resampling(test_ds, 5) - assert isinstance(out, xr.Dataset) - assert out.sizes["realization"] == 5 - - -def test_compute_ecc(): - test_ds = xr.DataArray( - np.random.random((3, 10)), dims=("a", "realization") - ).to_dataset(name="var") - test_template = xr.DataArray( - np.random.randint(1, 5, (3, 5)), dims=("a", "realization") - ) - out = ens.compute_ecc(test_ds, test_template) - assert isinstance(out, xr.Dataset) - assert out.sizes["realization"] == 5 +# import numpy as np +# import numpy as np +# import xarray as xr + +# import mlpp_lib.ensemble as ens + + +# def test_sortby(): +# """For a standard case, sortby should return the same results as numpy sort.""" +# test_ds = xr.DataArray(np.random.random((3, 3, 3)), dims=("a", "b", "c")) +# out = ens.sortby(test_ds, "a") +# assert isinstance(out, xr.DataArray) +# assert out.dims[0] == "rank" +# ref = np.sort(test_ds, axis=0) +# assert np.allclose(out, ref) + + +# def test_equidistant_resampling(): +# test_ds = xr.DataArray( +# np.random.random((3, 20)), dims=("a", "realization") +# ).to_dataset(name="var") +# out = ens.equidistant_resampling(test_ds, 5) +# assert isinstance(out, xr.Dataset) +# assert out.sizes["realization"] == 5 + + +# def test_equidistant_resampling_circular(): +# test_ds = xr.DataArray( +# np.random.randint(0, 360, (3, 20)), dims=("a", "realization") +# ).to_dataset(name="var_direction") +# out = ens.equidistant_resampling(test_ds, 5) +# assert isinstance(out, xr.Dataset) +# assert out.sizes["realization"] == 5 + + +# def test_compute_ecc(): +# test_ds = xr.DataArray( +# np.random.random((3, 10)), dims=("a", "realization") +# ).to_dataset(name="var") +# test_template = xr.DataArray( +# np.random.randint(1, 5, (3, 5)), dims=("a", "realization") +# ) +# out = ens.compute_ecc(test_ds, test_template) +# assert isinstance(out, xr.Dataset) +# assert out.sizes["realization"] == 5 diff --git a/tests/test_losses.py b/tests/test_losses.py index 2773312..ec7b66a 100644 --- a/tests/test_losses.py +++ b/tests/test_losses.py @@ -1,250 +1,295 @@ +import torch from inspect import getmembers, isclass - -import numpy as np -import pytest -import tensorflow as tf -from tensorflow_probability import distributions as tfd - -from mlpp_lib import losses from mlpp_lib import probabilistic_layers - - -LAYERS = [obj[0] for obj in getmembers(probabilistic_layers, isclass)] +from mlpp_lib.losses import DistributionLossWrapper, SampleLossWrapper +from mlpp_lib.probabilistic_layers import BaseParametricDistributionModule, UniveriateGaussianModule, WrappingTorchDist +import scoringrules as sr +import numpy as np +import keras + +DISTRIBUTIONS = [obj[0] for obj in getmembers(probabilistic_layers, isclass) + if issubclass(obj[1], BaseParametricDistributionModule) and obj[0] != 'BaseParametricDistribution'] + +def test_scoringrules_crps_normal(): + mu, sigma = torch.randn(32,1), torch.ones(32,1) + y_pred = WrappingTorchDist(torch.distributions.Normal(mu, sigma)) + y_true = torch.randn(32,1) + loss_fn = DistributionLossWrapper(fn=sr.crps_normal) + + loss = loss_fn(y_true, y_pred).item() + + assert np.isclose(loss, crps_closed_form_gaussian(y_true, mu, sigma).mean(), atol=1e-4) + + +def test_scoringrules_crps_ensamble_normal(): + + mu, sigma = torch.randn(32,1), torch.ones(32,1) + + crps_ens = SampleLossWrapper(fn=sr.crps_ensemble, num_samples=1000, estimator='nrg') + + normal = UniveriateGaussianModule() + dist = normal.process_params(moments=torch.cat([mu, sigma], dim=-1)) + + # internally applies softplus, must retrieve it. + # the mu-sigma passed are not the true mean and variance used by the model. + # they undergo constraints. + sigma_softplus = dist.scale + y_true = torch.randn(32,1) + + loss = crps_ens(y_true=y_true, y_pred=dist) + + assert np.isclose(loss, crps_closed_form_gaussian(y_true, mu, sigma_softplus).mean(), atol=1e-2) + + + + +# from inspect import getmembers, isclass + +# import numpy as np +# import pytest +# import tensorflow as tf +# from tensorflow_probability import distributions as tfd + +# from mlpp_lib import losses +# from mlpp_lib import probabilistic_layers + + +# LAYERS = [obj[0] for obj in getmembers(probabilistic_layers, isclass)] def crps_closed_form_gaussian(obs, mu, sigma): loc = (obs - mu) / sigma - phi = 1.0 / np.sqrt(2.0 * np.pi) * tf.math.exp(-tf.math.square(loc) / 2.0) - Phi = 0.5 * (1.0 + tf.math.erf(loc / np.sqrt(2.0))) - crps_closed_form = tf.math.sqrt(tf.math.square(sigma)) * ( - loc * (2.0 * Phi - 1.0) + 2 * phi - 1.0 / tf.math.sqrt(np.pi) + phi = 1.0 / np.sqrt(2.0 * np.pi) * keras.ops.exp(-keras.ops.square(loc) / 2.0) + Phi = 0.5 * (1.0 + keras.ops.erf(loc / np.sqrt(2.0))) + crps_closed_form = keras.ops.sqrt(keras.ops.square(sigma)) * ( + loc * (2.0 * Phi - 1.0) + 2 * phi - 1.0 / keras.ops.sqrt(np.pi) ) return crps_closed_form -def test_crps_energy(): - batch_size = 100 - tf.random.set_seed(1234) - mu = tf.zeros((batch_size, 1)) - sigma = tf.ones((batch_size, 1)) - fct_dist = tfd.Normal(loc=mu, scale=sigma) - fct_dist = tfd.Independent(fct_dist, reinterpreted_batch_ndims=1) - fct_dist.shape = fct_dist.batch_shape + fct_dist.event_shape - obs = tf.zeros((batch_size, 1)) - - result = losses.crps_energy(obs, fct_dist) - good_result = crps_closed_form_gaussian(obs, mu, sigma) - - np.testing.assert_allclose( - tf.reduce_mean(result), tf.reduce_mean(good_result), atol=1e-2 - ) - - -def test_crps_energy_ensemble(): - batch_size = 100 - tf.random.set_seed(1234) - mu = tf.zeros((batch_size, 1)) - sigma = tf.ones((batch_size, 1)) - fct_dist = tfd.Normal(loc=mu, scale=sigma) - fct_ensemble = fct_dist.sample(1000) - obs = tf.zeros((batch_size, 1)) - - result = losses.crps_energy_ensemble(obs, fct_ensemble) - good_result = crps_closed_form_gaussian(obs, mu, sigma) - - np.testing.assert_allclose( - tf.reduce_mean(result), tf.reduce_mean(good_result), atol=1e-2 - ) - - -@pytest.mark.parametrize("layer", LAYERS) -def test_weighted_crps_layers(layer): - event_shape = (1,) - batch_shape = (10,) - event_size = event_shape[0] - layer_class = getattr(probabilistic_layers, layer) - prob_layer = layer_class(event_size) - y_pred_dist = prob_layer( - np.random.random(batch_shape + (layer_class.params_size(event_size),)) - ) - loss = losses.WeightedCRPSEnergy(threshold=0, reduction="none") - result = loss(tf.zeros(batch_shape + event_shape), y_pred_dist) - assert result.shape == batch_shape + event_shape - - -def test_weighted_crps_dtypes(): - """Test various input data types""" - - tf.random.set_seed(1234) - loss = losses.WeightedCRPSEnergy(threshold=1) - y_pred_dist = tfd.Normal(loc=tf.zeros((3, 1)), scale=tf.ones((3, 1))) - y_pred_ens = y_pred_dist.sample(100) - y_true = tf.zeros((3, 1)) - - # prediction is TFP distribution - tf.random.set_seed(42) - result = loss(y_true, y_pred_dist) - assert tf.is_tensor(result) - assert result.dtype == "float32" - tf.random.set_seed(42) - np.testing.assert_allclose(result, loss(y_true.numpy(), y_pred_dist)) - - # prediction is TF tensor - result = loss(y_true, y_pred_ens) - assert tf.is_tensor(result) - assert result.dtype == "float32" - np.testing.assert_allclose(result, loss(y_true.numpy(), y_pred_ens)) - - # prediction is numpy array - result = loss(y_true, y_pred_ens.numpy()) - assert tf.is_tensor(result) - assert result.dtype == "float32" - np.testing.assert_allclose(result, loss(y_true.numpy(), y_pred_ens.numpy())) - - -def test_weighted_crps_high_threshold(): - """Using a very large threshold should set the loss to zero""" - tf.random.set_seed(1234) - loss = losses.WeightedCRPSEnergy(threshold=1e6) - fct_dist = tfd.Normal(loc=tf.zeros((3, 1)), scale=tf.ones((3, 1))) - obs = tf.zeros((3, 1)) - result = loss(obs, fct_dist) - assert result == 1e-7 - - -def test_weighted_crps_no_reduction(): - """Passing reduction='none' should return a loss value per sample""" - tf.random.set_seed(1234) - loss = losses.WeightedCRPSEnergy(threshold=0, reduction="none") - fct_dist = tfd.Normal(loc=tf.zeros((3, 1)), scale=tf.ones((3, 1))) - obs = tf.zeros((3, 1)) - result = loss(obs, fct_dist) - assert result.shape == obs.shape - - -def test_weighted_crps_zero_sample_weights(): - """Passing an array of all zeros as sample weights set the total loss to zero""" - tf.random.set_seed(1234) - loss = losses.WeightedCRPSEnergy(threshold=0) - fct_dist = tfd.Normal(loc=tf.zeros((3, 1)), scale=tf.ones((3, 1))) - obs = tf.zeros((3, 1)) - sample_weights = tf.zeros((3, 1)) - result = loss(obs, fct_dist, sample_weight=sample_weights) - assert result == 0 - - -def test_multiscale_crps_layer(): - event_shape = (1,) - batch_shape = (10,) - event_size = event_shape[0] - layer_class = getattr(probabilistic_layers, "IndependentGamma") - prob_layer = layer_class(event_size) - y_pred_dist = prob_layer( - np.random.random(batch_shape + (layer_class.params_size(event_size),)) - ) - loss = losses.MultiScaleCRPSEnergy(threshold=0, scales=[1, 2], reduction="none") - result = loss(tf.zeros(batch_shape + event_shape), y_pred_dist) - assert result.shape == batch_shape + event_shape - - -def test_multiscale_crps_array(): - event_shape = (1,) - batch_shape = (10,) - event_size = event_shape[0] - layer_class = getattr(probabilistic_layers, "IndependentGamma") - prob_layer = layer_class(event_size) - y_pred_dist = prob_layer( - np.random.random(batch_shape + (layer_class.params_size(event_size),)) - ) - y_pred = y_pred_dist.sample(3) - loss = losses.MultiScaleCRPSEnergy(threshold=0, scales=[1, 2], reduction="none") - result = loss(tf.zeros(batch_shape + event_shape), y_pred) - assert result.shape == batch_shape + event_shape - - -def test_energy_score(): - n_events, n_dims = 10, 3 - loss = losses.EnergyScore(reduction=tf.keras.losses.Reduction.NONE) - fct_dist = tfd.MultivariateNormalDiag( - loc=tf.zeros((n_events, n_dims)), - scale_diag=tf.ones((n_events, n_dims)), - ) - obs = tf.zeros((n_events, n_dims)) - result = loss(obs, fct_dist) - assert tf.is_tensor(result) - assert result.dtype == "float32" - assert result.shape == obs.shape[0] - - -@pytest.mark.parametrize( - "metric, scaling, weights", - ( - ["mae", "standard", None], - ["crps_energy", "minmax", None], - ["mse", None, [1.0, 1.5]], - ), -) -def test_multivariate_loss(metric, scaling, weights): - - tf.random.set_seed(1234) - loss = losses.MultivariateLoss(metric, scaling, weights) - - if getattr(loss.metric, "loss_type", None) == "probabilistic": - dist = tfd.Normal(loc=tf.zeros((3, 2)), scale=tf.ones((3, 2))) - fct = tfd.Independent(dist, reinterpreted_batch_ndims=1) - fct.shape = (*fct.batch_shape, *fct.event_shape) - else: - fct = tf.random.normal((3, 2)) - - obs = tf.random.normal((3, 2)) - - result = loss(obs, fct).numpy() - - assert isinstance(result, np.float32) - - -def test_binary_loss_dtypes(): - """Test various input data types""" - tf.random.set_seed(1234) - loss = losses.BinaryClassifierLoss(threshold=1) - y_pred_dist = tfd.Normal(loc=tf.zeros((3, 1)), scale=tf.ones((3, 1))) - y_pred_ens = y_pred_dist.sample(100) - y_true = tf.zeros((3, 1)) - - # prediction is TFP distribution - tf.random.set_seed(42) - result = loss(y_true, y_pred_dist) - assert tf.is_tensor(result) - assert result.dtype == "float32" - tf.random.set_seed(42) - np.testing.assert_allclose(result, loss(y_true.numpy(), y_pred_dist)) - - # prediction is TF tensor - result = loss(y_true, y_pred_ens) - assert tf.is_tensor(result) - assert result.dtype == "float32" - np.testing.assert_allclose(result, loss(y_true.numpy(), y_pred_ens)) - - # prediction is numpy array - result = loss(y_true, y_pred_ens.numpy()) - assert tf.is_tensor(result) - assert result.dtype == "float32" - np.testing.assert_allclose(result, loss(y_true.numpy(), y_pred_ens.numpy())) - - -def test_combined_loss(): - """""" - loss_specs = [ - {"BinaryClassifierLoss": {"threshold": 1}, "weight": 0.7}, - {"WeightedCRPSEnergy": {"threshold": 0.1}, "weight": 0.1}, - ] - - combined_loss = losses.CombinedLoss(loss_specs) - y_pred_dist = tfd.Normal(loc=tf.zeros((3, 1)), scale=tf.ones((3, 1))) - y_true = tf.zeros((3, 1)) - - # prediction is TFP distribution - tf.random.set_seed(42) - result = combined_loss(y_true, y_pred_dist) - assert tf.is_tensor(result) - assert result.dtype == "float32" +# def test_crps_energy(): +# batch_size = 100 +# tf.random.set_seed(1234) +# mu = tf.zeros((batch_size, 1)) +# sigma = tf.ones((batch_size, 1)) +# fct_dist = tfd.Normal(loc=mu, scale=sigma) +# fct_dist = tfd.Independent(fct_dist, reinterpreted_batch_ndims=1) +# fct_dist.shape = fct_dist.batch_shape + fct_dist.event_shape +# obs = tf.zeros((batch_size, 1)) + +# result = losses.crps_energy(obs, fct_dist) +# good_result = crps_closed_form_gaussian(obs, mu, sigma) + +# np.testing.assert_allclose( +# tf.reduce_mean(result), tf.reduce_mean(good_result), atol=1e-2 +# ) + + +# def test_crps_energy_ensemble(): +# batch_size = 100 +# tf.random.set_seed(1234) +# mu = tf.zeros((batch_size, 1)) +# sigma = tf.ones((batch_size, 1)) +# fct_dist = tfd.Normal(loc=mu, scale=sigma) +# fct_ensemble = fct_dist.sample(1000) +# obs = tf.zeros((batch_size, 1)) + +# result = losses.crps_energy_ensemble(obs, fct_ensemble) +# good_result = crps_closed_form_gaussian(obs, mu, sigma) + +# np.testing.assert_allclose( +# tf.reduce_mean(result), tf.reduce_mean(good_result), atol=1e-2 +# ) + + +# @pytest.mark.parametrize("layer", LAYERS) +# def test_weighted_crps_layers(layer): +# event_shape = (1,) +# batch_shape = (10,) +# event_size = event_shape[0] +# layer_class = getattr(probabilistic_layers, layer) +# prob_layer = layer_class(event_size) +# y_pred_dist = prob_layer( +# np.random.random(batch_shape + (layer_class.params_size(event_size),)) +# ) +# loss = losses.WeightedCRPSEnergy(threshold=0, reduction="none") +# result = loss(tf.zeros(batch_shape + event_shape), y_pred_dist) +# assert result.shape == batch_shape + event_shape + + +# def test_weighted_crps_dtypes(): +# """Test various input data types""" + +# tf.random.set_seed(1234) +# loss = losses.WeightedCRPSEnergy(threshold=1) +# y_pred_dist = tfd.Normal(loc=tf.zeros((3, 1)), scale=tf.ones((3, 1))) +# y_pred_ens = y_pred_dist.sample(100) +# y_true = tf.zeros((3, 1)) + +# # prediction is TFP distribution +# tf.random.set_seed(42) +# result = loss(y_true, y_pred_dist) +# assert tf.is_tensor(result) +# assert result.dtype == "float32" +# tf.random.set_seed(42) +# np.testing.assert_allclose(result, loss(y_true.numpy(), y_pred_dist)) + +# # prediction is TF tensor +# result = loss(y_true, y_pred_ens) +# assert tf.is_tensor(result) +# assert result.dtype == "float32" +# np.testing.assert_allclose(result, loss(y_true.numpy(), y_pred_ens)) + +# # prediction is numpy array +# result = loss(y_true, y_pred_ens.numpy()) +# assert tf.is_tensor(result) +# assert result.dtype == "float32" +# np.testing.assert_allclose(result, loss(y_true.numpy(), y_pred_ens.numpy())) + + +# def test_weighted_crps_high_threshold(): +# """Using a very large threshold should set the loss to zero""" +# tf.random.set_seed(1234) +# loss = losses.WeightedCRPSEnergy(threshold=1e6) +# fct_dist = tfd.Normal(loc=tf.zeros((3, 1)), scale=tf.ones((3, 1))) +# obs = tf.zeros((3, 1)) +# result = loss(obs, fct_dist) +# assert result == 1e-7 + + +# def test_weighted_crps_no_reduction(): +# """Passing reduction='none' should return a loss value per sample""" +# tf.random.set_seed(1234) +# loss = losses.WeightedCRPSEnergy(threshold=0, reduction="none") +# fct_dist = tfd.Normal(loc=tf.zeros((3, 1)), scale=tf.ones((3, 1))) +# obs = tf.zeros((3, 1)) +# result = loss(obs, fct_dist) +# assert result.shape == obs.shape + + +# def test_weighted_crps_zero_sample_weights(): +# """Passing an array of all zeros as sample weights set the total loss to zero""" +# tf.random.set_seed(1234) +# loss = losses.WeightedCRPSEnergy(threshold=0) +# fct_dist = tfd.Normal(loc=tf.zeros((3, 1)), scale=tf.ones((3, 1))) +# obs = tf.zeros((3, 1)) +# sample_weights = tf.zeros((3, 1)) +# result = loss(obs, fct_dist, sample_weight=sample_weights) +# assert result == 0 + + +# def test_multiscale_crps_layer(): +# event_shape = (1,) +# batch_shape = (10,) +# event_size = event_shape[0] +# layer_class = getattr(probabilistic_layers, "IndependentGamma") +# prob_layer = layer_class(event_size) +# y_pred_dist = prob_layer( +# np.random.random(batch_shape + (layer_class.params_size(event_size),)) +# ) +# loss = losses.MultiScaleCRPSEnergy(threshold=0, scales=[1, 2], reduction="none") +# result = loss(tf.zeros(batch_shape + event_shape), y_pred_dist) +# assert result.shape == batch_shape + event_shape + + +# def test_multiscale_crps_array(): +# event_shape = (1,) +# batch_shape = (10,) +# event_size = event_shape[0] +# layer_class = getattr(probabilistic_layers, "IndependentGamma") +# prob_layer = layer_class(event_size) +# y_pred_dist = prob_layer( +# np.random.random(batch_shape + (layer_class.params_size(event_size),)) +# ) +# y_pred = y_pred_dist.sample(3) +# loss = losses.MultiScaleCRPSEnergy(threshold=0, scales=[1, 2], reduction="none") +# result = loss(tf.zeros(batch_shape + event_shape), y_pred) +# assert result.shape == batch_shape + event_shape + + +# def test_energy_score(): +# n_events, n_dims = 10, 3 +# loss = losses.EnergyScore(reduction=tf.keras.losses.Reduction.NONE) +# fct_dist = tfd.MultivariateNormalDiag( +# loc=tf.zeros((n_events, n_dims)), +# scale_diag=tf.ones((n_events, n_dims)), +# ) +# obs = tf.zeros((n_events, n_dims)) +# result = loss(obs, fct_dist) +# assert tf.is_tensor(result) +# assert result.dtype == "float32" +# assert result.shape == obs.shape[0] + + +# @pytest.mark.parametrize( +# "metric, scaling, weights", +# ( +# ["mae", "standard", None], +# ["crps_energy", "minmax", None], +# ["mse", None, [1.0, 1.5]], +# ), +# ) +# def test_multivariate_loss(metric, scaling, weights): + +# tf.random.set_seed(1234) +# loss = losses.MultivariateLoss(metric, scaling, weights) + +# if getattr(loss.metric, "loss_type", None) == "probabilistic": +# dist = tfd.Normal(loc=tf.zeros((3, 2)), scale=tf.ones((3, 2))) +# fct = tfd.Independent(dist, reinterpreted_batch_ndims=1) +# fct.shape = (*fct.batch_shape, *fct.event_shape) +# else: +# fct = tf.random.normal((3, 2)) + +# obs = tf.random.normal((3, 2)) + +# result = loss(obs, fct).numpy() + +# assert isinstance(result, np.float32) + + +# def test_binary_loss_dtypes(): +# """Test various input data types""" +# tf.random.set_seed(1234) +# loss = losses.BinaryClassifierLoss(threshold=1) +# y_pred_dist = tfd.Normal(loc=tf.zeros((3, 1)), scale=tf.ones((3, 1))) +# y_pred_ens = y_pred_dist.sample(100) +# y_true = tf.zeros((3, 1)) + +# # prediction is TFP distribution +# tf.random.set_seed(42) +# result = loss(y_true, y_pred_dist) +# assert tf.is_tensor(result) +# assert result.dtype == "float32" +# tf.random.set_seed(42) +# np.testing.assert_allclose(result, loss(y_true.numpy(), y_pred_dist)) + +# # prediction is TF tensor +# result = loss(y_true, y_pred_ens) +# assert tf.is_tensor(result) +# assert result.dtype == "float32" +# np.testing.assert_allclose(result, loss(y_true.numpy(), y_pred_ens)) + +# # prediction is numpy array +# result = loss(y_true, y_pred_ens.numpy()) +# assert tf.is_tensor(result) +# assert result.dtype == "float32" +# np.testing.assert_allclose(result, loss(y_true.numpy(), y_pred_ens.numpy())) + + +# def test_combined_loss(): +# """""" +# loss_specs = [ +# {"BinaryClassifierLoss": {"threshold": 1}, "weight": 0.7}, +# {"WeightedCRPSEnergy": {"threshold": 0.1}, "weight": 0.1}, +# ] + +# combined_loss = losses.CombinedLoss(loss_specs) +# y_pred_dist = tfd.Normal(loc=tf.zeros((3, 1)), scale=tf.ones((3, 1))) +# y_true = tf.zeros((3, 1)) + +# # prediction is TFP distribution +# tf.random.set_seed(42) +# result = combined_loss(y_true, y_pred_dist) +# assert tf.is_tensor(result) +# assert result.dtype == "float32" diff --git a/tests/test_metrics.py b/tests/test_metrics.py index dface06..7198587 100644 --- a/tests/test_metrics.py +++ b/tests/test_metrics.py @@ -1,51 +1,51 @@ -import pytest -import numpy as np -import tensorflow as tf - -from mlpp_lib import metrics - - -def test_bias(): - y_true = tf.constant([1, 2, 3, 4, 5], dtype=tf.float32) - y_pred = tf.constant([0.8, 2.2, 2.9, 4.1, 5.5], dtype=tf.float32) - result = metrics.bias(y_true, y_pred) - expected_result = (-0.2 + 0.2 - 0.1 + 0.1 + 0.5) / 5 - assert result.numpy() == pytest.approx(expected_result) - - -class TestMAEBusts: - @pytest.fixture - def maebusts(self): - return metrics.MAEBusts(threshold=0.5) - - def test_maebusts_initialization(self, maebusts): - assert maebusts.threshold == 0.5 - assert maebusts.n_busts.numpy() == 0 - assert maebusts.n_samples.numpy() == 0 - - def test_maebusts_update_state(self, maebusts): - y_true = tf.constant([1, 2, 3, 4, 5, 6], dtype=tf.float32) - y_pred = tf.constant([0.8, 2.2, 2.5, 4.5, 4.4, 6.6], dtype=tf.float32) - maebusts.update_state(y_true, y_pred) - assert maebusts.n_busts.numpy() == 2 - assert maebusts.n_samples.numpy() == 6 - maebusts.reset_state() - sample_weight = tf.constant([1, 0, 1, 0, 1, 0], dtype=tf.float32) - maebusts.update_state(y_true, y_pred, sample_weight) - assert maebusts.n_busts.numpy() == 1 - assert maebusts.n_samples.numpy() == 3 - maebusts.reset_state() - assert maebusts.n_busts.numpy() == 0 - assert maebusts.n_samples.numpy() == 0 - - def test_maebusts_result(self, maebusts): - y_true = tf.constant([1, 2, 3, 4, 5, 6], dtype=tf.float32) - y_pred = tf.constant([0.8, 2.2, 2.5, 4.5, 4.4, 6.6], dtype=tf.float32) - maebusts.update_state(y_true, y_pred) - assert maebusts.result().numpy() == pytest.approx(2 / 6) - maebusts.reset_state() - sample_weight = tf.constant([1, 0, 1, 0, 1, 0], dtype=tf.float32) - maebusts.update_state(y_true, y_pred, sample_weight) - assert maebusts.result().numpy() == pytest.approx(1 / 3) - maebusts.reset_state() - assert np.isnan(maebusts.result().numpy()) +# import pytest +# import numpy as np +# import tensorflow as tf + +# from mlpp_lib import metrics + + +# def test_bias(): +# y_true = tf.constant([1, 2, 3, 4, 5], dtype=tf.float32) +# y_pred = tf.constant([0.8, 2.2, 2.9, 4.1, 5.5], dtype=tf.float32) +# result = metrics.bias(y_true, y_pred) +# expected_result = (-0.2 + 0.2 - 0.1 + 0.1 + 0.5) / 5 +# assert result.numpy() == pytest.approx(expected_result) + + +# class TestMAEBusts: +# @pytest.fixture +# def maebusts(self): +# return metrics.MAEBusts(threshold=0.5) + +# def test_maebusts_initialization(self, maebusts): +# assert maebusts.threshold == 0.5 +# assert maebusts.n_busts.numpy() == 0 +# assert maebusts.n_samples.numpy() == 0 + +# def test_maebusts_update_state(self, maebusts): +# y_true = tf.constant([1, 2, 3, 4, 5, 6], dtype=tf.float32) +# y_pred = tf.constant([0.8, 2.2, 2.5, 4.5, 4.4, 6.6], dtype=tf.float32) +# maebusts.update_state(y_true, y_pred) +# assert maebusts.n_busts.numpy() == 2 +# assert maebusts.n_samples.numpy() == 6 +# maebusts.reset_state() +# sample_weight = tf.constant([1, 0, 1, 0, 1, 0], dtype=tf.float32) +# maebusts.update_state(y_true, y_pred, sample_weight) +# assert maebusts.n_busts.numpy() == 1 +# assert maebusts.n_samples.numpy() == 3 +# maebusts.reset_state() +# assert maebusts.n_busts.numpy() == 0 +# assert maebusts.n_samples.numpy() == 0 + +# def test_maebusts_result(self, maebusts): +# y_true = tf.constant([1, 2, 3, 4, 5, 6], dtype=tf.float32) +# y_pred = tf.constant([0.8, 2.2, 2.5, 4.5, 4.4, 6.6], dtype=tf.float32) +# maebusts.update_state(y_true, y_pred) +# assert maebusts.result().numpy() == pytest.approx(2 / 6) +# maebusts.reset_state() +# sample_weight = tf.constant([1, 0, 1, 0, 1, 0], dtype=tf.float32) +# maebusts.update_state(y_true, y_pred, sample_weight) +# assert maebusts.result().numpy() == pytest.approx(1 / 3) +# maebusts.reset_state() +# assert np.isnan(maebusts.result().numpy()) diff --git a/tests/test_model_selection.py b/tests/test_model_selection.py index bc456da..4ba9375 100644 --- a/tests/test_model_selection.py +++ b/tests/test_model_selection.py @@ -8,44 +8,44 @@ import mlpp_lib.model_selection as ms -def check_splits(splits: dict): - """ - Check if the data splits are valid. - - Args: - splits (dict): A dictionary containing train, val, and test splits. - Each split should contain 'station' and 'forecast_reference_time' keys. - - Raises: - AssertionError: If there are overlapping stations or forecast reference times between splits. - """ - train_stations = set(splits["train"]["station"]) - val_stations = set(splits["val"]["station"]) - test_stations = set(splits["test"]["station"]) - - train_reftimes = set(splits["train"]["forecast_reference_time"]) - val_reftimes = set(splits["val"]["forecast_reference_time"]) - test_reftimes = set(splits["test"]["forecast_reference_time"]) - - assert len(train_stations & test_stations) == 0, "Train and test stations overlap." - assert len(train_stations & val_stations) == 0, "Train and val stations overlap." - assert len(val_stations & test_stations) == 0, "Val and test stations overlap." - assert ( - len(train_reftimes & test_reftimes) == 0 - ), "Train and test forecast reference times overlap." - assert ( - len(train_reftimes & val_reftimes) == 0 - ), "Train and val forecast reference times overlap." - assert ( - len(val_reftimes & test_reftimes) == 0 - ), "Val and test forecast reference times overlap." - - assert len(train_stations) > 0, "Train split is empty." - assert len(val_stations) > 0, "Val split is empty." - assert len(test_stations) > 0, "Test split is empty." - assert len(train_reftimes) > 0, "Train split is empty." - assert len(val_reftimes) > 0, "Val split is empty." - assert len(test_reftimes) > 0, "Test split is empty." +# def check_splits(splits: dict): +# """ +# Check if the data splits are valid. + +# Args: +# splits (dict): A dictionary containing train, val, and test splits. +# Each split should contain 'station' and 'forecast_reference_time' keys. + +# Raises: +# AssertionError: If there are overlapping stations or forecast reference times between splits. +# """ +# train_stations = set(splits["train"]["station"]) +# val_stations = set(splits["val"]["station"]) +# test_stations = set(splits["test"]["station"]) + +# train_reftimes = set(splits["train"]["forecast_reference_time"]) +# val_reftimes = set(splits["val"]["forecast_reference_time"]) +# test_reftimes = set(splits["test"]["forecast_reference_time"]) + +# assert len(train_stations & test_stations) == 0, "Train and test stations overlap." +# assert len(train_stations & val_stations) == 0, "Train and val stations overlap." +# assert len(val_stations & test_stations) == 0, "Val and test stations overlap." +# assert ( +# len(train_reftimes & test_reftimes) == 0 +# ), "Train and test forecast reference times overlap." +# assert ( +# len(train_reftimes & val_reftimes) == 0 +# ), "Train and val forecast reference times overlap." +# assert ( +# len(val_reftimes & test_reftimes) == 0 +# ), "Val and test forecast reference times overlap." + +# assert len(train_stations) > 0, "Train split is empty." +# assert len(val_stations) > 0, "Val split is empty." +# assert len(test_stations) > 0, "Test split is empty." +# assert len(train_reftimes) > 0, "Train split is empty." +# assert len(val_reftimes) > 0, "Val split is empty." +# assert len(test_reftimes) > 0, "Test split is empty." @dataclass @@ -111,212 +111,212 @@ def pytest_id(self): return f"time: {self.time}, station: {self.station}" -class TestDataSplitter: - - scenarios = [ - ValidDataSplitterOptions(time="fractions", station="lists"), - ValidDataSplitterOptions(time="slices", station="fractions"), - ValidDataSplitterOptions(time="lists", station="fractions"), - ValidDataSplitterOptions(time="lists", station="mixed"), - ValidDataSplitterOptions(time="mixed-with-list", station="fractions"), - ValidDataSplitterOptions(time="mixed-with-slice", station="fractions"), - ] - - @pytest.mark.parametrize( - "options", scenarios, ids=ValidDataSplitterOptions.pytest_id - ) - def test_valid_split(self, options, features_dataset, targets_dataset): - splitter = ms.DataSplitter( - options.time_split, - options.station_split, - options.time_split_method, - options.station_split_method, - ) - splits = splitter.fit(features_dataset).to_dict() - - check_splits(splits) - - @pytest.mark.parametrize( - "options", scenarios, ids=ValidDataSplitterOptions.pytest_id - ) - def test_get_partition(self, options, features_dataset, targets_dataset): - splitter = ms.DataSplitter( - options.time_split, - options.station_split, - options.time_split_method, - options.station_split_method, - ) - train_features, train_targets = splitter.get_partition( - features_dataset, targets_dataset, partition="train" - ) - val_features, val_targets = splitter.get_partition( - features_dataset, targets_dataset, partition="val" - ) - test_features, test_targets = splitter.get_partition( - features_dataset, targets_dataset, partition="test" - ) - - @pytest.mark.parametrize( - "options", scenarios, ids=ValidDataSplitterOptions.pytest_id - ) - def test_serialization(self, options, features_dataset, targets_dataset, tmp_path): - fn = f"{tmp_path}/splitter.json" - splitter = ms.DataSplitter( - options.time_split, - options.station_split, - options.time_split_method, - options.station_split_method, - ) - splitter.get_partition(features_dataset, targets_dataset, partition="train") - splitter.save_json(fn) - new_splitter = ms.DataSplitter.from_json(fn) - for split_key, split_dict in splitter.partitions.items(): - for dim, value in split_dict.items(): - new_value = new_splitter.partitions[split_key][dim] - np.testing.assert_array_equal(value, new_value) - - # test invalid arguments - def test_time_split_method_required(self): - time_split = {"train": 0.6, "val": 0.2, "test": 0.2} - station_split = {"train": 0.6, "val": 0.2, "test": 0.2} - with pytest.raises(ValueError) as excinfo: - splitter = ms.DataSplitter( - time_split, station_split, station_split_method="random" - ) - assert ( - str(excinfo.value) - == "`time_split_method` must be provided if the time splits are provided as fractions!" - ) - - def test_station_split_method_required(self): - time_split = {"train": 0.6, "val": 0.2, "test": 0.2} - station_split = {"train": 0.6, "val": 0.2, "test": 0.2} - with pytest.raises(ValueError) as excinfo: - splitter = ms.DataSplitter( - time_split, station_split, time_split_method="sequential" - ) - assert ( - str(excinfo.value) - == "`station_split_method` must be provided if the station splits are provided as fractions!" - ) - - def test_station_split_keys_invalid(self): - time_split = {"train": 0.6, "val": 0.2, "test": 0.2} - station_split = {"train": 0.6, "val": 0.2} - with pytest.raises(ValueError) as excinfo: - splitter = ms.DataSplitter( - time_split, - station_split, - time_split_method="sequential", - station_split_method="random", - ) - assert ( - str(excinfo.value) - == "Time split and station split must be defined with the same partitions!" - ) - - def test_time_split_method_invalid(self): - time_split = {"train": 0.6, "val": 0.2, "test": 0.2} - station_split = {"train": 0.6, "val": 0.2, "test": 0.2} - time_split_method = "invalid_method" - station_split_method = "random" - with pytest.raises(ValueError) as excinfo: - splitter = ms.DataSplitter( - time_split, - station_split, - time_split_method=time_split_method, - station_split_method=station_split_method, - ) - assert ( - str(excinfo.value) - == f"Invalid time split method: {time_split_method}. Must be one of {ms.DataSplitter.time_split_methods}." - ) - - def test_station_split_method_invalid(self): - time_split = {"train": 0.6, "val": 0.2, "test": 0.2} - station_split = {"train": 0.6, "val": 0.2, "test": 0.2} - time_split_method = "sequential" - station_split_method = "invalid_method" - with pytest.raises(ValueError) as excinfo: - splitter = ms.DataSplitter( - time_split, - station_split, - time_split_method=time_split_method, - station_split_method=station_split_method, - ) - assert ( - str(excinfo.value) - == f"Invalid station split method: {station_split_method}. Must be one of {ms.DataSplitter.station_split_methods}." - ) - - -def test_sequential_split(features_dataset): - index = features_dataset.forecast_reference_time.values[:50] - split_fractions = {"train": 0.6, "valid": 0.2, "test": 0.2} - result = ms.sequential_split(index, split_fractions) - assert len(result) == 3 - assert len(result["train"]) == 30 - assert len(result["valid"]) == 10 - assert len(result["test"]) == 10 - split_fractions = {"train": 0.6, "valid": 0.2, "test": 0.3} - with pytest.raises(AssertionError): - ms.sequential_split(index, split_fractions) - - -def test_random_split(features_dataset): - index = features_dataset.station.values[:30] - split_fractions = {"train": 0.5, "valid": 0.3, "test": 0.2} - result = ms.sequential_split(index, split_fractions) - assert len(result) == 3 - assert len(result["train"]) == 12 - assert len(result["valid"]) == 8 - assert len(result["test"]) == 5 - split_fractions = {"train": 0.6, "valid": 0.2, "test": 0.3} - with pytest.raises(AssertionError): - ms.sequential_split(index, split_fractions) - - -def get_split_lengths(test_size, n): - split_lengths = [] - split_ratios = [1 - test_size, test_size] - start = 0 - for ratio in split_ratios: - end = start + ratio - split_lengths.append(int(end * n) - int(start * n)) - start = end - return split_lengths - - -def test_train_test_split(): - """""" - n_samples = 101 - test_size = 0.2 - n_labels = 7 - - samples = list(range(n_samples)) - labels = np.random.randint(0, n_labels, n_samples) - - sample_split = ms.train_test_split(samples, labels, test_size) - assert len(sample_split) == 2 - assert len([item for sublist in sample_split for item in sublist]) == n_samples - - for label in set(labels): - subdata = [s for l, s in zip(labels, samples) if l == label] - split_lengths = get_split_lengths(test_size, len(subdata)) - assert sum([s in sample_split[0] for s in subdata]) == split_lengths[0] - assert sum([s in sample_split[1] for s in subdata]) == split_lengths[1] - - -def test_time_series_cv(): - """""" - n_splits = 5 - reftimes = np.arange("2016-01-01", "2021-01-01", dtype="datetime64[12h]").astype( - "datetime64[ns]" - ) - cv = ms.UniformTimeSeriesSplit(n_splits) - for n, (train, test) in enumerate(cv.split(reftimes)): - assert isinstance(train, list) - assert isinstance(test, list) - assert np.isclose(len(train) / len(reftimes), 1 - 1 / n_splits, atol=0.05) - assert np.isclose(len(test) / len(reftimes), 1 / n_splits, atol=0.05) - assert n_splits == (n + 1) +# class TestDataSplitter: + +# scenarios = [ +# ValidDataSplitterOptions(time="fractions", station="lists"), +# ValidDataSplitterOptions(time="slices", station="fractions"), +# ValidDataSplitterOptions(time="lists", station="fractions"), +# ValidDataSplitterOptions(time="lists", station="mixed"), +# ValidDataSplitterOptions(time="mixed-with-list", station="fractions"), +# ValidDataSplitterOptions(time="mixed-with-slice", station="fractions"), +# ] + +# @pytest.mark.parametrize( +# "options", scenarios, ids=ValidDataSplitterOptions.pytest_id +# ) +# def test_valid_split(self, options, features_dataset, targets_dataset): +# splitter = ms.DataSplitter( +# options.time_split, +# options.station_split, +# options.time_split_method, +# options.station_split_method, +# ) +# splits = splitter.fit(features_dataset).to_dict() + +# check_splits(splits) + +# @pytest.mark.parametrize( +# "options", scenarios, ids=ValidDataSplitterOptions.pytest_id +# ) +# def test_get_partition(self, options, features_dataset, targets_dataset): +# splitter = ms.DataSplitter( +# options.time_split, +# options.station_split, +# options.time_split_method, +# options.station_split_method, +# ) +# train_features, train_targets = splitter.get_partition( +# features_dataset, targets_dataset, partition="train" +# ) +# val_features, val_targets = splitter.get_partition( +# features_dataset, targets_dataset, partition="val" +# ) +# test_features, test_targets = splitter.get_partition( +# features_dataset, targets_dataset, partition="test" +# ) + +# @pytest.mark.parametrize( +# "options", scenarios, ids=ValidDataSplitterOptions.pytest_id +# ) +# def test_serialization(self, options, features_dataset, targets_dataset, tmp_path): +# fn = f"{tmp_path}/splitter.json" +# splitter = ms.DataSplitter( +# options.time_split, +# options.station_split, +# options.time_split_method, +# options.station_split_method, +# ) +# splitter.get_partition(features_dataset, targets_dataset, partition="train") +# splitter.save_json(fn) +# new_splitter = ms.DataSplitter.from_json(fn) +# for split_key, split_dict in splitter.partitions.items(): +# for dim, value in split_dict.items(): +# new_value = new_splitter.partitions[split_key][dim] +# np.testing.assert_array_equal(value, new_value) + +# # test invalid arguments +# def test_time_split_method_required(self): +# time_split = {"train": 0.6, "val": 0.2, "test": 0.2} +# station_split = {"train": 0.6, "val": 0.2, "test": 0.2} +# with pytest.raises(ValueError) as excinfo: +# splitter = ms.DataSplitter( +# time_split, station_split, station_split_method="random" +# ) +# assert ( +# str(excinfo.value) +# == "`time_split_method` must be provided if the time splits are provided as fractions!" +# ) + +# def test_station_split_method_required(self): +# time_split = {"train": 0.6, "val": 0.2, "test": 0.2} +# station_split = {"train": 0.6, "val": 0.2, "test": 0.2} +# with pytest.raises(ValueError) as excinfo: +# splitter = ms.DataSplitter( +# time_split, station_split, time_split_method="sequential" +# ) +# assert ( +# str(excinfo.value) +# == "`station_split_method` must be provided if the station splits are provided as fractions!" +# ) + +# def test_station_split_keys_invalid(self): +# time_split = {"train": 0.6, "val": 0.2, "test": 0.2} +# station_split = {"train": 0.6, "val": 0.2} +# with pytest.raises(ValueError) as excinfo: +# splitter = ms.DataSplitter( +# time_split, +# station_split, +# time_split_method="sequential", +# station_split_method="random", +# ) +# assert ( +# str(excinfo.value) +# == "Time split and station split must be defined with the same partitions!" +# ) + +# def test_time_split_method_invalid(self): +# time_split = {"train": 0.6, "val": 0.2, "test": 0.2} +# station_split = {"train": 0.6, "val": 0.2, "test": 0.2} +# time_split_method = "invalid_method" +# station_split_method = "random" +# with pytest.raises(ValueError) as excinfo: +# splitter = ms.DataSplitter( +# time_split, +# station_split, +# time_split_method=time_split_method, +# station_split_method=station_split_method, +# ) +# assert ( +# str(excinfo.value) +# == f"Invalid time split method: {time_split_method}. Must be one of {ms.DataSplitter.time_split_methods}." +# ) + +# def test_station_split_method_invalid(self): +# time_split = {"train": 0.6, "val": 0.2, "test": 0.2} +# station_split = {"train": 0.6, "val": 0.2, "test": 0.2} +# time_split_method = "sequential" +# station_split_method = "invalid_method" +# with pytest.raises(ValueError) as excinfo: +# splitter = ms.DataSplitter( +# time_split, +# station_split, +# time_split_method=time_split_method, +# station_split_method=station_split_method, +# ) +# assert ( +# str(excinfo.value) +# == f"Invalid station split method: {station_split_method}. Must be one of {ms.DataSplitter.station_split_methods}." +# ) + + +# def test_sequential_split(features_dataset): +# index = features_dataset.forecast_reference_time.values[:50] +# split_fractions = {"train": 0.6, "valid": 0.2, "test": 0.2} +# result = ms.sequential_split(index, split_fractions) +# assert len(result) == 3 +# assert len(result["train"]) == 30 +# assert len(result["valid"]) == 10 +# assert len(result["test"]) == 10 +# split_fractions = {"train": 0.6, "valid": 0.2, "test": 0.3} +# with pytest.raises(AssertionError): +# ms.sequential_split(index, split_fractions) + + +# def test_random_split(features_dataset): +# index = features_dataset.station.values[:30] +# split_fractions = {"train": 0.5, "valid": 0.3, "test": 0.2} +# result = ms.sequential_split(index, split_fractions) +# assert len(result) == 3 +# assert len(result["train"]) == 12 +# assert len(result["valid"]) == 8 +# assert len(result["test"]) == 5 +# split_fractions = {"train": 0.6, "valid": 0.2, "test": 0.3} +# with pytest.raises(AssertionError): +# ms.sequential_split(index, split_fractions) + + +# def get_split_lengths(test_size, n): +# split_lengths = [] +# split_ratios = [1 - test_size, test_size] +# start = 0 +# for ratio in split_ratios: +# end = start + ratio +# split_lengths.append(int(end * n) - int(start * n)) +# start = end +# return split_lengths + + +# def test_train_test_split(): +# """""" +# n_samples = 101 +# test_size = 0.2 +# n_labels = 7 + +# samples = list(range(n_samples)) +# labels = np.random.randint(0, n_labels, n_samples) + +# sample_split = ms.train_test_split(samples, labels, test_size) +# assert len(sample_split) == 2 +# assert len([item for sublist in sample_split for item in sublist]) == n_samples + +# for label in set(labels): +# subdata = [s for l, s in zip(labels, samples) if l == label] +# split_lengths = get_split_lengths(test_size, len(subdata)) +# assert sum([s in sample_split[0] for s in subdata]) == split_lengths[0] +# assert sum([s in sample_split[1] for s in subdata]) == split_lengths[1] + + +# def test_time_series_cv(): +# """""" +# n_splits = 5 +# reftimes = np.arange("2016-01-01", "2021-01-01", dtype="datetime64[12h]").astype( +# "datetime64[ns]" +# ) +# cv = ms.UniformTimeSeriesSplit(n_splits) +# for n, (train, test) in enumerate(cv.split(reftimes)): +# assert isinstance(train, list) +# assert isinstance(test, list) +# assert np.isclose(len(train) / len(reftimes), 1 - 1 / n_splits, atol=0.05) +# assert np.isclose(len(test) / len(reftimes), 1 / n_splits, atol=0.05) +# assert n_splits == (n + 1) diff --git a/tests/test_models.py b/tests/test_models.py index 48858ca..efe01e6 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,192 +1,284 @@ -import itertools - -import numpy as np +import torch +from inspect import getmembers, isclass import pytest -import tensorflow as tf -from tensorflow.keras import Model -from numpy.testing import assert_array_equal - -from mlpp_lib import models - - -FCN_OPTIONS = dict( - input_shape=[(5,)], - output_size=[1, 2], - hidden_layers=[[8, 8]], - activations=["relu", ["relu", "elu"]], - dropout=[None, 0.1, [0.1, 0.0]], - mc_dropout=[True, False], - out_bias_init=["zeros", np.array([0.2]), np.array([0.2, 2.1])], - probabilistic_layer=[None] + ["IndependentGamma", "MultivariateNormalDiag"], - skip_connection=[False, True], -) +from mlpp_lib.layers import FullyConnectedLayer +from mlpp_lib.probabilistic_layers import ( + BaseDistributionLayer, + BaseParametricDistributionModule, + UniveriateGaussianModule, + distribution_to_layer +) +from mlpp_lib.models import ( + fully_connected_network, + fully_connected_multibranch_network, + deep_cross_network, +) -FCN_SCENARIOS = [ - dict(zip(list(FCN_OPTIONS.keys()), x)) - for x in itertools.product(*FCN_OPTIONS.values()) -] - - -DCN_SCENARIOS = [ - dict(zip(list(FCN_OPTIONS.keys()), x)) - for x in itertools.product(*FCN_OPTIONS.values()) -] - - -def _test_model(model): - moodel_is_keras = isinstance(model, tf.keras.Model) - assert moodel_is_keras - assert isinstance(model, Model) - assert len(model.layers[-1]._inbound_nodes) > 0 - model_output = model.layers[-1].output - assert not isinstance( - model_output, list - ), "The model output must be a single tensor!" - assert ( - len(model_output.shape) < 3 - ), "The model output must be a vector or a single value!" - - -def _test_prediction(model, scenario_kwargs, dummy_input, output_size): - is_deterministic = ( - scenario_kwargs["dropout"] is None or not scenario_kwargs["mc_dropout"] - ) - is_probabilistic = scenario_kwargs["probabilistic_layer"] is not None - if is_probabilistic: - return - - pred = model(dummy_input) - assert pred.shape == (32, output_size) - pred2 = model(dummy_input) - - if is_deterministic: - assert_array_equal(pred, pred2) - else: - with pytest.raises(AssertionError): - assert_array_equal(pred, pred2) - - -def _test_prediction_prob(model, scenario_kwargs, dummy_input, output_size): - is_deterministic = ( - scenario_kwargs["dropout"] is None or not scenario_kwargs["mc_dropout"] - ) - is_probabilistic = scenario_kwargs["probabilistic_layer"] is not None - if not is_probabilistic: - return - - pred1 = model(dummy_input) - assert pred1.shape == (32, output_size) - pred2 = model(dummy_input) - try: - # Idependent layers have a "distribution" attribute - pred1_params = pred1.parameters["distribution"].parameters - pred2_params = pred2.parameters["distribution"].parameters - except KeyError: - pred1_params = pred1.parameters - pred2_params = pred2.parameters - - for param in pred1_params.keys(): - try: - param_array1 = pred1_params[param].numpy() - param_array2 = pred2_params[param].numpy() - except AttributeError: - continue - - if is_deterministic: - assert_array_equal(param_array1, param_array2) - else: - with pytest.raises(AssertionError): - assert_array_equal(param_array1, param_array2) - - -@pytest.mark.parametrize("scenario_kwargs", FCN_SCENARIOS) -def test_fully_connected_network(scenario_kwargs): - - tf.keras.backend.clear_session() - - scenario_kwargs = scenario_kwargs.copy() - input_shape = scenario_kwargs.pop("input_shape") - output_size = scenario_kwargs.pop("output_size") - dummy_input = np.random.randn(32, *input_shape) - - # check that correct errors are raised for some scenarios - if isinstance(scenario_kwargs["out_bias_init"], np.ndarray): - if scenario_kwargs["out_bias_init"].shape[-1] != output_size: - with pytest.raises(AssertionError): - models.fully_connected_network( - input_shape, output_size, **scenario_kwargs - ) - return - else: - model = models.fully_connected_network( - input_shape, output_size, **scenario_kwargs - ) - - else: - model = models.fully_connected_network( - input_shape, output_size, **scenario_kwargs - ) - - _test_model(model) - _test_prediction(model, scenario_kwargs, dummy_input, output_size) - _test_prediction_prob(model, scenario_kwargs, dummy_input, output_size) - - -@pytest.mark.parametrize("scenario_kwargs", FCN_SCENARIOS) -def test_fully_connected_multibranch_network(scenario_kwargs): - - tf.keras.backend.clear_session() - - scenario_kwargs = scenario_kwargs.copy() - input_shape = scenario_kwargs.pop("input_shape") - output_size = scenario_kwargs.pop("output_size") - dummy_input = np.random.randn(32, *input_shape) - - # check that correct errors are raised for some scenarios - if isinstance(scenario_kwargs["out_bias_init"], np.ndarray): - if scenario_kwargs["out_bias_init"].shape[-1] != output_size: - with pytest.raises(AssertionError): - models.fully_connected_multibranch_network( - input_shape, output_size, **scenario_kwargs - ) - return - else: - model = models.fully_connected_multibranch_network( - input_shape, output_size, **scenario_kwargs - ) - - else: - model = models.fully_connected_multibranch_network( - input_shape, output_size, **scenario_kwargs - ) - - _test_model(model) - _test_prediction(model, scenario_kwargs, dummy_input, output_size) - _test_prediction_prob(model, scenario_kwargs, dummy_input, output_size) - - -@pytest.mark.parametrize("scenario_kwargs", DCN_SCENARIOS) -def test_deep_cross_network(scenario_kwargs): - - scenario_kwargs = scenario_kwargs.copy() - input_shape = scenario_kwargs.pop("input_shape") - output_size = scenario_kwargs.pop("output_size") - dummy_input = np.random.randn(32, *input_shape) - # check that correct errors are raised for some scenarios - if isinstance(scenario_kwargs["out_bias_init"], np.ndarray): - if scenario_kwargs["out_bias_init"].shape[-1] != output_size: - with pytest.raises(AssertionError): - models.deep_cross_network(input_shape, output_size, **scenario_kwargs) - return - else: - model = models.deep_cross_network( - input_shape, output_size, **scenario_kwargs - ) - - else: - model = models.deep_cross_network(input_shape, output_size, **scenario_kwargs) - - _test_model(model) - _test_prediction(model, scenario_kwargs, dummy_input, output_size) - _test_prediction_prob(model, scenario_kwargs, dummy_input, output_size) +from mlpp_lib import probabilistic_layers + +DISTRIBUTIONS = [obj[1] for obj in getmembers(probabilistic_layers, isclass) + if issubclass(obj[1], BaseParametricDistributionModule) and obj[0] != 'BaseParametricDistributionModule'] + + +distribution_modules_kwargs = { + 'a': 0, + 'b': 1, + 'dim': 3 +} + +@pytest.mark.parametrize("distribution", list(distribution_to_layer.keys())+[None]) +@pytest.mark.parametrize("skip_connection", [True, False]) +@pytest.mark.parametrize("batchnorm", [True, False]) +def test_fcn_model_creation(distribution, skip_connection, batchnorm): + + hidden_layers=[16,16,8] + output_size = 4 + + model = fully_connected_network(output_size=output_size, + hidden_layers=hidden_layers, + batchnorm=batchnorm, + skip_connection=skip_connection, + probabilistic_layer=distribution, + prob_layer_kwargs=distribution_modules_kwargs) + + + inputs = torch.randn(32,6) + + output = model(inputs) + + +@pytest.mark.parametrize("distribution", list(distribution_to_layer.keys())+[None]) +@pytest.mark.parametrize("skip_connection", [True, False]) +@pytest.mark.parametrize("batchnorm", [True, False]) +@pytest.mark.parametrize("aggregation", ['sum', 'concat']) +def test_multibranch_fcn_creation(distribution, skip_connection, batchnorm, aggregation): + hidden_layers=[16,16,8] + output_size = 4 + n_branches = 3 + + model = fully_connected_multibranch_network(output_size=output_size, + hidden_layers=hidden_layers, + batchnorm=batchnorm, + skip_connection=skip_connection, + probabilistic_layer=distribution, + n_branches=n_branches, + aggregation=aggregation, + prob_layer_kwargs=distribution_modules_kwargs) + + + inputs = torch.randn(32,6) + + output = model(inputs) + + +@pytest.mark.parametrize("distribution", list(distribution_to_layer.keys())+[None], ids=lambda d: f"distribution={d}" if d else "distribution=None") +def test_deep_cross_network(distribution): + hidden_layers=[16,16,8] + output_size = 4 + n_crosses = 3 + + model = deep_cross_network(output_size=output_size, + hidden_layers=hidden_layers, + n_cross_layers=n_crosses, + cross_layers_hiddensize=16, + probabilistic_layer=distribution, + prob_layer_kwargs=distribution_modules_kwargs) + + inputs = torch.randn(32,6) + + output = model(inputs) + + +# import itertools + +# import numpy as np +# import pytest +# import tensorflow as tf +# from tensorflow.keras import Model +# from numpy.testing import assert_array_equal + +# from mlpp_lib import models + + +# FCN_OPTIONS = dict( +# input_shape=[(5,)], +# output_size=[1, 2], +# hidden_layers=[[8, 8]], +# activations=["relu", ["relu", "elu"]], +# dropout=[None, 0.1, [0.1, 0.0]], +# mc_dropout=[True, False], +# out_bias_init=["zeros", np.array([0.2]), np.array([0.2, 2.1])], +# probabilistic_layer=[None] + ["IndependentGamma", "MultivariateNormalDiag"], +# skip_connection=[False, True], +# ) + + +# FCN_SCENARIOS = [ +# dict(zip(list(FCN_OPTIONS.keys()), x)) +# for x in itertools.product(*FCN_OPTIONS.values()) +# ] + + +# DCN_SCENARIOS = [ +# dict(zip(list(FCN_OPTIONS.keys()), x)) +# for x in itertools.product(*FCN_OPTIONS.values()) +# ] + + +# def _test_model(model): +# moodel_is_keras = isinstance(model, tf.keras.Model) +# assert moodel_is_keras +# assert isinstance(model, Model) +# assert len(model.layers[-1]._inbound_nodes) > 0 +# model_output = model.layers[-1].output +# assert not isinstance( +# model_output, list +# ), "The model output must be a single tensor!" +# assert ( +# len(model_output.shape) < 3 +# ), "The model output must be a vector or a single value!" + + +# def _test_prediction(model, scenario_kwargs, dummy_input, output_size): +# is_deterministic = ( +# scenario_kwargs["dropout"] is None or not scenario_kwargs["mc_dropout"] +# ) +# is_probabilistic = scenario_kwargs["probabilistic_layer"] is not None +# if is_probabilistic: +# return + +# pred = model(dummy_input) +# assert pred.shape == (32, output_size) +# pred2 = model(dummy_input) + +# if is_deterministic: +# assert_array_equal(pred, pred2) +# else: +# with pytest.raises(AssertionError): +# assert_array_equal(pred, pred2) + + +# def _test_prediction_prob(model, scenario_kwargs, dummy_input, output_size): +# is_deterministic = ( +# scenario_kwargs["dropout"] is None or not scenario_kwargs["mc_dropout"] +# ) +# is_probabilistic = scenario_kwargs["probabilistic_layer"] is not None +# if not is_probabilistic: +# return + +# pred1 = model(dummy_input) +# assert pred1.shape == (32, output_size) +# pred2 = model(dummy_input) +# try: +# # Idependent layers have a "distribution" attribute +# pred1_params = pred1.parameters["distribution"].parameters +# pred2_params = pred2.parameters["distribution"].parameters +# except KeyError: +# pred1_params = pred1.parameters +# pred2_params = pred2.parameters + +# for param in pred1_params.keys(): +# try: +# param_array1 = pred1_params[param].numpy() +# param_array2 = pred2_params[param].numpy() +# except AttributeError: +# continue + +# if is_deterministic: +# assert_array_equal(param_array1, param_array2) +# else: +# with pytest.raises(AssertionError): +# assert_array_equal(param_array1, param_array2) + + +# @pytest.mark.parametrize("scenario_kwargs", FCN_SCENARIOS) +# def test_fully_connected_network(scenario_kwargs): + +# tf.keras.backend.clear_session() + +# scenario_kwargs = scenario_kwargs.copy() +# input_shape = scenario_kwargs.pop("input_shape") +# output_size = scenario_kwargs.pop("output_size") +# dummy_input = np.random.randn(32, *input_shape) + +# # check that correct errors are raised for some scenarios +# if isinstance(scenario_kwargs["out_bias_init"], np.ndarray): +# if scenario_kwargs["out_bias_init"].shape[-1] != output_size: +# with pytest.raises(AssertionError): +# models.fully_connected_network( +# input_shape, output_size, **scenario_kwargs +# ) +# return +# else: +# model = models.fully_connected_network( +# input_shape, output_size, **scenario_kwargs +# ) + +# else: +# model = models.fully_connected_network( +# input_shape, output_size, **scenario_kwargs +# ) + +# _test_model(model) +# _test_prediction(model, scenario_kwargs, dummy_input, output_size) +# _test_prediction_prob(model, scenario_kwargs, dummy_input, output_size) + + +# @pytest.mark.parametrize("scenario_kwargs", FCN_SCENARIOS) +# def test_fully_connected_multibranch_network(scenario_kwargs): + +# tf.keras.backend.clear_session() + +# scenario_kwargs = scenario_kwargs.copy() +# input_shape = scenario_kwargs.pop("input_shape") +# output_size = scenario_kwargs.pop("output_size") +# dummy_input = np.random.randn(32, *input_shape) + +# # check that correct errors are raised for some scenarios +# if isinstance(scenario_kwargs["out_bias_init"], np.ndarray): +# if scenario_kwargs["out_bias_init"].shape[-1] != output_size: +# with pytest.raises(AssertionError): +# models.fully_connected_multibranch_network( +# input_shape, output_size, **scenario_kwargs +# ) +# return +# else: +# model = models.fully_connected_multibranch_network( +# input_shape, output_size, **scenario_kwargs +# ) + +# else: +# model = models.fully_connected_multibranch_network( +# input_shape, output_size, **scenario_kwargs +# ) + +# _test_model(model) +# _test_prediction(model, scenario_kwargs, dummy_input, output_size) +# _test_prediction_prob(model, scenario_kwargs, dummy_input, output_size) + + +# @pytest.mark.parametrize("scenario_kwargs", DCN_SCENARIOS) +# def test_deep_cross_network(scenario_kwargs): + +# scenario_kwargs = scenario_kwargs.copy() +# input_shape = scenario_kwargs.pop("input_shape") +# output_size = scenario_kwargs.pop("output_size") +# dummy_input = np.random.randn(32, *input_shape) +# # check that correct errors are raised for some scenarios +# if isinstance(scenario_kwargs["out_bias_init"], np.ndarray): +# if scenario_kwargs["out_bias_init"].shape[-1] != output_size: +# with pytest.raises(AssertionError): +# models.deep_cross_network(input_shape, output_size, **scenario_kwargs) +# return +# else: +# model = models.deep_cross_network( +# input_shape, output_size, **scenario_kwargs +# ) + +# else: +# model = models.deep_cross_network(input_shape, output_size, **scenario_kwargs) + +# _test_model(model) +# _test_prediction(model, scenario_kwargs, dummy_input, output_size) +# _test_prediction_prob(model, scenario_kwargs, dummy_input, output_size) diff --git a/tests/test_normalizers.py b/tests/test_normalizers.py index 420f7e3..0131cd2 100644 --- a/tests/test_normalizers.py +++ b/tests/test_normalizers.py @@ -1,186 +1,186 @@ -import numpy as np -import pytest -import xarray as xr - -from mlpp_lib.normalizers import DataTransformer - - -def get_class_attributes(cls): - class_attrs = { - name: field.default for name, field in cls.__dataclass_fields__.items() - } - return class_attrs - - -def test_fit(datatransformations, data_transformer, features_multi): - - for i, datatransform in enumerate(datatransformations): - sel_vars = [f"var{i}"] - datatransform.fit(features_multi[sel_vars]) - - data_transformer.fit(features_multi) - - assert all( - ( - np.allclose( - getattr(datatransform, attr), - getattr(data_transformer.parameters[i][0], attr), - equal_nan=True, - ) - for attr in get_class_attributes(datatransform) - ) - for datatransform in datatransformations - ) - - -def test_transform(datatransformations, data_transformer, features_multi): - - features_individual = features_multi.copy() - for i, datatransform in enumerate(datatransformations): - sel_vars = [f"var{i}"] - datatransform.fit(features_multi[sel_vars]) - features_individual_ = datatransform.transform(features_individual)[0] - features_individual.update(features_individual_) - - data_transformer.fit(features_multi) - features_multi = data_transformer.transform(features_multi)[0] - - assert all( - np.allclose( - features_individual[f"var{i}"].values, - features_multi[f"var{i}"].values, - equal_nan=True, - ) - for i in range(len(datatransformations)) - ) - - -def test_inverse_transform(datatransformations, data_transformer, features_multi): - - original_data = features_multi.copy().astype("float32") - features_individual = features_multi.copy() - for i, datatransform in enumerate(datatransformations): - # set the same fillvalue as in the data_transformer - sel_vars = [f"var{i}"] - datatransform.fillvalue = data_transformer.fillvalue - datatransform.fit(features_multi[sel_vars]) - features_individual_ = datatransform.transform(features_individual)[0] - features_individual.update(features_individual_) - inv_ds_individual = features_individual.copy() - for i, datatransform in enumerate(datatransformations): - inv_ds_individual_ = datatransform.inverse_transform(inv_ds_individual)[0] - inv_ds_individual.update(inv_ds_individual_) - - data_transformer.fit(features_multi) - ds_multi = data_transformer.transform(features_multi)[0] - inv_ds_multi = data_transformer.inverse_transform(ds_multi)[0] - - assert all( - np.allclose( - inv_ds_individual[f"var{i}"].values, - inv_ds_multi[f"var{i}"].values, - equal_nan=True, - ) - for i in range(len(datatransformations)) - ), "Inverse transform is not equal between individual data transformations and data_transformer" - - assert all( - np.allclose( - original_data[f"var{i}"].values, - inv_ds_individual[f"var{i}"].values, - equal_nan=True, - atol=1e-6, - ) - for i in range(len(datatransformations)) - ), "Inverse transform is not equal between transformed individual data transformations and original features" - - assert all( - np.allclose( - original_data[f"var{i}"].values, - inv_ds_multi[f"var{i}"].values, - equal_nan=True, - atol=1e-6, - ) - for i in range(len(datatransformations)) - ), "Inverse transform is not equal between transformed data_transformer and original features" - - -def test_serialization(data_transformer, features_multi, tmp_path): - - fn_multi = f"{tmp_path}/data_transformer.json" - - data_transformer.fit(features_multi) - data_transformer.save_json(fn_multi) - new_datatransformer = DataTransformer.from_json(fn_multi) - - assert data_transformer.method_vars_dict == new_datatransformer.method_vars_dict - assert data_transformer.default == new_datatransformer.default - assert data_transformer.fillvalue == new_datatransformer.fillvalue - assert data_transformer.transformers == new_datatransformer.transformers - - -class TestLegacyStandardizer: - @pytest.fixture - def standardizer(self): - from mlpp_lib.standardizers import Standardizer - - return Standardizer(fillvalue=-5) - - def test_fit(self, standardizer, features_dataset): - standardizer.fit(features_dataset) - assert all( - var in standardizer.mean.data_vars for var in features_dataset.data_vars - ) - assert all( - var in standardizer.std.data_vars for var in features_dataset.data_vars - ) - assert standardizer.fillvalue == -5 - - def test_transform(self, standardizer, features_dataset): - standardizer.fit(features_dataset) - ds = standardizer.transform(features_dataset)[0] - assert all(var in ds.data_vars for var in features_dataset.data_vars) - assert all(np.isclose(ds[var].mean().values, 0) for var in ds.data_vars) - assert all(np.isclose(ds[var].std().values, 1) for var in ds.data_vars) - - def test_inverse_transform(self, standardizer, features_dataset): - standardizer.fit(features_dataset) - ds = standardizer.transform(features_dataset)[0] - inv_ds = standardizer.inverse_transform(ds)[0] - - assert all( - np.allclose( - inv_ds[var].values, - features_dataset[var].values, - equal_nan=True, - atol=1e-6, - ) - for var in features_dataset.data_vars - ) - assert all(var in inv_ds.data_vars for var in features_dataset.data_vars) - - def test_retro_compatibility(self, standardizer, features_multi): - standardizer.fit(features_multi) - dict_stand = standardizer.to_dict() - data_transformer = DataTransformer.from_dict(dict_stand) - - assert all( - ( - [ - np.allclose( - getattr(data_transformer.transformers["Standardizer"][0], attr)[ - var - ].values, - getattr(standardizer, attr)[var].values, - equal_nan=True, - ) - for var in getattr(standardizer, attr).data_vars - ] - if isinstance(getattr(standardizer, attr), xr.Dataset) - else np.allclose( - getattr(data_transformer.transformers["Standardizer"][0], attr), - getattr(standardizer, attr), - ) - ) - for attr in get_class_attributes(standardizer) - ) +# import numpy as np +# import pytest +# import xarray as xr + +# from mlpp_lib.normalizers import DataTransformer + + +# def get_class_attributes(cls): +# class_attrs = { +# name: field.default for name, field in cls.__dataclass_fields__.items() +# } +# return class_attrs + + +# def test_fit(datatransformations, data_transformer, features_multi): + +# for i, datatransform in enumerate(datatransformations): +# sel_vars = [f"var{i}"] +# datatransform.fit(features_multi[sel_vars]) + +# data_transformer.fit(features_multi) + +# assert all( +# ( +# np.allclose( +# getattr(datatransform, attr), +# getattr(data_transformer.parameters[i][0], attr), +# equal_nan=True, +# ) +# for attr in get_class_attributes(datatransform) +# ) +# for datatransform in datatransformations +# ) + + +# def test_transform(datatransformations, data_transformer, features_multi): + +# features_individual = features_multi.copy() +# for i, datatransform in enumerate(datatransformations): +# sel_vars = [f"var{i}"] +# datatransform.fit(features_multi[sel_vars]) +# features_individual_ = datatransform.transform(features_individual)[0] +# features_individual.update(features_individual_) + +# data_transformer.fit(features_multi) +# features_multi = data_transformer.transform(features_multi)[0] + +# assert all( +# np.allclose( +# features_individual[f"var{i}"].values, +# features_multi[f"var{i}"].values, +# equal_nan=True, +# ) +# for i in range(len(datatransformations)) +# ) + + +# def test_inverse_transform(datatransformations, data_transformer, features_multi): + +# original_data = features_multi.copy().astype("float32") +# features_individual = features_multi.copy() +# for i, datatransform in enumerate(datatransformations): +# # set the same fillvalue as in the data_transformer +# sel_vars = [f"var{i}"] +# datatransform.fillvalue = data_transformer.fillvalue +# datatransform.fit(features_multi[sel_vars]) +# features_individual_ = datatransform.transform(features_individual)[0] +# features_individual.update(features_individual_) +# inv_ds_individual = features_individual.copy() +# for i, datatransform in enumerate(datatransformations): +# inv_ds_individual_ = datatransform.inverse_transform(inv_ds_individual)[0] +# inv_ds_individual.update(inv_ds_individual_) + +# data_transformer.fit(features_multi) +# ds_multi = data_transformer.transform(features_multi)[0] +# inv_ds_multi = data_transformer.inverse_transform(ds_multi)[0] + +# assert all( +# np.allclose( +# inv_ds_individual[f"var{i}"].values, +# inv_ds_multi[f"var{i}"].values, +# equal_nan=True, +# ) +# for i in range(len(datatransformations)) +# ), "Inverse transform is not equal between individual data transformations and data_transformer" + +# assert all( +# np.allclose( +# original_data[f"var{i}"].values, +# inv_ds_individual[f"var{i}"].values, +# equal_nan=True, +# atol=1e-6, +# ) +# for i in range(len(datatransformations)) +# ), "Inverse transform is not equal between transformed individual data transformations and original features" + +# assert all( +# np.allclose( +# original_data[f"var{i}"].values, +# inv_ds_multi[f"var{i}"].values, +# equal_nan=True, +# atol=1e-6, +# ) +# for i in range(len(datatransformations)) +# ), "Inverse transform is not equal between transformed data_transformer and original features" + + +# def test_serialization(data_transformer, features_multi, tmp_path): + +# fn_multi = f"{tmp_path}/data_transformer.json" + +# data_transformer.fit(features_multi) +# data_transformer.save_json(fn_multi) +# new_datatransformer = DataTransformer.from_json(fn_multi) + +# assert data_transformer.method_vars_dict == new_datatransformer.method_vars_dict +# assert data_transformer.default == new_datatransformer.default +# assert data_transformer.fillvalue == new_datatransformer.fillvalue +# assert data_transformer.transformers == new_datatransformer.transformers + + +# class TestLegacyStandardizer: +# @pytest.fixture +# def standardizer(self): +# from mlpp_lib.standardizers import Standardizer + +# return Standardizer(fillvalue=-5) + +# def test_fit(self, standardizer, features_dataset): +# standardizer.fit(features_dataset) +# assert all( +# var in standardizer.mean.data_vars for var in features_dataset.data_vars +# ) +# assert all( +# var in standardizer.std.data_vars for var in features_dataset.data_vars +# ) +# assert standardizer.fillvalue == -5 + +# def test_transform(self, standardizer, features_dataset): +# standardizer.fit(features_dataset) +# ds = standardizer.transform(features_dataset)[0] +# assert all(var in ds.data_vars for var in features_dataset.data_vars) +# assert all(np.isclose(ds[var].mean().values, 0) for var in ds.data_vars) +# assert all(np.isclose(ds[var].std().values, 1) for var in ds.data_vars) + +# def test_inverse_transform(self, standardizer, features_dataset): +# standardizer.fit(features_dataset) +# ds = standardizer.transform(features_dataset)[0] +# inv_ds = standardizer.inverse_transform(ds)[0] + +# assert all( +# np.allclose( +# inv_ds[var].values, +# features_dataset[var].values, +# equal_nan=True, +# atol=1e-6, +# ) +# for var in features_dataset.data_vars +# ) +# assert all(var in inv_ds.data_vars for var in features_dataset.data_vars) + +# def test_retro_compatibility(self, standardizer, features_multi): +# standardizer.fit(features_multi) +# dict_stand = standardizer.to_dict() +# data_transformer = DataTransformer.from_dict(dict_stand) + +# assert all( +# ( +# [ +# np.allclose( +# getattr(data_transformer.transformers["Standardizer"][0], attr)[ +# var +# ].values, +# getattr(standardizer, attr)[var].values, +# equal_nan=True, +# ) +# for var in getattr(standardizer, attr).data_vars +# ] +# if isinstance(getattr(standardizer, attr), xr.Dataset) +# else np.allclose( +# getattr(data_transformer.transformers["Standardizer"][0], attr), +# getattr(standardizer, attr), +# ) +# ) +# for attr in get_class_attributes(standardizer) +# ) diff --git a/tests/test_probabilistic_layers.py b/tests/test_probabilistic_layers.py index 55ce556..21d0855 100644 --- a/tests/test_probabilistic_layers.py +++ b/tests/test_probabilistic_layers.py @@ -1,94 +1,139 @@ -from inspect import getmembers, isclass - -import numpy as np +import torch import pytest -import tensorflow as tf -import tensorflow_probability as tfp - -from mlpp_lib import models -from mlpp_lib import probabilistic_layers -from mlpp_lib.datasets import Dataset - - -LAYERS = [obj[0] for obj in getmembers(probabilistic_layers, isclass)] - - -@pytest.mark.parametrize("layer", LAYERS) -def test_probabilistic_layers(layer): - - layer_class = getattr(probabilistic_layers, layer) - - output_size = 2 - params_size = layer_class.params_size(output_size) - - # build - layer = layer_class(output_size) - - # call - input_tensor = tf.random.normal((10, params_size)) - output_dist = layer(input_tensor) - - n_samples = 5 - samples = output_dist.sample(n_samples) - - assert samples.shape == (n_samples, 10, output_size) - - -@pytest.mark.parametrize("layer", LAYERS) -def test_probabilistic_model(layer): - """Test model compile with prob layers""" - - layer_class = getattr(probabilistic_layers, layer) - tfkl = tf.keras.layers - input_shape = [28, 28, 1] - encoded_shape = 2 - encoder = tf.keras.Sequential( - [ - tfkl.InputLayer(input_shape=input_shape), - tfkl.Flatten(), - tfkl.Dense(10, activation="relu"), - tfkl.Dense(layer_class.params_size(encoded_shape)), - layer_class(encoded_shape), - ] - ) - encoder.summary() - encoder.compile() - assert isinstance(encoder, tf.keras.Sequential) - model_output = encoder.layers[-1].output - assert not isinstance( - model_output, list - ), "The model output must be a single tensor!" - assert ( - len(model_output.shape) < 3 - ), "The model output must be a vector or a single value!" - - -@pytest.mark.parametrize("layer", LAYERS) -def test_probabilistic_model_predict(layer, features_dataset, targets_dataset): - batch_dims = ["forecast_reference_time", "t", "station"] - data = ( - Dataset.from_xarray_datasets(features_dataset, targets_dataset) - .stack(batch_dims) - .drop_nans() - ) - x_shape = data.x.shape - y_shape = data.y.shape - input_shape = x_shape[1] - output_size = y_shape[1] - model = models.fully_connected_network( - input_shape, output_size, hidden_layers=[3], probabilistic_layer=layer - ) - out_predict = model.predict(data.x) - assert isinstance(out_predict, np.ndarray) - assert out_predict.ndim == 2 - assert out_predict.shape[0] == data.y.shape[0] - assert out_predict.shape[1] == data.y.shape[-1] - out_distr = model(data.x) - assert isinstance(out_distr, tfp.distributions.Distribution) - num_samples = 2 - out_samples = out_distr.sample(num_samples) - assert isinstance(out_samples, tf.Tensor) - assert out_samples.ndim == 3 - assert out_samples.shape[0] == num_samples - assert out_samples.shape[1] == data.y.shape[0] - assert out_samples.shape[2] == data.y.shape[-1] + +from mlpp_lib.probabilistic_layers import ( + BaseDistributionLayer, + MultivariateGaussianTriLModule, + UnivariateCensoredGaussianModule, + UniveriateGaussianModule +) +from mlpp_lib.probabilistic_layers import MissingReparameterizationError + + +def test_multivariate_gaussian(): + distr = MultivariateGaussianTriLModule(dim=4) + multivariate_gaussian_layer = BaseDistributionLayer(distribution=distr, num_samples=21) + + inputs = torch.randn(16,8) + + # ensure you can sample, ie the generated matrix L is a valid Cholesky lower triangular + multivariate_gaussian_layer(inputs, output_type='samples') + + +def test_defense_missing_rsample(): + # censored normal does not have rsample so far + distr = UnivariateCensoredGaussianModule(a=-1., b=1.) + censored_gaussian_layer = BaseDistributionLayer(distribution=distr, num_samples=21) + # ensure that trying to call the layer in training mode requiring samples raises an error + with pytest.raises(MissingReparameterizationError): + censored_gaussian_layer(torch.randn(32,4), output_type='samples', training=True) + +@pytest.mark.parametrize("pattern", ['bsd', 'sbd'], ids=['batch first', 'samples first']) +def test_sampling_patterns(pattern): + distr = UniveriateGaussianModule() + + distr_layer = BaseDistributionLayer(distribution=distr) + batch_dim, samples, data_dim = 32,12,7 + inputs = torch.randn(batch_dim, data_dim) + + output = distr_layer(inputs, pattern=pattern, output_type='samples', num_samples=samples) + + if pattern == 'bsd': + assert output.shape == (batch_dim, samples, 1) + else: + assert output.shape == (samples, batch_dim, 1) + +# from inspect import getmembers, isclass + +# import numpy as np +# import pytest +# import tensorflow as tf +# import tensorflow_probability as tfp + +# from mlpp_lib import models +# from mlpp_lib import probabilistic_layers +# from mlpp_lib.datasets import Dataset + + +# LAYERS = [obj[0] for obj in getmembers(probabilistic_layers, isclass)] + + +# @pytest.mark.parametrize("layer", LAYERS) +# def test_probabilistic_layers(layer): + +# layer_class = getattr(probabilistic_layers, layer) + +# output_size = 2 +# params_size = layer_class.params_size(output_size) + +# # build +# layer = layer_class(output_size) + +# # call +# input_tensor = tf.random.normal((10, params_size)) +# output_dist = layer(input_tensor) + +# n_samples = 5 +# samples = output_dist.sample(n_samples) + +# assert samples.shape == (n_samples, 10, output_size) + + +# @pytest.mark.parametrize("layer", LAYERS) +# def test_probabilistic_model(layer): +# """Test model compile with prob layers""" + +# layer_class = getattr(probabilistic_layers, layer) +# tfkl = tf.keras.layers +# input_shape = [28, 28, 1] +# encoded_shape = 2 +# encoder = tf.keras.Sequential( +# [ +# tfkl.InputLayer(input_shape=input_shape), +# tfkl.Flatten(), +# tfkl.Dense(10, activation="relu"), +# tfkl.Dense(layer_class.params_size(encoded_shape)), +# layer_class(encoded_shape), +# ] +# ) +# encoder.summary() +# encoder.compile() +# assert isinstance(encoder, tf.keras.Sequential) +# model_output = encoder.layers[-1].output +# assert not isinstance( +# model_output, list +# ), "The model output must be a single tensor!" +# assert ( +# len(model_output.shape) < 3 +# ), "The model output must be a vector or a single value!" + + +# @pytest.mark.parametrize("layer", LAYERS) +# def test_probabilistic_model_predict(layer, features_dataset, targets_dataset): +# batch_dims = ["forecast_reference_time", "t", "station"] +# data = ( +# Dataset.from_xarray_datasets(features_dataset, targets_dataset) +# .stack(batch_dims) +# .drop_nans() +# ) +# x_shape = data.x.shape +# y_shape = data.y.shape +# input_shape = x_shape[1] +# output_size = y_shape[1] +# model = models.fully_connected_network( +# input_shape, output_size, hidden_layers=[3], probabilistic_layer=layer +# ) +# out_predict = model.predict(data.x) +# assert isinstance(out_predict, np.ndarray) +# assert out_predict.ndim == 2 +# assert out_predict.shape[0] == data.y.shape[0] +# assert out_predict.shape[1] == data.y.shape[-1] +# out_distr = model(data.x) +# assert isinstance(out_distr, tfp.distributions.Distribution) +# num_samples = 2 +# out_samples = out_distr.sample(num_samples) +# assert isinstance(out_samples, tf.Tensor) +# assert out_samples.ndim == 3 +# assert out_samples.shape[0] == num_samples +# assert out_samples.shape[1] == data.y.shape[0] +# assert out_samples.shape[2] == data.y.shape[-1] diff --git a/tests/test_save_model.py b/tests/test_save_model.py index b6746b5..629393c 100644 --- a/tests/test_save_model.py +++ b/tests/test_save_model.py @@ -1,174 +1,174 @@ -import subprocess -from inspect import getmembers, isfunction, isclass - -import numpy as np -import pytest -import tensorflow as tf -from tensorflow.keras import Model - -from mlpp_lib import models -from mlpp_lib import losses, metrics -from mlpp_lib import probabilistic_layers -from mlpp_lib.utils import get_loss, get_metric, get_optimizer - - -def _belongs_here(obj, module): - return obj[1].__module__ == module.__name__ - - -ALL_PROB_LAYERS = [ - obj[0] - for obj in getmembers(probabilistic_layers, isclass) - if _belongs_here(obj, probabilistic_layers) -] - -ALL_LOSSES = [ - obj[0] - for obj in getmembers(losses, isfunction) + getmembers(losses, isclass) - if _belongs_here(obj, losses) -] - -ALL_METRICS = [ - obj[0] - for obj in getmembers(metrics, isfunction) + getmembers(metrics, isclass) - if _belongs_here(obj, metrics) -] - - -TEST_LOSSES = [ - "crps_energy_ensemble", - "crps_energy", - {"WeightedCRPSEnergy": {"threshold": 0}}, - {"MultivariateLoss": {"metric": "mse"}}, -] - -TEST_METRICS = [ - "bias", - "mean_absolute_error", - {"MAEBusts": {"threshold": 0.5}}, -] - - -@pytest.mark.parametrize("save_format", ["tf", "keras"]) -@pytest.mark.parametrize("loss", TEST_LOSSES) -@pytest.mark.parametrize("prob_layer", ALL_PROB_LAYERS) -def test_save_model(save_format, loss, prob_layer, tmp_path): - """Test model save/load""" - - if save_format == "keras": - tmp_path = f"{tmp_path}.keras" - save_traces = True # default value - else: - tmp_path = f"{tmp_path}" - save_traces = False - - model = models.fully_connected_network( - (5,), - 2, - hidden_layers=[3], - probabilistic_layer=prob_layer, - mc_dropout=False, - ) - # The assertion below fails because of safety mechanism in keras against - # the deserialization of Lambda layers that we cannot switch off - # assert isinstance(model.from_config(model.get_config()), Model) - loss = get_loss(loss) - metrics = [get_metric(metric) for metric in TEST_METRICS] - model.compile(loss=loss, metrics=metrics) - if save_format != "keras": - model.save(tmp_path, save_traces=save_traces) - else: - model.save(tmp_path) - - # test trying to load the model from a new process - # this is a bit slow, since each process needs to reload all the dependencies ... - - # not compiling - args = [ - "python", - "-c", - "import tensorflow as tf;" - f"from mlpp_lib.probabilistic_layers import {prob_layer};" - f"tf.keras.saving.load_model('{tmp_path}', compile=False, safe_mode=False)", - ] - completed_process = subprocess.run(args, shell=True) - assert completed_process.returncode == 0, "failed to reload model" - - # compiling - args = [ - "python", - "-c", - "import tensorflow as tf;" - f"from mlpp_lib.losses import {loss};" - f"from mlpp_lib.probabilistic_layers import {prob_layer};" - f"tf.keras.saving.load_model('{tmp_path}', custom_objects={{'{loss}':{loss}}}, safe_mode=False)", - ] - completed_process = subprocess.run(args, shell=True) - assert completed_process.returncode == 0, "failed to reload model" - - input_arr = tf.random.uniform((1, 5)) - pred1 = model(input_arr) - del model - tf.keras.backend.clear_session() - model = tf.keras.saving.load_model(tmp_path, compile=False, safe_mode=False) - assert isinstance(model, Model) - - pred2 = model(input_arr) - try: - # Idependent layers have a "distribution" attribute - pred1_params = pred1.parameters["distribution"].parameters - pred2_params = pred2.parameters["distribution"].parameters - except KeyError: - pred1_params = pred1.parameters - pred2_params = pred2.parameters - - for param in pred1_params.keys(): - try: - param_array1 = pred1_params[param].numpy() - param_array2 = pred2_params[param].numpy() - except AttributeError: - continue - - np.testing.assert_allclose(param_array1, param_array2) - - -def test_save_model_mlflow(tmp_path): - """Test model save/load""" - pytest.importorskip("mlflow") - - import mlflow - - mlflow_uri = f"file://{tmp_path.absolute()}/mlruns" - mlflow.set_tracking_uri(mlflow_uri) - - model = models.fully_connected_network( - (5,), - 2, - hidden_layers=[3, 3], - dropout=0.5, - mc_dropout=True, - probabilistic_layer="IndependentNormal", - ) - optimizer = get_optimizer("Adam") - model.compile(optimizer=optimizer, loss=None, metrics=None) - custom_objects = tf.keras.layers.serialize(model) - - model_info = mlflow.tensorflow.log_model( - model, - "model_save", - custom_objects=custom_objects, - keras_model_kwargs={"save_format": "keras"}, - ) - - tf.keras.backend.clear_session() - - # this raises a ValueError because of the risk of deserializing Lambda layers - with pytest.raises(ValueError): - model = mlflow.tensorflow.load_model(model_info.model_uri) - - # this should work - model: tf.tensorflow.Model = mlflow.tensorflow.load_model( - model_info.model_uri, keras_model_kwargs={"safe_mode": False} - ) - - assert isinstance(model, Model) +# import subprocess +# from inspect import getmembers, isfunction, isclass + +# import numpy as np +# import pytest +# import tensorflow as tf +# from tensorflow.keras import Model + +# from mlpp_lib import models +# from mlpp_lib import losses, metrics +# from mlpp_lib import probabilistic_layers +# from mlpp_lib.utils import get_loss, get_metric, get_optimizer + + +# def _belongs_here(obj, module): +# return obj[1].__module__ == module.__name__ + + +# ALL_PROB_LAYERS = [ +# obj[0] +# for obj in getmembers(probabilistic_layers, isclass) +# if _belongs_here(obj, probabilistic_layers) +# ] + +# ALL_LOSSES = [ +# obj[0] +# for obj in getmembers(losses, isfunction) + getmembers(losses, isclass) +# if _belongs_here(obj, losses) +# ] + +# ALL_METRICS = [ +# obj[0] +# for obj in getmembers(metrics, isfunction) + getmembers(metrics, isclass) +# if _belongs_here(obj, metrics) +# ] + + +# TEST_LOSSES = [ +# "crps_energy_ensemble", +# "crps_energy", +# {"WeightedCRPSEnergy": {"threshold": 0}}, +# {"MultivariateLoss": {"metric": "mse"}}, +# ] + +# TEST_METRICS = [ +# "bias", +# "mean_absolute_error", +# {"MAEBusts": {"threshold": 0.5}}, +# ] + + +# @pytest.mark.parametrize("save_format", ["tf", "keras"]) +# @pytest.mark.parametrize("loss", TEST_LOSSES) +# @pytest.mark.parametrize("prob_layer", ALL_PROB_LAYERS) +# def test_save_model(save_format, loss, prob_layer, tmp_path): +# """Test model save/load""" + +# if save_format == "keras": +# tmp_path = f"{tmp_path}.keras" +# save_traces = True # default value +# else: +# tmp_path = f"{tmp_path}" +# save_traces = False + +# model = models.fully_connected_network( +# (5,), +# 2, +# hidden_layers=[3], +# probabilistic_layer=prob_layer, +# mc_dropout=False, +# ) +# # The assertion below fails because of safety mechanism in keras against +# # the deserialization of Lambda layers that we cannot switch off +# # assert isinstance(model.from_config(model.get_config()), Model) +# loss = get_loss(loss) +# metrics = [get_metric(metric) for metric in TEST_METRICS] +# model.compile(loss=loss, metrics=metrics) +# if save_format != "keras": +# model.save(tmp_path, save_traces=save_traces) +# else: +# model.save(tmp_path) + +# # test trying to load the model from a new process +# # this is a bit slow, since each process needs to reload all the dependencies ... + +# # not compiling +# args = [ +# "python", +# "-c", +# "import tensorflow as tf;" +# f"from mlpp_lib.probabilistic_layers import {prob_layer};" +# f"tf.keras.saving.load_model('{tmp_path}', compile=False, safe_mode=False)", +# ] +# completed_process = subprocess.run(args, shell=True) +# assert completed_process.returncode == 0, "failed to reload model" + +# # compiling +# args = [ +# "python", +# "-c", +# "import tensorflow as tf;" +# f"from mlpp_lib.losses import {loss};" +# f"from mlpp_lib.probabilistic_layers import {prob_layer};" +# f"tf.keras.saving.load_model('{tmp_path}', custom_objects={{'{loss}':{loss}}}, safe_mode=False)", +# ] +# completed_process = subprocess.run(args, shell=True) +# assert completed_process.returncode == 0, "failed to reload model" + +# input_arr = tf.random.uniform((1, 5)) +# pred1 = model(input_arr) +# del model +# tf.keras.backend.clear_session() +# model = tf.keras.saving.load_model(tmp_path, compile=False, safe_mode=False) +# assert isinstance(model, Model) + +# pred2 = model(input_arr) +# try: +# # Idependent layers have a "distribution" attribute +# pred1_params = pred1.parameters["distribution"].parameters +# pred2_params = pred2.parameters["distribution"].parameters +# except KeyError: +# pred1_params = pred1.parameters +# pred2_params = pred2.parameters + +# for param in pred1_params.keys(): +# try: +# param_array1 = pred1_params[param].numpy() +# param_array2 = pred2_params[param].numpy() +# except AttributeError: +# continue + +# np.testing.assert_allclose(param_array1, param_array2) + + +# def test_save_model_mlflow(tmp_path): +# """Test model save/load""" +# pytest.importorskip("mlflow") + +# import mlflow + +# mlflow_uri = f"file://{tmp_path.absolute()}/mlruns" +# mlflow.set_tracking_uri(mlflow_uri) + +# model = models.fully_connected_network( +# (5,), +# 2, +# hidden_layers=[3, 3], +# dropout=0.5, +# mc_dropout=True, +# probabilistic_layer="IndependentNormal", +# ) +# optimizer = get_optimizer("Adam") +# model.compile(optimizer=optimizer, loss=None, metrics=None) +# custom_objects = tf.keras.layers.serialize(model) + +# model_info = mlflow.tensorflow.log_model( +# model, +# "model_save", +# custom_objects=custom_objects, +# keras_model_kwargs={"save_format": "keras"}, +# ) + +# tf.keras.backend.clear_session() + +# # this raises a ValueError because of the risk of deserializing Lambda layers +# with pytest.raises(ValueError): +# model = mlflow.tensorflow.load_model(model_info.model_uri) + +# # this should work +# model: tf.tensorflow.Model = mlflow.tensorflow.load_model( +# model_info.model_uri, keras_model_kwargs={"safe_mode": False} +# ) + +# assert isinstance(model, Model) diff --git a/tests/test_train.py b/tests/test_train.py index c90100a..a94d041 100644 --- a/tests/test_train.py +++ b/tests/test_train.py @@ -1,18 +1,61 @@ +import torch +import scoringrules as sr +import keras +import pytest import json import cloudpickle import numpy as np import pytest -from tensorflow.keras import Model +from keras import Model import xarray as xr from mlpp_lib import train from mlpp_lib.normalizers import DataTransformer from mlpp_lib.datasets import DataModule, DataSplitter +from mlpp_lib.layers import FullyConnectedLayer +from mlpp_lib.losses import DistributionLossWrapper, SampleLossWrapper +from mlpp_lib.models import ProbabilisticModel +from mlpp_lib.probabilistic_layers import BaseDistributionLayer, UniveriateGaussianModule from .test_model_selection import ValidDataSplitterOptions +@pytest.mark.parametrize("loss_type", ['analytical', 'samples'], ids=['train=CRPS closed form', 'train=CRPS MC estimate']) +def test_train_noisy_polynomial(loss_type): + # test whether the model can learn y = x^2 + e ~ N(0,sigma) + num_samples = 1000 + x_values = torch.linspace(-1, 1, num_samples).reshape(-1,1) + + true_mean = x_values**2 # Mean centered at x^2 for x in [-1,1] + true_std = 0.05 + + # Generate the dataset in torch + y_values = torch.normal(mean=true_mean, std=true_std * torch.ones_like(true_mean)).reshape(-1,1) + + if loss_type == 'analytical': + crps_normal = DistributionLossWrapper(fn=sr.crps_normal) + else: + crps_normal = SampleLossWrapper(fn=sr.crps_ensemble, num_samples=100) + + prob_layer = BaseDistributionLayer(distribution=UniveriateGaussianModule(), num_samples=21) + encoder = FullyConnectedLayer(hidden_layers=[16,8], + batchnorm=False, + skip_connection=False, + activations='sigmoid') + + model = ProbabilisticModel(encoder_layer=encoder, probabilistic_layer=prob_layer) + + model(x_values[:100]) # infer shapes + model.compile(loss=crps_normal, optimizer=keras.optimizers.Adam(learning_rate=0.1)) + + history = model.fit(x=x_values, y=y_values, epochs=50, batch_size=200) + + # Assert it learned something + assert history.history['loss'][-1] < 0.05 + + + RUNS = [ # minimal set of parameters { @@ -25,119 +68,127 @@ "probabilistic_layer": "IndependentNormal", } }, - "loss": "crps_energy", - "optimizer": "RMSprop", - "callbacks": [ - {"EarlyStopping": {"patience": 10, "restore_best_weights": True}} - ], - }, - # use a more complicated loss function - { - "features": ["coe:x1"], - "targets": ["obs:y1"], - "normalizer": {"default": "MinMaxScaler"}, - "model": { - "fully_connected_network": { - "hidden_layers": [10], - "probabilistic_layer": "IndependentBeta", - } - }, - "loss": {"WeightedCRPSEnergy": {"threshold": 0, "n_samples": 5}}, - "optimizer": {"Adam": {"learning_rate": 0.1, "beta_1": 0.95}}, - "metrics": ["bias", "mean_absolute_error", {"MAEBusts": {"threshold": 0.5}}], - }, - # use a learning rate scheduler - { - "features": ["coe:x1"], - "targets": ["obs:y1"], - "normalizer": {"default": "MinMaxScaler"}, - "model": { - "fully_connected_network": { - "hidden_layers": [10], - "probabilistic_layer": "IndependentNormal", - } - }, - "loss": "crps_energy", - "optimizer": { - "Adam": { - "learning_rate": { - "CosineDecayRestarts": { - "initial_learning_rate": 0.001, - "first_decay_steps": 20, - "t_mul": 1.5, - "m_mul": 1.1, - "alpha": 0, - } - } - } + 'loss': { + 'DistributionLossWrapper': + 'scoringrules.crps_normal' + # {'scoringrules.crps_normal': + # { + # 'num_samples': 100, + # } + # } }, + "optimizer": "RMSprop", "callbacks": [ {"EarlyStopping": {"patience": 10, "restore_best_weights": True}} ], }, - # - { - "features": ["coe:x1"], - "targets": ["obs:y1"], - "normalizer": {"default": "MinMaxScaler"}, - "model": { - "fully_connected_network": { - "hidden_layers": [10], - "probabilistic_layer": "IndependentNormal", - "skip_connection": True, - } - }, - "loss": "crps_energy", - "metrics": ["bias"], - "callbacks": [ - { - "EarlyStopping": { - "patience": 10, - "restore_best_weights": True, - "verbose": 1, - } - }, - {"ReduceLROnPlateau": {"patience": 1, "verbose": 1}}, - {"EnsembleMetrics": {"thresholds": [0, 1, 2]}}, - ], - }, - # with multiscale CRPS loss - { - "features": ["coe:x1"], - "targets": ["obs:y1"], - "normalizer": {"default": "MinMaxScaler"}, - "model": { - "fully_connected_network": { - "hidden_layers": [10], - "probabilistic_layer": "IndependentNormal", - } - }, - "group_samples": {"t": 2}, - "loss": { - "MultiScaleCRPSEnergy": {"scales": [1, 2], "threshold": 0, "n_samples": 5} - }, - "metrics": ["bias"], - }, - # with combined loss - { - "features": ["coe:x1"], - "targets": ["obs:y1"], - "normalizer": {"default": "MinMaxScaler"}, - "model": { - "fully_connected_network": { - "hidden_layers": [10], - "probabilistic_layer": "IndependentNormal", - } - }, - "loss": { - "CombinedLoss": { - "losses": [ - {"BinaryClassifierLoss": {"threshold": 1}, "weight": 0.7}, - {"WeightedCRPSEnergy": {"threshold": 0.1}, "weight": 0.1}, - ], - } - }, - }, +# # use a more complicated loss function +# { +# "features": ["coe:x1"], +# "targets": ["obs:y1"], +# "normalizer": {"default": "MinMaxScaler"}, +# "model": { +# "fully_connected_network": { +# "hidden_layers": [10], +# "probabilistic_layer": "IndependentBeta", +# } +# }, +# "loss": {"WeightedCRPSEnergy": {"threshold": 0, "n_samples": 5}}, +# "optimizer": {"Adam": {"learning_rate": 0.1, "beta_1": 0.95}}, +# "metrics": ["bias", "mean_absolute_error", {"MAEBusts": {"threshold": 0.5}}], +# }, +# # use a learning rate scheduler +# { +# "features": ["coe:x1"], +# "targets": ["obs:y1"], +# "normalizer": {"default": "MinMaxScaler"}, +# "model": { +# "fully_connected_network": { +# "hidden_layers": [10], +# "probabilistic_layer": "IndependentNormal", +# } +# }, +# "loss": "crps_energy", +# "optimizer": { +# "Adam": { +# "learning_rate": { +# "CosineDecayRestarts": { +# "initial_learning_rate": 0.001, +# "first_decay_steps": 20, +# "t_mul": 1.5, +# "m_mul": 1.1, +# "alpha": 0, +# } +# } +# } +# }, +# "callbacks": [ +# {"EarlyStopping": {"patience": 10, "restore_best_weights": True}} +# ], +# }, +# # +# { +# "features": ["coe:x1"], +# "targets": ["obs:y1"], +# "normalizer": {"default": "MinMaxScaler"}, +# "model": { +# "fully_connected_network": { +# "hidden_layers": [10], +# "probabilistic_layer": "IndependentNormal", +# "skip_connection": True, +# } +# }, +# "loss": "crps_energy", +# "metrics": ["bias"], +# "callbacks": [ +# { +# "EarlyStopping": { +# "patience": 10, +# "restore_best_weights": True, +# "verbose": 1, +# } +# }, +# {"ReduceLROnPlateau": {"patience": 1, "verbose": 1}}, +# {"EnsembleMetrics": {"thresholds": [0, 1, 2]}}, +# ], +# }, +# # with multiscale CRPS loss +# { +# "features": ["coe:x1"], +# "targets": ["obs:y1"], +# "normalizer": {"default": "MinMaxScaler"}, +# "model": { +# "fully_connected_network": { +# "hidden_layers": [10], +# "probabilistic_layer": "IndependentNormal", +# } +# }, +# "group_samples": {"t": 2}, +# "loss": { +# "MultiScaleCRPSEnergy": {"scales": [1, 2], "threshold": 0, "n_samples": 5} +# }, +# "metrics": ["bias"], +# }, +# # with combined loss +# { +# "features": ["coe:x1"], +# "targets": ["obs:y1"], +# "normalizer": {"default": "MinMaxScaler"}, +# "model": { +# "fully_connected_network": { +# "hidden_layers": [10], +# "probabilistic_layer": "IndependentNormal", +# } +# }, +# "loss": { +# "CombinedLoss": { +# "losses": [ +# {"BinaryClassifierLoss": {"threshold": 1}, "weight": 0.7}, +# {"WeightedCRPSEnergy": {"threshold": 0.1}, "weight": 0.1}, +# ], +# } +# }, +# }, ] @@ -186,38 +237,38 @@ def test_train_fromfile(tmp_path, cfg): json.dumps(results[3]) -@pytest.mark.parametrize("cfg", RUNS) -def test_train_fromds(features_dataset, targets_dataset, cfg): - num_epochs = 3 - cfg.update({"epochs": num_epochs}) +# @pytest.mark.parametrize("cfg", RUNS) +# def test_train_fromds(features_dataset, targets_dataset, cfg): +# num_epochs = 3 +# cfg.update({"epochs": num_epochs}) - splitter_options = ValidDataSplitterOptions(time="lists", station="lists") - datasplitter = DataSplitter( - splitter_options.time_split, splitter_options.station_split - ) - datanormalizer = DataTransformer(**cfg["normalizer"]) - batch_dims = ["forecast_reference_time", "t", "station"] - datamodule = DataModule( - features_dataset[cfg["features"]], - targets_dataset[cfg["targets"]], - batch_dims, - splitter=datasplitter, - normalizer=datanormalizer, - group_samples=cfg.get("group_samples"), - ) - results = train.train(cfg, datamodule) +# splitter_options = ValidDataSplitterOptions(time="lists", station="lists") +# datasplitter = DataSplitter( +# splitter_options.time_split, splitter_options.station_split +# ) +# datanormalizer = DataTransformer(**cfg["normalizer"]) +# batch_dims = ["forecast_reference_time", "t", "station"] +# datamodule = DataModule( +# features_dataset[cfg["features"]], +# targets_dataset[cfg["targets"]], +# batch_dims, +# splitter=datasplitter, +# normalizer=datanormalizer, +# group_samples=cfg.get("group_samples"), +# ) +# results = train.train(cfg, datamodule) - assert len(results) == 4 - assert isinstance(results[0], Model) # model - assert isinstance(results[1], dict) # custom_objects - assert isinstance(results[2], DataTransformer) # normalizer - assert isinstance(results[3], dict) # history +# assert len(results) == 4 +# assert isinstance(results[0], Model) # model +# assert isinstance(results[1], dict) # custom_objects +# assert isinstance(results[2], DataTransformer) # normalizer +# assert isinstance(results[3], dict) # history - assert all([np.isfinite(v).all() for v in results[3].values()]) - assert all([len(v) == num_epochs for v in results[3].values()]) +# assert all([np.isfinite(v).all() for v in results[3].values()]) +# assert all([len(v) == num_epochs for v in results[3].values()]) - # try to pickle the custom objects - cloudpickle.dumps(results[1]) +# # try to pickle the custom objects +# cloudpickle.dumps(results[1]) - # try to dump fit history to json - json.dumps(results[3]) +# # try to dump fit history to json +# json.dumps(results[3]) diff --git a/tests/test_utils.py b/tests/test_utils.py index 076b2e4..ebcb2aa 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,35 +1,35 @@ -import xarray as xr +# import xarray as xr -from mlpp_lib import utils +# from mlpp_lib import utils -def test_as_weather(): - ds_in = xr.Dataset( - { - "wind_speed": ("x", [0, 1, 2]), - "source:wind_speed": ("x", [0, 1, 2]), - "asd": ("x", [0, 1, 2]), - } - ) - ds_out = utils.as_weather(ds_in) - xr.testing.assert_equal(ds_out, ds_in) +# def test_as_weather(): +# ds_in = xr.Dataset( +# { +# "wind_speed": ("x", [0, 1, 2]), +# "source:wind_speed": ("x", [0, 1, 2]), +# "asd": ("x", [0, 1, 2]), +# } +# ) +# ds_out = utils.as_weather(ds_in) +# xr.testing.assert_equal(ds_out, ds_in) - ds_in = xr.Dataset( - { - "cos_wind_from_direction": ("x", [0, 1, 2]), - "sin_wind_from_direction": ("x", [0, 1, 2]), - } - ) - ds_out = utils.as_weather(ds_in) - assert "wind_from_direction" in ds_out +# ds_in = xr.Dataset( +# { +# "cos_wind_from_direction": ("x", [0, 1, 2]), +# "sin_wind_from_direction": ("x", [0, 1, 2]), +# } +# ) +# ds_out = utils.as_weather(ds_in) +# assert "wind_from_direction" in ds_out - ds_in = xr.Dataset( - { - "northward_wind": ("x", [0, 1, 2]), - "eastward_wind": ("x", [0, 1, 2]), - } - ) - ds_out = utils.as_weather(ds_in) - assert "wind_from_direction" in ds_out - assert "wind_speed" in ds_out +# ds_in = xr.Dataset( +# { +# "northward_wind": ("x", [0, 1, 2]), +# "eastward_wind": ("x", [0, 1, 2]), +# } +# ) +# ds_out = utils.as_weather(ds_in) +# assert "wind_from_direction" in ds_out +# assert "wind_speed" in ds_out