diff --git a/docs/exporting_bayesflow_models.md b/docs/exporting_bayesflow_models.md new file mode 100644 index 0000000..bd0246d --- /dev/null +++ b/docs/exporting_bayesflow_models.md @@ -0,0 +1,245 @@ +# Exporting bayesflow-trained networks to ONNX + +LANfactory's [`transform_bayesflow_to_onnx`](api/onnx.md) is the bayesflow +sibling of [`transform_sbi_to_onnx`](exporting_sbi_models.md). It wraps a +trained [`bayesflow`](https://github.com/bayesflow-org/bayesflow) +`ContinuousApproximator` (NLE) or `RatioApproximator` (NRE) and writes a +single-trial ONNX file that HSSM's `loglik_kind="approx_differentiable"` +path can consume exactly like an sbi export. Same user gesture, same file +format, same HSSM-side loader — regardless of which training framework you +came from. + +## Installation + +```bash +pip install lanfactory[bayesflow] +``` + +The `bayesflow` extra pulls `bayesflow>=2.0.8` and `keras>=3.12`. For both +libraries side-by-side use `pip install lanfactory[all]`. + +## Critical: set the Keras backend before importing + +`torch.onnx.export` cannot trace a JAX-backed Keras model. You **must** set +`KERAS_BACKEND=torch` *before* importing keras or bayesflow: + +```python +import os +os.environ["KERAS_BACKEND"] = "torch" +# On Apple silicon, also pin to CPU — the orthogonal initializer needs +# torch.linalg.qr which MPS does not implement. +os.environ["KERAS_TORCH_DEVICE"] = "cpu" + +import bayesflow as bf # now safe +``` + +The exporter checks this and raises a clear `RuntimeError` if the backend is +anything other than `torch` at export time. + +## Quick start (NLE) + +```python +import os +os.environ["KERAS_BACKEND"] = "torch" +os.environ["KERAS_TORCH_DEVICE"] = "cpu" + +import bayesflow as bf +import keras +from bayesflow.datasets import OfflineDataset +from bayesflow.networks.inference.coupling.transforms import AffineTransform +from lanfactory.onnx import transform_bayesflow_to_onnx + +# 1. Build an ONNX-friendly ContinuousApproximator. +# NLE convention: inference_variables=x (obs), inference_conditions=θ. +approximator = bf.ContinuousApproximator( + inference_network=bf.networks.CouplingFlow( + depth=4, + subnet_kwargs={"widths": (64, 64), "activation": "silu", "dropout": None}, + permutation=None, # see Known constraints below + use_actnorm=False, + transform=AffineTransform(clamp=False), + ), + standardize="inference_variables", +) +approximator.build({ + "inference_variables": (None, x_dim), + "inference_conditions": (None, theta_dim), +}) +approximator.compile(optimizer=keras.optimizers.Adam(learning_rate=5e-4)) + +# 2. Train on your simulator output. +# `x` is observations, `theta` is parameters — numpy float32 arrays. +dataset = OfflineDataset( + data={"inference_variables": x, "inference_conditions": theta}, + batch_size=200, adapter=None, +) +approximator.fit(dataset=dataset, epochs=30, verbose=0) + +# 3. Export to a single ONNX file. +transform_bayesflow_to_onnx( + approximator, + "ddm_nle.onnx", + mode="nle", + example_theta_dim=theta_dim, + example_x_dim=x_dim, +) + +# 4. Hand it to HSSM exactly like an sbi or LAN file. +import hssm +model = hssm.HSSM( + data=obs_data, + model="ddm", + loglik_kind="approx_differentiable", + loglik="ddm_nle.onnx", + p_outlier=0, +) +idata = model.sample(sampler="numpyro", draws=500, tune=500, chains=2) +``` + +## Quick start (NRE) + +```python +approximator = bf.RatioApproximator( + inference_network=bf.networks.MLP( + widths=(64, 64), + activation="silu", + residual=False, + dropout=None, + ), + standardize="inference_variables", +) +# NRE convention: inference_variables=θ, inference_conditions=x. +approximator.build({ + "inference_variables": (None, theta_dim), + "inference_conditions": (None, x_dim), +}) +# ... train as above with the OfflineDataset keys flipped ... + +transform_bayesflow_to_onnx( + approximator, + "ddm_nre.onnx", + mode="nre", + example_theta_dim=theta_dim, + example_x_dim=x_dim, +) +``` + +The classifier logit is `log p(x|θ)/p(x) = log p(x|θ) − log p(x)`. The +θ-independent `log p(x)` term drops out under MCMC, so the raw logit is the +log-likelihood up to a constant. No Jacobian correction is needed — ratios +are invariant to z-score standardization. + +## Known constraints (v1) + +The constraints below were uncovered by the C-series validation spike. They +fall into four buckets. + +### 1. KERAS_BACKEND must be `torch` + +ONNX export goes through `torch.onnx.export`. Under `KERAS_BACKEND=jax` the +network weights live in JAX; tracing them with torch's exporter is not +supported. The exporter raises `RuntimeError` with a corrective hint. + +### 2. CouplingFlow knobs + +`bf.networks.CouplingFlow` has a few defaults that don't survive ONNX export +at opset 17/20. Override them at training time: + +| Knob | Required value | Why | +|---|---|---| +| `permutation` | `None` | `FixedPermutation` uses `keras.ops.take`, which lowers to `aten::ravel`. Neither opset 17 nor 20 implements it. | +| `use_actnorm` | `False` | Not validated in v1. May work; not tested. | +| `transform` | `AffineTransform(clamp=False)` (explicit instance) | Default `clamp=True` emits `ops.arcsinh`, which exports as `aten::asinh`. Unsupported in opset 17/20. Pass an explicit instance — bayesflow's `find_transform("affine")` silently drops `transform_kwargs` (upstream bug). | + +### 3. Subnet activation + +The default coupling MLP activation is `"hard_silu"` (HardSwish, the +piecewise-linear approximation to SiLU). PyTorch exports HardSwish as a +single fused ONNX op (`HardSwish`, added in opset 14) preserving the +efficiency motivation behind the function. jaxonnxruntime does not yet +implement a handler for that op. + +**Workaround**: use `"silu"` (the smooth Swish, `x · σ(x)`). It decomposes +to `Sigmoid + Mul` on export — primitive ops every runtime supports. The +two functions differ by at most ~0.14 across the real line (max around +`|x| ≈ 3`) and are interchangeable for SBI accuracy. Set: + +```python +subnet_kwargs={"widths": (...), "activation": "silu", "dropout": None} +``` + +`dropout=None` is recommended for a cleaner inference-time trace; the +trained weights are unchanged by this. + +### 4. Adapter must be identity + +The exporter raises `ValueError` if `approximator.adapter` contains any +transforms. The bayesflow `Adapter` pipeline is implemented in numpy +(dict reshuffling, log/sqrt transforms, scale, concat, etc.) and cannot +be baked into an ONNX graph in v1. + +**What you can use without an adapter**: the in-network `Standardize` +layer (via `standardize="inference_variables"` or `"all"`) IS tensor-based +and gets baked into the exported graph automatically, including the +correct Jacobian correction for absolute log-probability values. + +**What you cannot use**: `Adapter().log("rt").standardize(...).concatenate(...)` +style chains. Move pointwise transforms (log/sqrt of observations) into your +simulator output and apply them externally to your HSSM data before +sampling. + +### 5. Enable JAX x64 in the consuming process + +Same caveat as the sbi exporter — ONNX graphs from `torch.onnx.export` carry +int64 shape/index tensors. With JAX's default 32-bit mode, those get +silently truncated to int32, producing wrong log-prob outputs. Before +importing JAX in the consuming process: + +```python +import jax +jax.config.update("jax_enable_x64", True) +``` + +HSSM's `onnx2jax` consumer sets the related +`jaxort_only_allow_initializers_as_static_args = False` flag +automatically. The x64 setting is process-wide and must be opted into by +the caller. + +## Explicitly out of scope (v1) + +| Excluded | Reason | +|---|---| +| Discrete + continuous observations (MNLE-style) | bayesflow has no MNLE-equivalent approximator; would need new network types and training objectives. | +| Non-identity adapters | Numpy-only operations can't be baked into ONNX; see Constraint 4 above. Pointwise tensor adapter ops (log, sqrt, scale) are a candidate for v1.x. | +| Transformer / attention summary networks | Contain `LayerNormalization` (no jaxonnxruntime handler) and dynamic-shape attention. | +| FlowMatching, DiffusionModel, ConsistencyModel inference networks | `log_prob` requires ODE integration, not ONNX-exportable. | +| `KERAS_BACKEND=jax` workflows | Use the bayesflow LRE-style in-memory JAX callable path (see [`bayesflow_lre_integration.ipynb`](https://github.com/lnccbrown/HSSM/blob/main/docs/tutorials/bayesflow_lre_integration.ipynb) in HSSM). | + +## Numerical guarantees + +The bayesflow regression tests (`tests/test_bayesflow_*_export.py`) assert: + +- Forward pass: torch reference wrapper, `onnxruntime`, and + `jaxonnxruntime` all agree to `atol=1e-5` on fixed inputs. +- Gradients: `jax.grad` of the translated graph agrees with + `torch.autograd.grad` on the wrapped network to `atol=1e-4`. + +If you observe drift larger than these thresholds, please open an issue +with a minimal reproducer. + +## Two paths into HSSM, side by side + +| Path | Source library | Mechanism | When to use | +|---|---|---|---| +| `loglik="file.onnx"` | sbi or bayesflow | ONNX file, framework-agnostic | Portability, reproducibility, sharing trained surrogates | +| `loglik=` | bayesflow (LRE tutorial) | In-memory JAX callable | Fast iteration during model development; bayesflow-only | + +The two paths produce numerically equivalent results on the same trained +network. The ONNX path is what you'd ship; the JAX-callable path is what you'd +prototype with. + +## Related API + +- [`lanfactory.onnx.transform_bayesflow_to_onnx`](api/onnx.md) — this exporter. +- [`lanfactory.onnx.transform_sbi_to_onnx`](api/onnx.md) — the sbi sibling. +- [`lanfactory.onnx.transform_to_onnx`](api/onnx.md) — the LAN-MLP exporter. diff --git a/pyproject.toml b/pyproject.toml index 9bdf9b3..e9d1aba 100755 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,11 +53,15 @@ keywords = [ [project.optional-dependencies] mlflow = ["mlflow>=3.6.0"] hf = ["huggingface-hub>=0.20.0"] +sbi = ["sbi>=0.26", "nflows>=0.14"] +bayesflow = ["bayesflow>=2.0.8", "keras>=3.12"] all = [ "mlflow>=3.6.0", "huggingface-hub>=0.20.0", "sbi>=0.26", "nflows>=0.14", + "bayesflow>=2.0.8", + "keras>=3.12", ] [dependency-groups] @@ -82,6 +86,8 @@ dev = [ "jaxonnxruntime>=0.3", "onnxruntime>=1.17", "nflows>=0.14", + "bayesflow>=2.0.8", + "keras>=3.12", ] [tool.setuptools.packages.find] diff --git a/src/lanfactory/onnx/__init__.py b/src/lanfactory/onnx/__init__.py index 1924f47..0b25cb2 100755 --- a/src/lanfactory/onnx/__init__.py +++ b/src/lanfactory/onnx/__init__.py @@ -1,4 +1,9 @@ +from .bayesflow import transform_bayesflow_to_onnx from .sbi import transform_sbi_to_onnx from .transform_onnx import transform_to_onnx -__all__ = ["transform_to_onnx", "transform_sbi_to_onnx"] +__all__ = [ + "transform_to_onnx", + "transform_sbi_to_onnx", + "transform_bayesflow_to_onnx", +] diff --git a/src/lanfactory/onnx/bayesflow.py b/src/lanfactory/onnx/bayesflow.py new file mode 100644 index 0000000..d2e6904 --- /dev/null +++ b/src/lanfactory/onnx/bayesflow.py @@ -0,0 +1,352 @@ +"""Export trained bayesflow approximators to ONNX for HSSM consumption. + +The single public entry point is :func:`transform_bayesflow_to_onnx`, which +wraps a trained :class:`bayesflow.ContinuousApproximator` (NLE) or +:class:`bayesflow.RatioApproximator` (NRE) and writes a single-trial ONNX +graph that HSSM's ``loglik_kind="approx_differentiable"`` path can load via +``jaxonnxruntime``. + +This is the bayesflow sibling of :mod:`lanfactory.onnx.sbi` — same surface +shape, same single-trial rank-1 input convention, so HSSM consumes both +artifacts through the identical ``loglik="model.onnx"`` gesture. + +Input/output contract (matches the sbi exporter exactly) +--------------------------------------------------------- +The exported graph takes a **rank-1** tensor of shape ``(theta_dim + x_dim,)`` +with parameters first, observations second, and returns a **rank-0 scalar** +log-likelihood. HSSM vmaps the graph over trials; tracing with rank-1 dummy +keeps the resulting ``Slice`` ops on ``axes=[0]`` so the vmap path works. + +What gets baked into the trace +------------------------------ +The wrapper bypasses :meth:`bayesflow.ContinuousApproximator.log_prob` (which +runs the numpy adapter and emits dynamic-shape ``Tile`` / ``Where`` / +``Size`` ops via the ``Standardize`` Keras layer's per-batch log-Jacobian) +and instead: + +1. Pre-evaluates the standardizer's accumulated ``moving_mean`` / ``moving_std`` + to ``torch.nn.Module`` buffer constants. +2. Applies standardization inline as a static affine transform. +3. Calls ``approximator.inference_network.log_prob`` directly on torch tensors. +4. Adds the standardizer's constant Jacobian correction so the exported scalar + is an absolute log-likelihood (matches :meth:`approximator.log_prob` modulo + adapter ops, which v1 forbids). + +Hard constraints for v1 +----------------------- +* ``KERAS_BACKEND=torch`` at export time (``torch.onnx.export`` cannot trace a + JAX-backed Keras model). Set ``KERAS_TORCH_DEVICE=cpu`` on Apple silicon to + avoid MPS missing-op errors (``aten::linalg_qr.out``). +* The approximator's ``adapter`` must be the default identity ``Adapter()`` + (i.e. ``len(adapter.transforms) == 0``). Numpy-only adapter ops (log, sqrt, + concat, drop) cannot be baked into ONNX. We raise if any are present. +* The ``CouplingFlow`` inference network must use: + - ``permutation=None`` — ``FixedPermutation`` calls ``keras.ops.take`` which + lowers to ``aten::ravel``, unsupported in ONNX opsets 17/20. + - ``transform=AffineTransform(clamp=False)`` — bayesflow's + ``find_transform("affine")`` silently drops ``transform_kwargs`` + (upstream bug), and the default ``clamp=True`` emits ``aten::asinh`` + which neither opset 17 nor 20 can export. + - ``subnet_kwargs={"activation": ..., ...}`` — any smooth activation that + decomposes to base ONNX ops works (``"silu"`` → ``Sigmoid`` + ``Mul``, + ``"relu"`` → ``Relu``, ``"tanh"`` → ``Tanh``). The CouplingFlow default + ``"hard_silu"`` (= HardSwish, a piecewise-linear approximation of SiLU) + exports as the single ONNX op ``HardSwish`` (added in opset 14), which + jaxonnxruntime does not yet implement. Workarounds: pick ``"silu"`` at + training time, or upstream a ``HardSwish`` handler to jaxonnxruntime + (decomposition: ``x * Clip(x + 3, 0, 6) / 6``). + - ``use_actnorm=False`` (untested with ActNorm in v1). +* Continuous observations only. MNLE-style discrete + continuous mixes are + out of v1 scope (mirrors sbi v1 scope). + +These constraints are not all *enforced* by this function — some (network +internals) are documented in ``docs/exporting_bayesflow_models.md`` for the +user to satisfy at training time. We enforce what we can introspect cheaply. +""" + +from __future__ import annotations + +from typing import Any, Literal + +import numpy as np +import torch +from torch import nn + +__all__ = ["transform_bayesflow_to_onnx"] + + +def transform_bayesflow_to_onnx( + approximator: Any, + path: str, + *, + mode: Literal["nle", "nre"] = "nle", + example_theta_dim: int, + example_x_dim: int, + opset: int = 17, +) -> None: + """Export a trained bayesflow approximator to a single-trial ONNX graph. + + Parameters + ---------- + approximator + Trained bayesflow approximator. For ``mode="nle"`` this is a + :class:`bayesflow.ContinuousApproximator` whose ``inference_variables`` + slot was trained on the observation ``x`` (with + ``inference_conditions`` holding the parameters ``θ``). For + ``mode="nre"`` this is a :class:`bayesflow.RatioApproximator` trained + with the opposite convention (``inference_variables=θ``, + ``inference_conditions=x``). + path + Filesystem path to write the ``.onnx`` artifact to. + mode + ``"nle"`` exports ``log p(x|θ)`` with the standardizer Jacobian baked + in. ``"nre"`` exports the classifier logit as the log-likelihood up to + a θ-independent constant (which drops out in MCMC). + example_theta_dim + Parameter-vector dimensionality used to trace the graph. + example_x_dim + Observation-vector dimensionality used to trace the graph. + opset + ONNX opset version. Pinned to 17 by default for reproducibility against + ``jaxonnxruntime``. + + Raises + ------ + RuntimeError + If ``KERAS_BACKEND`` is not ``"torch"``. + TypeError + If ``mode`` is ``"nle"`` and the approximator does not have an + ``inference_network`` with ``.log_prob``; or if ``mode`` is ``"nre"`` + and the approximator does not have a ``projector``. + ValueError + If the approximator's ``adapter`` contains any non-trivial transforms, + or if ``mode`` is not one of ``"nle"``/``"nre"``. + """ + # The plan only ships KERAS_BACKEND=torch as a hard requirement; the + # device note is advisory and only matters on Apple silicon. + import keras # local import: don't force keras at LANfactory import time + + if keras.backend.backend() != "torch": + raise RuntimeError( + "transform_bayesflow_to_onnx requires KERAS_BACKEND='torch' " + "(got KERAS_BACKEND=" + f"'{keras.backend.backend()}'). torch.onnx.export cannot trace a " + "JAX-backed Keras model. Set the environment variable BEFORE " + "importing keras/bayesflow, e.g.:\n" + " import os\n" + " os.environ['KERAS_BACKEND'] = 'torch'\n" + " # on Apple silicon also: os.environ['KERAS_TORCH_DEVICE'] = 'cpu'\n" + " import bayesflow as bf" + ) + + _assert_identity_adapter(approximator) + + if mode == "nle": + wrapper: nn.Module = _BayesflowNLELogProbWrapper( + approximator, example_theta_dim, example_x_dim + ) + elif mode == "nre": + wrapper = _BayesflowNRELogRatioWrapper( + approximator, example_theta_dim, example_x_dim + ) + else: + raise ValueError(f"mode must be 'nle' or 'nre', got {mode!r}") + + wrapper.eval() + combined_input_dim = example_theta_dim + example_x_dim + # Rank-1 dummy: see module docstring for why this matters (vmap survival). + dummy_input = torch.randn(combined_input_dim, requires_grad=True) + torch.onnx.export( + wrapper, + dummy_input, + path, + dynamo=False, + opset_version=opset, + ) + + +def _assert_identity_adapter(approximator: Any) -> None: + """Raise if the approximator's Adapter has non-trivial transforms. + + The plan's locked-in v1 contract: bake only the tensor-based + ``standardize_layers`` (which we handle); error on any numpy-only adapter + op since those cannot live in an ONNX graph. + """ + adapter = getattr(approximator, "adapter", None) + if adapter is None: + return + transforms = getattr(adapter, "transforms", None) + if not transforms: + return + transform_names = [type(t).__name__ for t in transforms] + raise ValueError( + "transform_bayesflow_to_onnx requires an identity Adapter " + "(approximator.adapter must have no transforms). Found " + f"{len(transforms)} transform(s): {transform_names}. The numpy-based " + "Adapter pipeline cannot be exported to ONNX. Apply the adapter's " + "forward transform externally to your data before sampling, or retrain " + "the approximator without an adapter." + ) + + +def _frozen_mean_std( + standardizer: Any, key: str +) -> tuple[torch.Tensor | None, torch.Tensor | None]: + """Pre-evaluate a Standardize layer's moving mean/std to torch tensors. + + Going through ``Standardize.call`` at trace time emits ``Where`` / ``If`` + (from ``moving_std``'s ``where(m2>0, …, 1.0)``) and ``Shape`` / ``Size`` / + ``Tile`` (from the per-batch log-Jacobian tile). jaxonnxruntime cannot + execute ``Size``. By materializing mean and std now (training is done) we + sidestep every dynamic-shape construct. + """ + import keras + + if key not in standardizer.standardize_layers: + return None, None + layer = standardizer.standardize_layers[key] + mean = keras.ops.convert_to_numpy(layer.moving_mean[0]) + std = keras.ops.convert_to_numpy(layer.moving_std(0)) + return ( + torch.as_tensor(mean, dtype=torch.float32), + torch.as_tensor(std, dtype=torch.float32), + ) + + +class _BayesflowNLELogProbWrapper(nn.Module): + """Wrap a bayesflow ``ContinuousApproximator`` so forward(combined) returns log p(x|θ). + + NLE convention assumed: ``inference_variables = x`` (observation), + ``inference_conditions = θ``. The wrapper bakes the standardizer's + accumulated ``moving_mean`` / ``moving_std`` as constants and adds the + constant Jacobian correction so the exported scalar matches + :meth:`approximator.log_prob` (modulo the adapter, which v1 forbids). + """ + + def __init__( + self, approximator: Any, theta_dim: int, x_dim: int + ) -> None: + super().__init__() + if not hasattr(approximator, "inference_network"): + raise TypeError( + "NLE mode expects a bayesflow ContinuousApproximator with an " + f".inference_network attribute; got {type(approximator).__name__}." + ) + if not hasattr(approximator.inference_network, "log_prob"): + raise TypeError( + "NLE mode expects approximator.inference_network to expose " + f".log_prob(samples, conditions=...); got " + f"{type(approximator.inference_network).__name__}." + ) + + self.approximator = approximator + self.theta_dim = theta_dim + self.x_dim = x_dim + self.inference_network = approximator.inference_network + + x_mean, x_std = _frozen_mean_std( + approximator.standardizer, "inference_variables" + ) + th_mean, th_std = _frozen_mean_std( + approximator.standardizer, "inference_conditions" + ) + + if x_mean is not None: + self.register_buffer("_x_mean", x_mean) + self.register_buffer("_x_std", x_std) + # log|det J| of forward standardization x → (x − μ)/σ is −Σ log|σ|, + # a constant that doesn't depend on the input. We bake it in so the + # exported scalar is an absolute log p(x|θ). + self._x_ldj = float(-np.sum(np.log(np.abs(x_std.numpy())))) + else: + self._x_mean = None + self._x_std = None + self._x_ldj = 0.0 + + if th_mean is not None: + self.register_buffer("_th_mean", th_mean) + self.register_buffer("_th_std", th_std) + else: + self._th_mean = None + self._th_std = None + + def forward(self, combined: torch.Tensor) -> torch.Tensor: + # combined: rank-1, (theta_dim + x_dim,) — see module docstring. + theta = combined[: self.theta_dim].unsqueeze(0) + x_obs = combined[self.theta_dim :].unsqueeze(0) + + if self._x_mean is not None: + x_obs = (x_obs - self._x_mean) / self._x_std + if self._th_mean is not None: + theta = (theta - self._th_mean) / self._th_std + + logp = self.inference_network.log_prob(x_obs, conditions=theta) + return logp.reshape(()) + self._x_ldj + + +class _BayesflowNRELogRatioWrapper(nn.Module): + """Wrap a bayesflow ``RatioApproximator`` so forward(combined) returns log r(x, θ). + + NRE convention assumed: ``inference_variables = θ``, + ``inference_conditions = x``. We mirror the in-library ``logits`` method + (concat θ and x, push through inference_network, then projector, squeeze), + bypassing the contrastive-batch / ``log_ratio`` numpy gluing. The + projector's scalar logit IS log p(x,θ)/(p(x)p(θ)) up to a θ-independent + constant, which drops out of any MCMC accept ratio HSSM evaluates. + """ + + def __init__( + self, approximator: Any, theta_dim: int, x_dim: int + ) -> None: + super().__init__() + if not hasattr(approximator, "projector"): + raise TypeError( + "NRE mode expects a bayesflow RatioApproximator with a " + f".projector attribute; got {type(approximator).__name__}. " + "If this is a ContinuousApproximator (NLE), use mode='nle'." + ) + + self.approximator = approximator + self.theta_dim = theta_dim + self.x_dim = x_dim + self.inference_network = approximator.inference_network + self.projector = approximator.projector + + th_mean, th_std = _frozen_mean_std( + approximator.standardizer, "inference_variables" + ) + x_mean, x_std = _frozen_mean_std( + approximator.standardizer, "inference_conditions" + ) + + if th_mean is not None: + self.register_buffer("_th_mean", th_mean) + self.register_buffer("_th_std", th_std) + else: + self._th_mean = None + self._th_std = None + + if x_mean is not None: + self.register_buffer("_x_mean", x_mean) + self.register_buffer("_x_std", x_std) + else: + self._x_mean = None + self._x_std = None + + def forward(self, combined: torch.Tensor) -> torch.Tensor: + # combined: rank-1, [theta..., x...]. Internal ordering matches the + # bayesflow logits() convention: classifier_input = concat([θ, x]). + theta = combined[: self.theta_dim].unsqueeze(0) + x_obs = combined[self.theta_dim :].unsqueeze(0) + + if self._th_mean is not None: + theta = (theta - self._th_mean) / self._th_std + if self._x_mean is not None: + x_obs = (x_obs - self._x_mean) / self._x_std + + classifier_input = torch.cat([theta, x_obs], dim=-1) + hidden = self.inference_network(classifier_input, training=False) + logit = self.projector(hidden) + # logit shape (1, 1) → scalar. No Jacobian correction: a ratio of + # standardized densities equals the ratio of unstandardized densities. + return logit.reshape(()) diff --git a/tests/test_bayesflow_hssm_integration.py b/tests/test_bayesflow_hssm_integration.py new file mode 100644 index 0000000..54e57e7 --- /dev/null +++ b/tests/test_bayesflow_hssm_integration.py @@ -0,0 +1,171 @@ +"""End-to-end pipeline: bayesflow training → ONNX export → HSSM MCMC. + +Mirrors ``tests/test_sbi_hssm_integration.py`` so the bayesflow path gets the +same cross-repo coverage as the sbi path. + +1. Train a tiny bayesflow ``ContinuousApproximator`` (NLE) on synthetic DDM + data via ssm-simulators. +2. Export via ``lanfactory.onnx.transform_bayesflow_to_onnx``. +3. Build an HSSM model with ``loglik_kind="approx_differentiable"`` and + ``loglik=``. +4. Run a short MCMC and verify posterior means recover the ground truth + (within ±2σ) and r_hat is reasonable. + +Skip guard: requires HSSM importable in the test environment. LANfactory's +own CI does not currently install HSSM, so this test is a no-op locally — +it's intended for the coordinated cross-repo CI matrix. +""" + +# KERAS_BACKEND must be set BEFORE any keras / bayesflow import. +import os + +os.environ.setdefault("KERAS_BACKEND", "torch") +os.environ.setdefault("KERAS_TORCH_DEVICE", "cpu") + +from pathlib import Path # noqa: E402 + +import pytest # noqa: E402 + +hssm = pytest.importorskip("hssm") + +import numpy as np # noqa: E402 +import pandas as pd # noqa: E402 + +import bayesflow as bf # noqa: E402 +import keras # noqa: E402 +from bayesflow.datasets import OfflineDataset # noqa: E402 +from bayesflow.networks.inference.coupling.transforms import AffineTransform # noqa: E402 +from ssms.basic_simulators.simulator import simulator # noqa: E402 + +from lanfactory.onnx import transform_bayesflow_to_onnx # noqa: E402 + +_DDM_PARAM_NAMES = ["v", "a", "z", "t"] +_DDM_PARAM_LOW = np.array([-2.0, 0.6, 0.3, 0.1], dtype=np.float32) +_DDM_PARAM_HIGH = np.array([2.0, 1.8, 0.7, 0.5], dtype=np.float32) +_TRUE_THETA = np.array([0.5, 1.2, 0.5, 0.25], dtype=np.float32) +_N_OBS = 300 +_N_TRAIN = 5000 + + +def _simulate_ddm_rows(theta: np.ndarray) -> np.ndarray: + """Simulate one (rt, choice) per row of theta. Returns (n, 2) float32.""" + rts = np.empty(theta.shape[0], dtype=np.float32) + choices = np.empty(theta.shape[0], dtype=np.float32) + for i, th in enumerate(theta): + out = simulator(theta=th[None, :], model="ddm", n_samples=1) + rts[i] = out["rts"].squeeze() + choices[i] = out["choices"].squeeze() + return np.stack([rts, choices], axis=-1) + + +def _build_observed_dataframe() -> pd.DataFrame: + """Generate N_OBS trials at the true theta as an HSSM-shaped DataFrame.""" + out = simulator(theta=_TRUE_THETA[None, :], model="ddm", n_samples=_N_OBS) + rts = out["rts"].squeeze().astype(np.float32) + choices = out["choices"].squeeze().astype(np.float32) + return pd.DataFrame({"rt": rts, "response": choices}) + + +@pytest.fixture(scope="module") +def trained_bayesflow_for_ddm(tmp_path_factory) -> Path: + """Train tiny bayesflow CouplingFlow NLE on DDM and return the .onnx path.""" + keras.utils.set_random_seed(0) + rng = np.random.default_rng(0) + + theta = rng.uniform( + _DDM_PARAM_LOW, _DDM_PARAM_HIGH, size=(_N_TRAIN, len(_DDM_PARAM_NAMES)) + ).astype(np.float32) + x = _simulate_ddm_rows(theta).astype(np.float32) + + approximator = bf.ContinuousApproximator( + inference_network=bf.networks.CouplingFlow( + depth=4, + subnet_kwargs={ + "widths": (64, 64), + "activation": "silu", # see lanfactory.onnx.bayesflow on hard_silu + "dropout": None, + }, + permutation=None, # avoids aten::ravel in ONNX trace + use_actnorm=False, + transform=AffineTransform(clamp=False), + ), + standardize="inference_variables", # standardize x (rt, choice) + ) + approximator.build({ + "inference_variables": (None, 2), + "inference_conditions": (None, len(_DDM_PARAM_NAMES)), + }) + approximator.compile(optimizer=keras.optimizers.Adam(learning_rate=5e-4)) + + dataset = OfflineDataset( + data={"inference_variables": x, "inference_conditions": theta}, + batch_size=200, + adapter=None, + ) + approximator.fit(dataset=dataset, epochs=30, verbose=0) + + onnx_path = tmp_path_factory.mktemp("bf_ddm") / "ddm_nle.onnx" + transform_bayesflow_to_onnx( + approximator, + str(onnx_path), + mode="nle", + example_theta_dim=len(_DDM_PARAM_NAMES), + example_x_dim=2, + ) + return onnx_path + + +@pytest.mark.flaky(reruns=2, reruns_delay=5) +def test_hssm_model_builds_from_bayesflow_onnx(trained_bayesflow_for_ddm: Path) -> None: + """Exported ONNX loads cleanly into an HSSM DDM model.""" + obs_data = _build_observed_dataframe() + model = hssm.HSSM( + data=obs_data, + model="ddm", + loglik_kind="approx_differentiable", + loglik=str(trained_bayesflow_for_ddm), + p_outlier=0, + ) + assert model is not None + + +@pytest.mark.flaky(reruns=2, reruns_delay=5) +def test_hssm_mcmc_recovers_ddm_parameters_via_bayesflow( + trained_bayesflow_for_ddm: Path, +) -> None: + """Short MCMC should recover the true DDM params within ±2σ.""" + obs_data = _build_observed_dataframe() + model = hssm.HSSM( + data=obs_data, + model="ddm", + loglik_kind="approx_differentiable", + loglik=str(trained_bayesflow_for_ddm), + p_outlier=0, + ) + idata = model.sample( + draws=500, + tune=500, + chains=2, + cores=1, + progressbar=False, + target_accept=0.9, + ) + + if hasattr(hssm.utils, "summary"): + summary = hssm.utils.summary(idata) + else: + import arviz as az + summary = az.summary(idata, var_names=_DDM_PARAM_NAMES) + + posterior_means = summary.loc[_DDM_PARAM_NAMES, "mean"].to_numpy() + posterior_sds = summary.loc[_DDM_PARAM_NAMES, "sd"].to_numpy() + r_hats = summary.loc[_DDM_PARAM_NAMES, "r_hat"].to_numpy() + + assert (r_hats < 1.05).all(), f"r_hat above 1.05 for some params: {r_hats}" + + deviations = np.abs(posterior_means - _TRUE_THETA) / posterior_sds + assert (deviations < 2.0).all(), ( + f"Posterior means more than 2σ from truth: " + f"true={_TRUE_THETA}, mean={posterior_means}, sd={posterior_sds}, " + f"deviations={deviations}" + ) diff --git a/tests/test_bayesflow_nle_export.py b/tests/test_bayesflow_nle_export.py new file mode 100644 index 0000000..965948d --- /dev/null +++ b/tests/test_bayesflow_nle_export.py @@ -0,0 +1,290 @@ +"""Verify ``transform_bayesflow_to_onnx`` for the NLE (ContinuousApproximator) +path: tiny train, export, three-way numerical agreement, gradient agreement. + +Mirrors ``tests/test_sbi_nle_export.py`` so the bayesflow exporter inherits the +same coverage shape as the sbi exporter. The fixture bakes in the v1 +constraints (permutation=None, AffineTransform(clamp=False), silu activation, +no actnorm) — see lanfactory.onnx.bayesflow module docstring for the full list. +""" + +# KERAS_BACKEND must be set BEFORE importing keras / bayesflow; torch.onnx.export +# cannot trace a JAX-backed Keras model. KERAS_TORCH_DEVICE=cpu avoids the +# Apple-silicon MPS missing-op error in the orthogonal initializer (qr). +import os + +os.environ.setdefault("KERAS_BACKEND", "torch") +os.environ.setdefault("KERAS_TORCH_DEVICE", "cpu") + +from pathlib import Path # noqa: E402 + +import jax # noqa: E402 + +# Same reason as the sbi test: ONNX shape/index tensors are int64; JAX's default +# int32 silently truncates them inside jaxonnxruntime translation. +jax.config.update("jax_enable_x64", True) + +import jax.numpy as jnp # noqa: E402 +import numpy as np # noqa: E402 +import onnx # noqa: E402 +import onnxruntime as ort # noqa: E402 +import pytest # noqa: E402 +import torch # noqa: E402 +from jaxonnxruntime import call_onnx, config # noqa: E402 + +import bayesflow as bf # noqa: E402 +import keras # noqa: E402 +from bayesflow.datasets import OfflineDataset # noqa: E402 +from bayesflow.networks.inference.coupling.transforms import AffineTransform # noqa: E402 + +from lanfactory.onnx import transform_bayesflow_to_onnx # noqa: E402 + +# bayesflow under KERAS_BACKEND=torch globally disables autograd at import to +# avoid excessive memory in long training loops. Restore the global default so +# that subsequent tests (e.g. test_sbi_*) that rely on autograd-by-default work. +# Local code that needs gradients should always use ``with torch.enable_grad():`` +# explicitly — this re-enable is purely for cross-test hygiene. +torch.set_grad_enabled(True) + +config.update("jaxort_only_allow_initializers_as_static_args", False) + +_THETA_DIM = 2 +_X_DIM = 2 + + +def _gaussian_xs_for_theta(rng: np.random.Generator, theta: np.ndarray) -> np.ndarray: + """x | theta ~ N(theta, 0.5^2 I) — same toy distribution as the sbi test.""" + return (theta + 0.5 * rng.standard_normal(size=theta.shape)).astype(np.float32) + + +def _build_nle_approximator() -> bf.ContinuousApproximator: + """Construct a ContinuousApproximator with the v1 ONNX-friendly knobs.""" + return bf.ContinuousApproximator( + inference_network=bf.networks.CouplingFlow( + depth=4, + # silu decomposes to Sigmoid + Mul under ONNX; default hard_silu + # emits a single HardSwish op that jaxonnxruntime can't run. + subnet_kwargs={ + "widths": (32, 32), + "activation": "silu", + "dropout": None, + }, + # FixedPermutation uses keras.ops.take → aten::ravel, unsupported + # in ONNX opsets 17/20. + permutation=None, + use_actnorm=False, + # bayesflow's find_transform("affine") silently drops kwargs, so + # transform_kwargs={"clamp": False} would not take effect. Pass an + # explicit instance. clamp=False disables ops.arcsinh which + # neither opset 17 nor 20 supports. + transform=AffineTransform(clamp=False), + ), + standardize="inference_variables", # standardize x (obs) + ) + + +@pytest.fixture(scope="module") +def trained_nle() -> bf.ContinuousApproximator: + """Train the v1-friendly CouplingFlow NLE on the 2D Gaussian toy.""" + keras.utils.set_random_seed(0) + rng = np.random.default_rng(0) + + n_train = 2000 + theta = rng.uniform(-3.0, 3.0, size=(n_train, _THETA_DIM)).astype(np.float32) + x = _gaussian_xs_for_theta(rng, theta) + + approximator = _build_nle_approximator() + approximator.build({ + "inference_variables": (None, _X_DIM), + "inference_conditions": (None, _THETA_DIM), + }) + approximator.compile(optimizer=keras.optimizers.Adam(learning_rate=1e-3)) + + dataset = OfflineDataset( + data={"inference_variables": x, "inference_conditions": theta}, + batch_size=128, + adapter=None, + ) + approximator.fit(dataset=dataset, epochs=30, verbose=0) + return approximator + + +def _load_jax_runner(onnx_path: Path, combined: np.ndarray): + onnx_model = onnx.load(str(onnx_path)) + input_name = onnx_model.graph.input[0].name + model_func, model_weights = call_onnx.call_onnx_model( + onnx_model, {input_name: combined} + ) + run_func = jax.tree_util.Partial(model_func, model_weights) + return run_func, input_name + + +def _wrapper_reference_log_prob( + approximator: bf.ContinuousApproximator, + theta: torch.Tensor, + x: torch.Tensor, +) -> float: + """Compute the same scalar the exported graph emits, using the in-memory + wrapper. Avoids depending on approximator.log_prob (which goes through the + numpy adapter path and re-runs the standardizer).""" + from lanfactory.onnx.bayesflow import _BayesflowNLELogProbWrapper + + wrapper = _BayesflowNLELogProbWrapper(approximator, _THETA_DIM, _X_DIM) + wrapper.eval() + combined = torch.cat([theta.flatten(), x.flatten()], dim=-1) + with torch.no_grad(): + return float(wrapper(combined).item()) + + +@pytest.mark.flaky(reruns=2) +def test_nle_export_three_way_numerical_agreement( + trained_nle: bf.ContinuousApproximator, tmp_path: Path +) -> None: + onnx_path = tmp_path / "bayesflow_nle.onnx" + transform_bayesflow_to_onnx( + trained_nle, + str(onnx_path), + mode="nle", + example_theta_dim=_THETA_DIM, + example_x_dim=_X_DIM, + ) + + theta_t = torch.tensor([[0.5, -0.2]], dtype=torch.float32) + x_t = torch.tensor([[0.7, 0.3]], dtype=torch.float32) + combined = torch.cat([theta_t, x_t], dim=-1).squeeze(0).numpy() + + y_torch = _wrapper_reference_log_prob(trained_nle, theta_t, x_t) + + sess = ort.InferenceSession(str(onnx_path)) + input_name = sess.get_inputs()[0].name + y_ort = float(np.asarray(sess.run(None, {input_name: combined})[0]).flatten()[0]) + + run_func, jax_input_name = _load_jax_runner(onnx_path, combined) + y_jax = float(np.asarray(run_func({jax_input_name: combined})[0]).flatten()[0]) + + atol = 1e-5 + assert np.isclose(y_torch, y_ort, atol=atol), ( + f"torch wrapper vs onnxruntime: |Δ| = {abs(y_torch - y_ort)}" + ) + assert np.isclose(y_torch, y_jax, atol=atol), ( + f"torch wrapper vs jaxonnxruntime: |Δ| = {abs(y_torch - y_jax)}" + ) + assert np.isclose(y_ort, y_jax, atol=atol), ( + f"onnxruntime vs jaxonnxruntime: |Δ| = {abs(y_ort - y_jax)}" + ) + + +@pytest.mark.flaky(reruns=2) +def test_nle_export_gradient_agreement( + trained_nle: bf.ContinuousApproximator, tmp_path: Path +) -> None: + """jax.grad of the translated graph matches torch.autograd.grad of the wrapper.""" + onnx_path = tmp_path / "bayesflow_nle_grad.onnx" + transform_bayesflow_to_onnx( + trained_nle, + str(onnx_path), + mode="nle", + example_theta_dim=_THETA_DIM, + example_x_dim=_X_DIM, + ) + + from lanfactory.onnx.bayesflow import _BayesflowNLELogProbWrapper + + wrapper = _BayesflowNLELogProbWrapper(trained_nle, _THETA_DIM, _X_DIM) + wrapper.eval() + + theta_init = torch.tensor([0.5, -0.2], dtype=torch.float32) + x_init = torch.tensor([0.7, 0.3], dtype=torch.float32) + combined_t = torch.cat([theta_init, x_init], dim=-1).clone().requires_grad_(True) + with torch.enable_grad(): + y = wrapper(combined_t) + (grad_torch,) = torch.autograd.grad(y, combined_t) + grad_theta_torch = grad_torch[: _THETA_DIM].detach().numpy() + + combined_init = torch.cat([theta_init, x_init], dim=-1).numpy().astype(np.float32) + run_func, input_name = _load_jax_runner(onnx_path, combined_init) + + def jax_logp_of_theta(theta_arr: jnp.ndarray) -> jnp.ndarray: + combined = jnp.concatenate([theta_arr, jnp.asarray(x_init.numpy())], axis=-1) + return run_func({input_name: combined})[0].sum() + + grad_theta_jax = np.asarray( + jax.grad(jax_logp_of_theta)(jnp.asarray(theta_init.numpy())) + ) + + atol = 1e-4 + assert np.allclose(grad_theta_torch, grad_theta_jax, atol=atol), ( + f"torch vs jax theta-gradient mismatch: max |Δ| = " + f"{np.abs(grad_theta_torch - grad_theta_jax).max()} " + f"(torch={grad_theta_torch}, jax={grad_theta_jax})" + ) + + +def test_nle_log_prob_ordering_makes_sense( + trained_nle: bf.ContinuousApproximator, +) -> None: + """Sanity: trained N(theta, 0.5) should rank a near-mean point above a far one. + + Not a precision test — just confirms training produced a sensible likelihood + surface rather than a random one. + """ + theta = torch.tensor([[0.0, 0.0]], dtype=torch.float32) + x_near = torch.tensor([[0.0, 0.0]], dtype=torch.float32) + x_far = torch.tensor([[2.5, 2.5]], dtype=torch.float32) + + lp_near = _wrapper_reference_log_prob(trained_nle, theta, x_near) + lp_far = _wrapper_reference_log_prob(trained_nle, theta, x_far) + + assert lp_near > lp_far, ( + f"trained log-prob should be higher near the mean: " + f"lp_near={lp_near}, lp_far={lp_far}" + ) + + +def test_transform_rejects_wrong_backend(monkeypatch, tmp_path: Path) -> None: + """If KERAS_BACKEND != 'torch' at export time, raise a clear error.""" + + class _FakeApprox: + adapter = None + + monkeypatch.setattr(keras.backend, "backend", lambda: "jax") + with pytest.raises(RuntimeError, match="KERAS_BACKEND='torch'"): + transform_bayesflow_to_onnx( + _FakeApprox(), + str(tmp_path / "should_not_exist.onnx"), + mode="nle", + example_theta_dim=1, + example_x_dim=1, + ) + + +def test_transform_rejects_non_identity_adapter(tmp_path: Path) -> None: + """Adapter with any transform → ValueError with the offending name listed.""" + from bayesflow.adapters import Adapter + + class _FakeApprox: + # .log("foo") attaches a MapTransform; the specific op doesn't matter + # for the test — we just need a non-identity adapter. + adapter = Adapter().log("foo") + + with pytest.raises(ValueError, match="identity Adapter"): + transform_bayesflow_to_onnx( + _FakeApprox(), + str(tmp_path / "should_not_exist.onnx"), + mode="nle", + example_theta_dim=1, + example_x_dim=1, + ) + + +def test_nle_approximator_in_nre_mode_rejected( + trained_nle: bf.ContinuousApproximator, tmp_path: Path +) -> None: + """ContinuousApproximator passed with mode='nre' has no .projector → raise.""" + with pytest.raises(TypeError, match=r"\.projector"): + transform_bayesflow_to_onnx( + trained_nle, + str(tmp_path / "should_not_exist.onnx"), + mode="nre", + example_theta_dim=_THETA_DIM, + example_x_dim=_X_DIM, + ) diff --git a/tests/test_bayesflow_nre_export.py b/tests/test_bayesflow_nre_export.py new file mode 100644 index 0000000..4066201 --- /dev/null +++ b/tests/test_bayesflow_nre_export.py @@ -0,0 +1,230 @@ +"""Verify ``transform_bayesflow_to_onnx`` for the NRE (RatioApproximator) path. + +NRE convention (opposite of NLE): inference_variables=θ, inference_conditions=x. +The classifier logit IS log p(x|θ) - log p(x); HSSM only cares about the +θ-dependent part, so the constant log p(x) is irrelevant. No Jacobian +correction is needed — ratios are invariant under z-score standardization. +""" + +# KERAS_BACKEND must precede any keras / bayesflow import. See nle test for +# why; same reasoning here. +import os + +os.environ.setdefault("KERAS_BACKEND", "torch") +os.environ.setdefault("KERAS_TORCH_DEVICE", "cpu") + +from pathlib import Path # noqa: E402 + +import jax # noqa: E402 + +jax.config.update("jax_enable_x64", True) + +import jax.numpy as jnp # noqa: E402 +import numpy as np # noqa: E402 +import onnx # noqa: E402 +import onnxruntime as ort # noqa: E402 +import pytest # noqa: E402 +import torch # noqa: E402 +from jaxonnxruntime import call_onnx, config # noqa: E402 + +import bayesflow as bf # noqa: E402 +import keras # noqa: E402 +from bayesflow.datasets import OfflineDataset # noqa: E402 + +from lanfactory.onnx import transform_bayesflow_to_onnx # noqa: E402 + +# bayesflow under KERAS_BACKEND=torch globally disables autograd at import to +# avoid excessive memory in long training loops. Restore the global default so +# that subsequent tests (e.g. test_sbi_*) that rely on autograd-by-default work. +torch.set_grad_enabled(True) + +config.update("jaxort_only_allow_initializers_as_static_args", False) + +_THETA_DIM = 2 +_X_DIM = 2 + + +def _build_nre_approximator() -> bf.RatioApproximator: + """RatioApproximator with v1 ONNX-friendly knobs. + + The inference network is a small MLP classifier; we override to silu + (default ``mish`` exports as a fused op missing in jaxonnxruntime) and + disable residual/dropout for a clean inference-time trace. + """ + return bf.RatioApproximator( + inference_network=bf.networks.MLP( + widths=(32, 32), + activation="silu", + residual=False, + dropout=None, + ), + standardize="inference_variables", + K=4, + ) + + +@pytest.fixture(scope="module") +def trained_nre() -> bf.RatioApproximator: + """Train a tiny RatioApproximator on the same 2D Gaussian toy.""" + keras.utils.set_random_seed(0) + rng = np.random.default_rng(0) + n_train = 2000 + theta = rng.uniform(-3.0, 3.0, size=(n_train, _THETA_DIM)).astype(np.float32) + x = (theta + 0.5 * rng.standard_normal(size=(n_train, _X_DIM))).astype(np.float32) + + approximator = _build_nre_approximator() + approximator.build({ + "inference_variables": (None, _THETA_DIM), # NRE: θ here + "inference_conditions": (None, _X_DIM), # NRE: x here + }) + approximator.compile(optimizer=keras.optimizers.Adam(learning_rate=1e-3)) + + dataset = OfflineDataset( + data={"inference_variables": theta, "inference_conditions": x}, + batch_size=128, + adapter=None, + ) + approximator.fit(dataset=dataset, epochs=30, verbose=0) + return approximator + + +def _load_jax_runner(onnx_path: Path, combined: np.ndarray): + onnx_model = onnx.load(str(onnx_path)) + input_name = onnx_model.graph.input[0].name + model_func, model_weights = call_onnx.call_onnx_model( + onnx_model, {input_name: combined} + ) + run_func = jax.tree_util.Partial(model_func, model_weights) + return run_func, input_name + + +def _wrapper_reference_log_ratio( + approximator: bf.RatioApproximator, + theta: torch.Tensor, + x: torch.Tensor, +) -> float: + from lanfactory.onnx.bayesflow import _BayesflowNRELogRatioWrapper + + wrapper = _BayesflowNRELogRatioWrapper(approximator, _THETA_DIM, _X_DIM) + wrapper.eval() + combined = torch.cat([theta.flatten(), x.flatten()], dim=-1) + with torch.no_grad(): + return float(wrapper(combined).item()) + + +@pytest.mark.flaky(reruns=2) +def test_nre_export_three_way_numerical_agreement( + trained_nre: bf.RatioApproximator, tmp_path: Path +) -> None: + onnx_path = tmp_path / "bayesflow_nre.onnx" + transform_bayesflow_to_onnx( + trained_nre, + str(onnx_path), + mode="nre", + example_theta_dim=_THETA_DIM, + example_x_dim=_X_DIM, + ) + + theta_t = torch.tensor([[0.5, -0.2]], dtype=torch.float32) + x_t = torch.tensor([[0.7, 0.3]], dtype=torch.float32) + combined = torch.cat([theta_t, x_t], dim=-1).squeeze(0).numpy() + + y_torch = _wrapper_reference_log_ratio(trained_nre, theta_t, x_t) + + sess = ort.InferenceSession(str(onnx_path)) + input_name = sess.get_inputs()[0].name + y_ort = float(np.asarray(sess.run(None, {input_name: combined})[0]).flatten()[0]) + + run_func, jax_input_name = _load_jax_runner(onnx_path, combined) + y_jax = float(np.asarray(run_func({jax_input_name: combined})[0]).flatten()[0]) + + atol = 1e-5 + assert np.isclose(y_torch, y_ort, atol=atol), ( + f"torch wrapper vs onnxruntime: |Δ| = {abs(y_torch - y_ort)}" + ) + assert np.isclose(y_torch, y_jax, atol=atol), ( + f"torch wrapper vs jaxonnxruntime: |Δ| = {abs(y_torch - y_jax)}" + ) + assert np.isclose(y_ort, y_jax, atol=atol), ( + f"onnxruntime vs jaxonnxruntime: |Δ| = {abs(y_ort - y_jax)}" + ) + + +@pytest.mark.flaky(reruns=2) +def test_nre_export_gradient_agreement( + trained_nre: bf.RatioApproximator, tmp_path: Path +) -> None: + """jax.grad of the translated graph matches torch.autograd.grad of the wrapper.""" + onnx_path = tmp_path / "bayesflow_nre_grad.onnx" + transform_bayesflow_to_onnx( + trained_nre, + str(onnx_path), + mode="nre", + example_theta_dim=_THETA_DIM, + example_x_dim=_X_DIM, + ) + + from lanfactory.onnx.bayesflow import _BayesflowNRELogRatioWrapper + + wrapper = _BayesflowNRELogRatioWrapper(trained_nre, _THETA_DIM, _X_DIM) + wrapper.eval() + + theta_init = torch.tensor([0.5, -0.2], dtype=torch.float32) + x_init = torch.tensor([0.7, 0.3], dtype=torch.float32) + combined_t = torch.cat([theta_init, x_init], dim=-1).clone().requires_grad_(True) + with torch.enable_grad(): + y = wrapper(combined_t) + (grad_torch,) = torch.autograd.grad(y, combined_t) + grad_theta_torch = grad_torch[: _THETA_DIM].detach().numpy() + + combined_init = torch.cat([theta_init, x_init], dim=-1).numpy().astype(np.float32) + run_func, input_name = _load_jax_runner(onnx_path, combined_init) + + def jax_logr_of_theta(theta_arr: jnp.ndarray) -> jnp.ndarray: + combined = jnp.concatenate([theta_arr, jnp.asarray(x_init.numpy())], axis=-1) + return run_func({input_name: combined})[0].sum() + + grad_theta_jax = np.asarray( + jax.grad(jax_logr_of_theta)(jnp.asarray(theta_init.numpy())) + ) + + atol = 1e-4 + assert np.allclose(grad_theta_torch, grad_theta_jax, atol=atol), ( + f"torch vs jax theta-gradient mismatch: max |Δ| = " + f"{np.abs(grad_theta_torch - grad_theta_jax).max()} " + f"(torch={grad_theta_torch}, jax={grad_theta_jax})" + ) + + +def test_nre_log_ratio_ordering_makes_sense( + trained_nre: bf.RatioApproximator, +) -> None: + """Trained NRE: log r(x | θ_near) > log r(x | θ_far) when x is near the true θ. + + The classifier learns higher logits for compatible (θ, x) pairs. + """ + x = torch.tensor([[0.0, 0.0]], dtype=torch.float32) + theta_near = torch.tensor([[0.0, 0.0]], dtype=torch.float32) + theta_far = torch.tensor([[2.5, 2.5]], dtype=torch.float32) + + lr_near = _wrapper_reference_log_ratio(trained_nre, theta_near, x) + lr_far = _wrapper_reference_log_ratio(trained_nre, theta_far, x) + + assert lr_near > lr_far, ( + f"trained log-ratio should be higher for compatible (θ, x): " + f"lr_near={lr_near}, lr_far={lr_far}" + ) + + +def test_nre_approximator_in_nle_mode_rejected( + trained_nre: bf.RatioApproximator, tmp_path: Path +) -> None: + """RatioApproximator in mode='nle' should fail: inference_network has no .log_prob.""" + with pytest.raises(TypeError, match=r"\.log_prob"): + transform_bayesflow_to_onnx( + trained_nre, + str(tmp_path / "should_not_exist.onnx"), + mode="nle", + example_theta_dim=_THETA_DIM, + example_x_dim=_X_DIM, + ) diff --git a/uv.lock b/uv.lock index bd4656f..1cf4680 100644 --- a/uv.lock +++ b/uv.lock @@ -2,7 +2,9 @@ version = 1 revision = 1 requires-python = ">3.10, <3.14" resolution-markers = [ - "python_full_version >= '3.13'", + "python_full_version >= '3.13' and sys_platform == 'win32'", + "python_full_version >= '3.13' and sys_platform == 'emscripten'", + "python_full_version >= '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.12.*'", "python_full_version == '3.11.*'", "python_full_version < '3.11'", @@ -168,6 +170,53 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537 }, ] +[[package]] +name = "bayesflow" +version = "2.0.8" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "keras", version = "3.12.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "matplotlib", marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "pandas", marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "seaborn", marker = "python_full_version < '3.11'" }, + { name = "tqdm", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/24/89/2505a7872708f13fab17ca41428e937b2a1f620e34be4cb9a23b6e45c95b/bayesflow-2.0.8.tar.gz", hash = "sha256:a9817c634b02650ef130bba073b4b7275014b003ddbdffe9e85566462b1e8104", size = 295818 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/1f/f315232c56a3a38ca234eb2e63de48883f9fcf93a1733e47c1f8d4738970/bayesflow-2.0.8-py3-none-any.whl", hash = "sha256:d9c184e016ed8a7f124ff67c0e07750ddf4fc99b266559eb7eb6cba3e21823ba", size = 456373 }, +] + +[[package]] +name = "bayesflow" +version = "2.0.12" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.13' and sys_platform == 'win32'", + "python_full_version >= '3.13' and sys_platform == 'emscripten'", + "python_full_version >= '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*'", + "python_full_version == '3.11.*'", +] +dependencies = [ + { name = "keras", version = "3.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "matplotlib", marker = "python_full_version >= '3.11'" }, + { name = "networkx", version = "3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "pandas", marker = "python_full_version >= '3.11'" }, + { name = "scipy", version = "1.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "seaborn", marker = "python_full_version >= '3.11'" }, + { name = "tqdm", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/53/72/85259caf0339d37f558b96ba27a533d7db34f4dd1b7a300f5f6c5a49d4d3/bayesflow-2.0.12.tar.gz", hash = "sha256:9a5d46389cc4cb6f0923669c24b074df6d8e6b8cf8b1e78674279818dc03e7a3", size = 327701 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/e4/478efa5f63270a706e0970e9ea7daf4c1900528a516580967bd8751800bd/bayesflow-2.0.12-py3-none-any.whl", hash = "sha256:6dec1fa022ef9366d21250291d52c474d93f8a654cad9a7cd89ba9599cc0681f", size = 511224 }, +] + [[package]] name = "beautifulsoup4" version = "4.13.4" @@ -1103,7 +1152,7 @@ name = "gunicorn" version = "23.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "packaging" }, + { name = "packaging", marker = "python_full_version < '3.13' or sys_platform != 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/34/72/9614c465dc206155d93eff0ca20d42e1e35afc533971379482de953521a4/gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec", size = 375031 } wheels = [ @@ -1119,6 +1168,49 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 }, ] +[[package]] +name = "h5py" +version = "3.16.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/db/33/acd0ce6863b6c0d7735007df01815403f5589a21ff8c2e1ee2587a38f548/h5py-3.16.0.tar.gz", hash = "sha256:a0dbaad796840ccaa67a4c144a0d0c8080073c34c76d5a6941d6818678ef2738", size = 446526 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/6b/231413e58a787a89b316bb0d1777da3c62257e4797e09afd8d17ad3549dc/h5py-3.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e06f864bedb2c8e7c1358e6c73af48519e317457c444d6f3d332bb4e8fa6d7d9", size = 3724137 }, + { url = "https://files.pythonhosted.org/packages/74/f9/557ce3aad0fe8471fb5279bab0fc56ea473858a022c4ce8a0b8f303d64e9/h5py-3.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ec86d4fffd87a0f4cb3d5796ceb5a50123a2a6d99b43e616e5504e66a953eca3", size = 3090112 }, + { url = "https://files.pythonhosted.org/packages/7a/f5/e15b3d0dc8a18e56409a839e6468d6fb589bc5207c917399c2e0706eeb44/h5py-3.16.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:86385ea895508220b8a7e45efa428aeafaa586bd737c7af9ee04661d8d84a10d", size = 4844847 }, + { url = "https://files.pythonhosted.org/packages/cb/92/a8851d936547efe30cc0ce5245feac01f3ec6171f7899bc3f775c72030b3/h5py-3.16.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:8975273c2c5921c25700193b408e28d6bdd0111c37468b2d4e25dcec4cd1d84d", size = 5065352 }, + { url = "https://files.pythonhosted.org/packages/2b/ae/f2adc5d0ca9626db3277a3d87516e124cbc5d0eea0bd79bc085702d04f2c/h5py-3.16.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1677ad48b703f44efc9ea0c3ab284527f81bc4f318386aaaebc5fede6bbae56f", size = 4839173 }, + { url = "https://files.pythonhosted.org/packages/64/0b/e0c8c69da1d8838da023a50cd3080eae5d475691f7636b35eff20bb6ef20/h5py-3.16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c4dd4cf5f0a4e36083f73172f6cfc25a5710789269547f132a20975bfe2434c", size = 5076216 }, + { url = "https://files.pythonhosted.org/packages/66/35/d88fd6718832133c885004c61ceeeb24dbd6397ef877dbed6b3a64d6a286/h5py-3.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:bdef06507725b455fccba9c16529121a5e1fbf56aa375f7d9713d9e8ff42454d", size = 3183639 }, + { url = "https://files.pythonhosted.org/packages/ba/95/a825894f3e45cbac7554c4e97314ce886b233a20033787eda755ca8fecc7/h5py-3.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:719439d14b83f74eeb080e9650a6c7aa6d0d9ea0ca7f804347b05fac6fbf18af", size = 3721663 }, + { url = "https://files.pythonhosted.org/packages/bf/3b/38ff88b347c3e346cda1d3fc1b65a7aa75d40632228d8b8a5d7b58508c24/h5py-3.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c3f0a0e136f2e95dd0b67146abb6668af4f1a69c81ef8651a2d316e8e01de447", size = 3087630 }, + { url = "https://files.pythonhosted.org/packages/98/a8/2594cef906aee761601eff842c7dc598bea2b394a3e1c00966832b8eeb7c/h5py-3.16.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:a6fbc5367d4046801f9b7db9191b31895f22f1c6df1f9987d667854cac493538", size = 4823472 }, + { url = "https://files.pythonhosted.org/packages/52/a0/c1f604538ff6db22a0690be2dc44ab59178e115f63c917794e529356ab23/h5py-3.16.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:fb1720028d99040792bb2fb31facb8da44a6f29df7697e0b84f0d79aff2e9bd3", size = 5027150 }, + { url = "https://files.pythonhosted.org/packages/2e/fd/301739083c2fc4fd89950f9bcfce75d6e14b40b0ca3d40e48a8993d1722c/h5py-3.16.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:314b6054fe0b1051c2b0cb2df5cbdab15622fb05e80f202e3b6a5eee0d6fe365", size = 4814544 }, + { url = "https://files.pythonhosted.org/packages/4c/42/2193ed41ccee78baba8fcc0cff2c925b8b9ee3793305b23e1f22c20bf4c7/h5py-3.16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ffbab2fedd6581f6aa31cf1639ca2cb86e02779de525667892ebf4cc9fd26434", size = 5034013 }, + { url = "https://files.pythonhosted.org/packages/f7/20/e6c0ff62ca2ad1a396a34f4380bafccaaf8791ff8fccf3d995a1fc12d417/h5py-3.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:17d1f1630f92ad74494a9a7392ab25982ce2b469fc62da6074c0ce48366a2999", size = 3191673 }, + { url = "https://files.pythonhosted.org/packages/f2/48/239cbe352ac4f2b8243a8e620fa1a2034635f633731493a7ff1ed71e8658/h5py-3.16.0-cp311-cp311-win_arm64.whl", hash = "sha256:85b9c49dd58dc44cf70af944784e2c2038b6f799665d0dcbbc812a26e0faa859", size = 2673834 }, + { url = "https://files.pythonhosted.org/packages/c8/c0/5d4119dba94093bbafede500d3defd2f5eab7897732998c04b54021e530b/h5py-3.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c5313566f4643121a78503a473f0fb1e6dcc541d5115c44f05e037609c565c4d", size = 3685604 }, + { url = "https://files.pythonhosted.org/packages/b0/42/c84efcc1d4caebafb1ecd8be4643f39c85c47a80fe254d92b8b43b1eadaf/h5py-3.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:42b012933a83e1a558c673176676a10ce2fd3759976a0fedee1e672d1e04fc9d", size = 3061940 }, + { url = "https://files.pythonhosted.org/packages/89/84/06281c82d4d1686fde1ac6b0f307c50918f1c0151062445ab3b6fa5a921d/h5py-3.16.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:ff24039e2573297787c3063df64b60aab0591980ac898329a08b0320e0cf2527", size = 5198852 }, + { url = "https://files.pythonhosted.org/packages/9e/e9/1a19e42cd43cc1365e127db6aae85e1c671da1d9a5d746f4d34a50edb577/h5py-3.16.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:dfc21898ff025f1e8e67e194965a95a8d4754f452f83454538f98f8a3fcb207e", size = 5405250 }, + { url = "https://files.pythonhosted.org/packages/b7/8e/9790c1655eabeb85b92b1ecab7d7e62a2069e53baefd58c98f0909c7a948/h5py-3.16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:698dd69291272642ffda44a0ecd6cd3bda5faf9621452d255f57ce91487b9794", size = 5190108 }, + { url = "https://files.pythonhosted.org/packages/51/d7/ab693274f1bd7e8c5f9fdd6c7003a88d59bedeaf8752716a55f532924fbb/h5py-3.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2b2c02b0a160faed5fb33f1ba8a264a37ee240b22e049ecc827345d0d9043074", size = 5419216 }, + { url = "https://files.pythonhosted.org/packages/03/c1/0976b235cf29ead553e22f2fb6385a8252b533715e00d0ae52ed7b900582/h5py-3.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:96b422019a1c8975c2d5dadcf61d4ba6f01c31f92bbde6e4649607885fe502d6", size = 3182868 }, + { url = "https://files.pythonhosted.org/packages/14/d9/866b7e570b39070f92d47b0ff1800f0f8239b6f9e45f02363d7112336c1f/h5py-3.16.0-cp312-cp312-win_arm64.whl", hash = "sha256:39c2838fb1e8d97bcf1755e60ad1f3dd76a7b2a475928dc321672752678b96db", size = 2653286 }, + { url = "https://files.pythonhosted.org/packages/0f/9e/6142ebfda0cb6e9349c091eae73c2e01a770b7659255248d637bec54a88b/h5py-3.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:370a845f432c2c9619db8eed334d1e610c6015796122b0e57aa46312c22617d9", size = 3671808 }, + { url = "https://files.pythonhosted.org/packages/b0/65/5e088a45d0f43cd814bc5bec521c051d42005a472e804b1a36c48dada09b/h5py-3.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:42108e93326c50c2810025aade9eac9d6827524cdccc7d4b75a546e5ab308edb", size = 3045837 }, + { url = "https://files.pythonhosted.org/packages/da/1e/6172269e18cc5a484e2913ced33339aad588e02ba407fafd00d369e22ef3/h5py-3.16.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:099f2525c9dcf28de366970a5fb34879aab20491589fa89ce2863a84218bb524", size = 5193860 }, + { url = "https://files.pythonhosted.org/packages/bd/98/ef2b6fe2903e377cbe870c3b2800d62552f1e3dbe81ce49e1923c53d1c5c/h5py-3.16.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:9300ad32dea9dfc5171f94d5f6948e159ed93e4701280b0f508773b3f582f402", size = 5400417 }, + { url = "https://files.pythonhosted.org/packages/bc/81/5b62d760039eed64348c98129d17061fdfc7839fc9c04eaaad6dee1004e4/h5py-3.16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:171038f23bccddfc23f344cadabdfc9917ff554db6a0d417180d2747fe4c75a7", size = 5185214 }, + { url = "https://files.pythonhosted.org/packages/28/c4/532123bcd9080e250696779c927f2cb906c8bf3447df98f5ceb8dcded539/h5py-3.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7e420b539fb6023a259a1b14d4c9f6df8cf50d7268f48e161169987a57b737ff", size = 5414598 }, + { url = "https://files.pythonhosted.org/packages/c3/d9/a27997f84341fc0dfcdd1fe4179b6ba6c32a7aa880fdb8c514d4dad6fba3/h5py-3.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:18f2bbcd545e6991412253b98727374c356d67caa920e68dc79eab36bf5fedad", size = 3175509 }, + { url = "https://files.pythonhosted.org/packages/a5/23/bb8647521d4fd770c30a76cfc6cb6a2f5495868904054e92f2394c5a78ff/h5py-3.16.0-cp313-cp313-win_arm64.whl", hash = "sha256:656f00e4d903199a1d58df06b711cf3ca632b874b4207b7dbec86185b5c8c7d4", size = 2647362 }, +] + [[package]] name = "hf-xet" version = "1.2.0" @@ -1311,7 +1403,9 @@ name = "ipython" version = "9.4.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.13'", + "python_full_version >= '3.13' and sys_platform == 'win32'", + "python_full_version >= '3.13' and sys_platform == 'emscripten'", + "python_full_version >= '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.12.*'", "python_full_version == '3.11.*'", ] @@ -1703,6 +1797,54 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/43/6a/ca128561b22b60bd5a0c4ea26649e68c8556b82bc70a0c396eebc977fe86/jupyterlab_widgets-3.0.15-py3-none-any.whl", hash = "sha256:d59023d7d7ef71400d51e6fee9a88867f6e65e10a4201605d2d7f3e8f012a31c", size = 216571 }, ] +[[package]] +name = "keras" +version = "3.12.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "absl-py", marker = "python_full_version < '3.11'" }, + { name = "h5py", marker = "python_full_version < '3.11'" }, + { name = "ml-dtypes", marker = "python_full_version < '3.11'" }, + { name = "namex", marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "optree", marker = "python_full_version < '3.11'" }, + { name = "packaging", marker = "python_full_version < '3.11'" }, + { name = "rich", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/73/19e057f7a2a6d641246bacca21e0bbcb2be341afca98ea461a0f2a9ab92d/keras-3.12.2.tar.gz", hash = "sha256:e19c7c7f8f2a81e44d4f203e567731a15a270d8ef351060982b45a1fafdf3fce", size = 1129833 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/55/ba/1f2daa7940d7c5c65efc85370453e9e67ace0d06b4a7346b53f0e7355453/keras-3.12.2-py3-none-any.whl", hash = "sha256:0433310d7d626d5cbbc58e98223b3a77ce7d7d4398bf7e169d4e8bdcf9ce0296", size = 1476474 }, +] + +[[package]] +name = "keras" +version = "3.14.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.13' and sys_platform == 'win32'", + "python_full_version >= '3.13' and sys_platform == 'emscripten'", + "python_full_version >= '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*'", + "python_full_version == '3.11.*'", +] +dependencies = [ + { name = "absl-py", marker = "python_full_version >= '3.11'" }, + { name = "h5py", marker = "python_full_version >= '3.11'" }, + { name = "ml-dtypes", marker = "python_full_version >= '3.11'" }, + { name = "namex", marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "optree", marker = "python_full_version >= '3.11'" }, + { name = "packaging", marker = "python_full_version >= '3.11'" }, + { name = "rich", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/35/e7/97a7664581b73e4f9ff1d3a767a493b6ac5d3e0ed1926bd2b6b2c8bbccd7/keras-3.14.1.tar.gz", hash = "sha256:ef479173102ad29db89b53c232efdc3fb5ad57c28bc27ead59f3e78a1eecd05b", size = 1263647 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/03/184267c1d09783dd070f1ddfd0d4beb7503139dfc7bd75b422867cf282fd/keras-3.14.1-py3-none-any.whl", hash = "sha256:ebd2c14d2af3c9de18083604d408483996407fc7d2f9ebd1d565961f96608c29", size = 1628606 }, +] + [[package]] name = "kiwisolver" version = "1.4.8" @@ -1810,20 +1952,36 @@ dependencies = [ [package.optional-dependencies] all = [ + { name = "bayesflow", version = "2.0.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "bayesflow", version = "2.0.12", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "huggingface-hub" }, + { name = "keras", version = "3.12.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "keras", version = "3.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "mlflow" }, { name = "nflows" }, { name = "sbi" }, ] +bayesflow = [ + { name = "bayesflow", version = "2.0.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "bayesflow", version = "2.0.12", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "keras", version = "3.12.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "keras", version = "3.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] hf = [ { name = "huggingface-hub" }, ] mlflow = [ { name = "mlflow" }, ] +sbi = [ + { name = "nflows" }, + { name = "sbi" }, +] [package.dev-dependencies] dev = [ + { name = "bayesflow", version = "2.0.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "bayesflow", version = "2.0.12", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "coverage" }, { name = "ipykernel" }, { name = "ipython", version = "8.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, @@ -1831,6 +1989,8 @@ dev = [ { name = "ipywidgets" }, { name = "jaxonnxruntime" }, { name = "jupyterlab" }, + { name = "keras", version = "3.12.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "keras", version = "3.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "mlflow" }, { name = "mypy" }, { name = "nbconvert" }, @@ -1850,33 +2010,41 @@ dev = [ [package.metadata] requires-dist = [ + { name = "bayesflow", marker = "extra == 'all'", specifier = ">=2.0.8" }, + { name = "bayesflow", marker = "extra == 'bayesflow'", specifier = ">=2.0.8" }, { name = "flax", specifier = ">=0.10.6" }, { name = "frozendict", specifier = ">=2.4.6" }, { name = "huggingface-hub", marker = "extra == 'all'", specifier = ">=0.20.0" }, { name = "huggingface-hub", marker = "extra == 'hf'", specifier = ">=0.20.0" }, + { name = "keras", marker = "extra == 'all'", specifier = ">=3.12" }, + { name = "keras", marker = "extra == 'bayesflow'", specifier = ">=3.12" }, { name = "matplotlib", specifier = ">=3.10.1" }, { name = "mlflow", marker = "extra == 'all'", specifier = ">=3.6.0" }, { name = "mlflow", marker = "extra == 'mlflow'", specifier = ">=3.6.0" }, { name = "nflows", marker = "extra == 'all'", specifier = ">=0.14" }, + { name = "nflows", marker = "extra == 'sbi'", specifier = ">=0.14" }, { name = "onnx", specifier = ">=1.17.0" }, { name = "pandas", specifier = ">=2.2.3" }, { name = "sbi", marker = "extra == 'all'", specifier = ">=0.26" }, + { name = "sbi", marker = "extra == 'sbi'", specifier = ">=0.26" }, { name = "scipy", specifier = ">=1.15.2" }, { name = "ssm-simulators", specifier = ">=0.12.2" }, { name = "torch", specifier = ">=2.7.0" }, { name = "tqdm", specifier = ">=4.67.1" }, { name = "typer", specifier = ">=0.9.0" }, ] -provides-extras = ["mlflow", "hf", "all"] +provides-extras = ["mlflow", "hf", "sbi", "bayesflow", "all"] [package.metadata.requires-dev] dev = [ + { name = "bayesflow", specifier = ">=2.0.8" }, { name = "coverage", specifier = ">=7.6.4" }, { name = "ipykernel", specifier = ">=6.29.5" }, { name = "ipython", specifier = ">=8.31.0" }, { name = "ipywidgets", specifier = ">=8.1.2" }, { name = "jaxonnxruntime", specifier = ">=0.3" }, { name = "jupyterlab", specifier = ">=4.2.4" }, + { name = "keras", specifier = ">=3.12" }, { name = "mlflow", specifier = ">=3.6.0" }, { name = "mypy", specifier = ">=1.11.1" }, { name = "nbconvert", specifier = ">=7.16.5" }, @@ -1986,7 +2154,7 @@ wheels = [ [[package]] name = "matplotlib" -version = "3.10.3" +version = "3.10.9" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "contourpy" }, @@ -2000,41 +2168,48 @@ dependencies = [ { name = "pyparsing" }, { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/26/91/d49359a21893183ed2a5b6c76bec40e0b1dcbf8ca148f864d134897cfc75/matplotlib-3.10.3.tar.gz", hash = "sha256:2f82d2c5bb7ae93aaaa4cd42aca65d76ce6376f83304fa3a630b569aca274df0", size = 34799811 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/ea/2bba25d289d389c7451f331ecd593944b3705f06ddf593fa7be75037d308/matplotlib-3.10.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:213fadd6348d106ca7db99e113f1bea1e65e383c3ba76e8556ba4a3054b65ae7", size = 8167862 }, - { url = "https://files.pythonhosted.org/packages/41/81/cc70b5138c926604e8c9ed810ed4c79e8116ba72e02230852f5c12c87ba2/matplotlib-3.10.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3bec61cb8221f0ca6313889308326e7bb303d0d302c5cc9e523b2f2e6c73deb", size = 8042149 }, - { url = "https://files.pythonhosted.org/packages/4a/9a/0ff45b6bfa42bb16de597e6058edf2361c298ad5ef93b327728145161bbf/matplotlib-3.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c21ae75651c0231b3ba014b6d5e08fb969c40cdb5a011e33e99ed0c9ea86ecb", size = 8453719 }, - { url = "https://files.pythonhosted.org/packages/85/c7/1866e972fed6d71ef136efbc980d4d1854ab7ef1ea8152bbd995ca231c81/matplotlib-3.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a49e39755580b08e30e3620efc659330eac5d6534ab7eae50fa5e31f53ee4e30", size = 8590801 }, - { url = "https://files.pythonhosted.org/packages/5d/b9/748f6626d534ab7e255bdc39dc22634d337cf3ce200f261b5d65742044a1/matplotlib-3.10.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cf4636203e1190871d3a73664dea03d26fb019b66692cbfd642faafdad6208e8", size = 9402111 }, - { url = "https://files.pythonhosted.org/packages/1f/78/8bf07bd8fb67ea5665a6af188e70b57fcb2ab67057daa06b85a08e59160a/matplotlib-3.10.3-cp310-cp310-win_amd64.whl", hash = "sha256:fd5641a9bb9d55f4dd2afe897a53b537c834b9012684c8444cc105895c8c16fd", size = 8057213 }, - { url = "https://files.pythonhosted.org/packages/f5/bd/af9f655456f60fe1d575f54fb14704ee299b16e999704817a7645dfce6b0/matplotlib-3.10.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0ef061f74cd488586f552d0c336b2f078d43bc00dc473d2c3e7bfee2272f3fa8", size = 8178873 }, - { url = "https://files.pythonhosted.org/packages/c2/86/e1c86690610661cd716eda5f9d0b35eaf606ae6c9b6736687cfc8f2d0cd8/matplotlib-3.10.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d96985d14dc5f4a736bbea4b9de9afaa735f8a0fc2ca75be2fa9e96b2097369d", size = 8052205 }, - { url = "https://files.pythonhosted.org/packages/54/51/a9f8e49af3883dacddb2da1af5fca1f7468677f1188936452dd9aaaeb9ed/matplotlib-3.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c5f0283da91e9522bdba4d6583ed9d5521566f63729ffb68334f86d0bb98049", size = 8465823 }, - { url = "https://files.pythonhosted.org/packages/e7/e3/c82963a3b86d6e6d5874cbeaa390166458a7f1961bab9feb14d3d1a10f02/matplotlib-3.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdfa07c0ec58035242bc8b2c8aae37037c9a886370eef6850703d7583e19964b", size = 8606464 }, - { url = "https://files.pythonhosted.org/packages/0e/34/24da1027e7fcdd9e82da3194c470143c551852757a4b473a09a012f5b945/matplotlib-3.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c0b9849a17bce080a16ebcb80a7b714b5677d0ec32161a2cc0a8e5a6030ae220", size = 9413103 }, - { url = "https://files.pythonhosted.org/packages/a6/da/948a017c3ea13fd4a97afad5fdebe2f5bbc4d28c0654510ce6fd6b06b7bd/matplotlib-3.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:eef6ed6c03717083bc6d69c2d7ee8624205c29a8e6ea5a31cd3492ecdbaee1e1", size = 8065492 }, - { url = "https://files.pythonhosted.org/packages/eb/43/6b80eb47d1071f234ef0c96ca370c2ca621f91c12045f1401b5c9b28a639/matplotlib-3.10.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0ab1affc11d1f495ab9e6362b8174a25afc19c081ba5b0775ef00533a4236eea", size = 8179689 }, - { url = "https://files.pythonhosted.org/packages/0f/70/d61a591958325c357204870b5e7b164f93f2a8cca1dc6ce940f563909a13/matplotlib-3.10.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2a818d8bdcafa7ed2eed74487fdb071c09c1ae24152d403952adad11fa3c65b4", size = 8050466 }, - { url = "https://files.pythonhosted.org/packages/e7/75/70c9d2306203148cc7902a961240c5927dd8728afedf35e6a77e105a2985/matplotlib-3.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:748ebc3470c253e770b17d8b0557f0aa85cf8c63fd52f1a61af5b27ec0b7ffee", size = 8456252 }, - { url = "https://files.pythonhosted.org/packages/c4/91/ba0ae1ff4b3f30972ad01cd4a8029e70a0ec3b8ea5be04764b128b66f763/matplotlib-3.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed70453fd99733293ace1aec568255bc51c6361cb0da94fa5ebf0649fdb2150a", size = 8601321 }, - { url = "https://files.pythonhosted.org/packages/d2/88/d636041eb54a84b889e11872d91f7cbf036b3b0e194a70fa064eb8b04f7a/matplotlib-3.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dbed9917b44070e55640bd13419de83b4c918e52d97561544814ba463811cbc7", size = 9406972 }, - { url = "https://files.pythonhosted.org/packages/b1/79/0d1c165eac44405a86478082e225fce87874f7198300bbebc55faaf6d28d/matplotlib-3.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:cf37d8c6ef1a48829443e8ba5227b44236d7fcaf7647caa3178a4ff9f7a5be05", size = 8067954 }, - { url = "https://files.pythonhosted.org/packages/3b/c1/23cfb566a74c696a3b338d8955c549900d18fe2b898b6e94d682ca21e7c2/matplotlib-3.10.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9f2efccc8dcf2b86fc4ee849eea5dcaecedd0773b30f47980dc0cbeabf26ec84", size = 8180318 }, - { url = "https://files.pythonhosted.org/packages/6c/0c/02f1c3b66b30da9ee343c343acbb6251bef5b01d34fad732446eaadcd108/matplotlib-3.10.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3ddbba06a6c126e3301c3d272a99dcbe7f6c24c14024e80307ff03791a5f294e", size = 8051132 }, - { url = "https://files.pythonhosted.org/packages/b4/ab/8db1a5ac9b3a7352fb914133001dae889f9fcecb3146541be46bed41339c/matplotlib-3.10.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:748302b33ae9326995b238f606e9ed840bf5886ebafcb233775d946aa8107a15", size = 8457633 }, - { url = "https://files.pythonhosted.org/packages/f5/64/41c4367bcaecbc03ef0d2a3ecee58a7065d0a36ae1aa817fe573a2da66d4/matplotlib-3.10.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a80fcccbef63302c0efd78042ea3c2436104c5b1a4d3ae20f864593696364ac7", size = 8601031 }, - { url = "https://files.pythonhosted.org/packages/12/6f/6cc79e9e5ab89d13ed64da28898e40fe5b105a9ab9c98f83abd24e46d7d7/matplotlib-3.10.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:55e46cbfe1f8586adb34f7587c3e4f7dedc59d5226719faf6cb54fc24f2fd52d", size = 9406988 }, - { url = "https://files.pythonhosted.org/packages/b1/0f/eed564407bd4d935ffabf561ed31099ed609e19287409a27b6d336848653/matplotlib-3.10.3-cp313-cp313-win_amd64.whl", hash = "sha256:151d89cb8d33cb23345cd12490c76fd5d18a56581a16d950b48c6ff19bb2ab93", size = 8068034 }, - { url = "https://files.pythonhosted.org/packages/3e/e5/2f14791ff69b12b09e9975e1d116d9578ac684460860ce542c2588cb7a1c/matplotlib-3.10.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c26dd9834e74d164d06433dc7be5d75a1e9890b926b3e57e74fa446e1a62c3e2", size = 8218223 }, - { url = "https://files.pythonhosted.org/packages/5c/08/30a94afd828b6e02d0a52cae4a29d6e9ccfcf4c8b56cc28b021d3588873e/matplotlib-3.10.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:24853dad5b8c84c8c2390fc31ce4858b6df504156893292ce8092d190ef8151d", size = 8094985 }, - { url = "https://files.pythonhosted.org/packages/89/44/f3bc6b53066c889d7a1a3ea8094c13af6a667c5ca6220ec60ecceec2dabe/matplotlib-3.10.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68f7878214d369d7d4215e2a9075fef743be38fa401d32e6020bab2dfabaa566", size = 8483109 }, - { url = "https://files.pythonhosted.org/packages/ba/c7/473bc559beec08ebee9f86ca77a844b65747e1a6c2691e8c92e40b9f42a8/matplotlib-3.10.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6929fc618cb6db9cb75086f73b3219bbb25920cb24cee2ea7a12b04971a4158", size = 8618082 }, - { url = "https://files.pythonhosted.org/packages/d8/e9/6ce8edd264c8819e37bbed8172e0ccdc7107fe86999b76ab5752276357a4/matplotlib-3.10.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6c7818292a5cc372a2dc4c795e5c356942eb8350b98ef913f7fda51fe175ac5d", size = 9413699 }, - { url = "https://files.pythonhosted.org/packages/1b/92/9a45c91089c3cf690b5badd4be81e392ff086ccca8a1d4e3a08463d8a966/matplotlib-3.10.3-cp313-cp313t-win_amd64.whl", hash = "sha256:4f23ffe95c5667ef8a2b56eea9b53db7f43910fa4a2d5472ae0f72b64deab4d5", size = 8139044 }, - { url = "https://files.pythonhosted.org/packages/3d/d1/f54d43e95384b312ffa4a74a4326c722f3b8187aaaa12e9a84cdf3037131/matplotlib-3.10.3-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:86ab63d66bbc83fdb6733471d3bff40897c1e9921cba112accd748eee4bce5e4", size = 8162896 }, - { url = "https://files.pythonhosted.org/packages/24/a4/fbfc00c2346177c95b353dcf9b5a004106abe8730a62cb6f27e79df0a698/matplotlib-3.10.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:a48f9c08bf7444b5d2391a83e75edb464ccda3c380384b36532a0962593a1751", size = 8039702 }, - { url = "https://files.pythonhosted.org/packages/6a/b9/59e120d24a2ec5fc2d30646adb2efb4621aab3c6d83d66fb2a7a182db032/matplotlib-3.10.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb73d8aa75a237457988f9765e4dfe1c0d2453c5ca4eabc897d4309672c8e014", size = 8594298 }, +sdist = { url = "https://files.pythonhosted.org/packages/63/1b/4be5be87d43d327a0cf4de1a56e86f7f84c89312452406cf122efe2839e6/matplotlib-3.10.9.tar.gz", hash = "sha256:fd66508e8c6877d98e586654b608a0456db8d7e8a546eb1e2600efd957302358", size = 34811233 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/6f/340b04986e67aac6f66c5145ce68bf72c64bed30f92c8913499a6e6b8f99/matplotlib-3.10.9-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77210dce9cb8153dffc967efaae990543392563d5a376d4dd8539bebcb0ed217", size = 8296625 }, + { url = "https://files.pythonhosted.org/packages/bb/2f/127081eb83162053ebb9678ceac64220b93a663e0167432566e9c7c82aab/matplotlib-3.10.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1e7698ac9868428e84d2c967424803b2472ff7167d9d6590d4204ed775343c3b", size = 8188790 }, + { url = "https://files.pythonhosted.org/packages/fc/b7/d8bcec2626c35f96972bff656299fef4578113ea6193c8fdad324710410c/matplotlib-3.10.9-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1aa972116abb4c9d201bf245620b433726cb6856f3bef6a78f776a00f5c92d37", size = 8769389 }, + { url = "https://files.pythonhosted.org/packages/12/49/b78e214a527ea732033b7f4d37f7afb504d74ba9d134bd47938230dfb8b1/matplotlib-3.10.9-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae2f11957b27ce53497dd4d7b235c4d4f1faf383dfb39d0c5beb833bff883294", size = 9589657 }, + { url = "https://files.pythonhosted.org/packages/5f/15/5246f7b43beae19c74dfee651d58d6cc8112e06f77adb4e88cc04f2e3a23/matplotlib-3.10.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b049278ddce116aaa1c1377ebf58adea909132dfce0281cf7e3a1ea9fc2e2c65", size = 9651983 }, + { url = "https://files.pythonhosted.org/packages/75/77/5acecfe672ba0fa1b8c0454f69ce155d1e6fc5852fa7206bf9afaf767121/matplotlib-3.10.9-cp310-cp310-win_amd64.whl", hash = "sha256:82834c3c292d24d3a8aae77cd2d20019de69d692a34a970e4fdb8d33e2ea3dda", size = 8199701 }, + { url = "https://files.pythonhosted.org/packages/4c/8c/290f021104741fea63769c31494f5324c0cd249bf536a65a4350767b1f22/matplotlib-3.10.9-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:68cfdcede415f7c8f5577b03303dd94526cdb6d11036cecdc205e08733b2d2bb", size = 8306860 }, + { url = "https://files.pythonhosted.org/packages/51/18/325cd32ece1120d1da51cc4e4294c6580190699490183fc2fe8cb6d61ec5/matplotlib-3.10.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfca0129678bd56379db26c52b5d77ed7de314c047492fbdc763aa7501710cfb", size = 8199254 }, + { url = "https://files.pythonhosted.org/packages/79/db/e28c1b83e3680740aa78925f5fb2ae4d16207207419ad75ea9fe604f8676/matplotlib-3.10.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8e436d155fa8a3399dc62683f8f5d0e2e50d25d0144a73edd73f82eec8f4abfb", size = 8777092 }, + { url = "https://files.pythonhosted.org/packages/55/fa/3ce7adfe9ba101748f465211660d9c6374c876b671bdb8c2bb6d347e8b94/matplotlib-3.10.9-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56fc0bd271b00025c6edfdc7c2dcd247372c8e1544971d62e1dc7c17367e8bf9", size = 9595691 }, + { url = "https://files.pythonhosted.org/packages/36/c4/6960a76686ed668f2c60f84e9799ba4c0d56abdb36b1577b60c1d061d1ec/matplotlib-3.10.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5a6104ed666402ba5106d7f36e0e0cdca4e8d7fa4d39708ca88019e2835a2eb", size = 9659771 }, + { url = "https://files.pythonhosted.org/packages/7e/0d/271aace3342157c64700c9ff4c59c7b392f3dbab393692e8db6fbe7ab96c/matplotlib-3.10.9-cp311-cp311-win_amd64.whl", hash = "sha256:d730e984eddf56974c3e72b6129c7ca462ac38dc624338f4b0b23eb23ecba00f", size = 8205112 }, + { url = "https://files.pythonhosted.org/packages/e2/ee/cb57ad4754f3e7b9174ce6ce66d9205fb827067e48a9f58ac09d7e7d6b77/matplotlib-3.10.9-cp311-cp311-win_arm64.whl", hash = "sha256:51bf0ddbdc598e060d46c16b5590708f81a1624cefbaaf62f6a81bf9285b8c80", size = 8132310 }, + { url = "https://files.pythonhosted.org/packages/35/c6/5581e26c72233ebb2a2a6fed2d24fb7c66b4700120b813f51b0555acf0b6/matplotlib-3.10.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f0c3c28d9fbcc1fe7a03be236d73430cf6409c41fb2383a7ac52fe932b072cb1", size = 8319908 }, + { url = "https://files.pythonhosted.org/packages/b7/18/4880dd762e40cd360c1bf06e890c5a97b997e91cb324602b1a19950ad5ce/matplotlib-3.10.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41cb28c2bd769aa3e98322c6ab09854cbcc52ab69d2759d681bba3e327b2b320", size = 8216016 }, + { url = "https://files.pythonhosted.org/packages/32/91/d024616abdba99e83120e07a20658976f6a343646710760c4a51df126029/matplotlib-3.10.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ae20801130378b82d647ff5047c07316295b68dc054ca6b3c13519d0ea624285", size = 8789336 }, + { url = "https://files.pythonhosted.org/packages/5c/04/030a2f61ef2158f5e4c259487a92ac877732499fb33d871585d89e03c42d/matplotlib-3.10.9-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c63ebcd8b4b169eb2f5c200552ae6b8be8999a005b6b507ed76fb8d7d674fe2", size = 9604602 }, + { url = "https://files.pythonhosted.org/packages/fc/c2/541e4d09d87bb6b5830fc28b4c887a9a8cf4e1c6cee698a8c05552ae2003/matplotlib-3.10.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d75d11c949914165976c621b2324f9ef162af7ebf4b057ddf95dd1dba7e5edcf", size = 9670966 }, + { url = "https://files.pythonhosted.org/packages/04/a1/4571fc46e7702de8d0c2dc54ad1b2f8e29328dea3ee90831181f7353d93c/matplotlib-3.10.9-cp312-cp312-win_amd64.whl", hash = "sha256:d091f9d758b34aaaaa6331d13574bf01891d903b3dec59bfff458ef7551de5d6", size = 8217462 }, + { url = "https://files.pythonhosted.org/packages/4b/d0/2269edb12aa30c13c8bcc9382892e39943ce1d28aab4ec296e0381798e81/matplotlib-3.10.9-cp312-cp312-win_arm64.whl", hash = "sha256:10cc5ce06d10231c36f40e875f3c7e8050362a4ee8f0ee5d29a6b3277d57bb42", size = 8136688 }, + { url = "https://files.pythonhosted.org/packages/aa/d3/8d4f6afbecb49fc04e060a57c0fce39ea51cc163a6bd87303ccd698e4fa6/matplotlib-3.10.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b580440f1ff81a0e34122051a3dfabb7e4b7f9e380629929bde0eff9af72165f", size = 8320331 }, + { url = "https://files.pythonhosted.org/packages/63/d9/9e14bc7564bf92d5ffa801ae5fac819ce74b925dfb55e3ebde61a3bbad3e/matplotlib-3.10.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b1b745c489cd1a77a0dc1120a05dc87af9798faebc913601feb8c73d89bf2d1e", size = 8216461 }, + { url = "https://files.pythonhosted.org/packages/8a/17/4402d0d14ccf1dfc70932600b68097fbbf9c898a4871d2cbbe79c7801a32/matplotlib-3.10.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8f3bcac1ca5ed000a6f4337d47ba67dfddf37ed6a46c15fd7f014997f7bf865f", size = 8790091 }, + { url = "https://files.pythonhosted.org/packages/3e/0b/322aeec06dd9b91411f92028b37d447342770a24392aa4813e317064dad5/matplotlib-3.10.9-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a8d66a55def891c33147ba3ba9bfcabf0b526a43764c818acbb4525e5ed0838", size = 9605027 }, + { url = "https://files.pythonhosted.org/packages/74/88/5f13482f55e7b00bcfc09838b093c2456e1379978d2a146844aae05350ad/matplotlib-3.10.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d843374407c4017a6403b59c6c81606773d136f3259d5b6da3131bc814542cc2", size = 9671269 }, + { url = "https://files.pythonhosted.org/packages/c5/e0/0840fd2f93da988ec660b8ad1984abe9f25d2aed22a5e394ff1c68c88307/matplotlib-3.10.9-cp313-cp313-win_amd64.whl", hash = "sha256:f4399f64b3e94cd500195490972ae1ee81170df1636fa15364d157d5bdd7b921", size = 8217588 }, + { url = "https://files.pythonhosted.org/packages/47/b9/d706d06dd605c49b9f83a2aed8c13e3e5db70697d7a80b7e3d7915de6b17/matplotlib-3.10.9-cp313-cp313-win_arm64.whl", hash = "sha256:ba7b3b8ef09eab7df0e86e9ae086faa433efbfbdb46afcb3aa16aabf779469a8", size = 8136913 }, + { url = "https://files.pythonhosted.org/packages/9b/45/6e32d96978264c8ca8c4b1010adb955a1a49cfaf314e212bbc8908f04a61/matplotlib-3.10.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:09218df8a93712bd6ea133e83a153c755448cf7868316c531cffcc43f69d1cc9", size = 8368019 }, + { url = "https://files.pythonhosted.org/packages/86/0a/c8e3d3bba245f0f7fc424937f8ff7ef77291a36af3edb97ccd78aa93d84f/matplotlib-3.10.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:82368699727bfb7b0182e1aa13082e3c08e092fa1a25d3e1fd92405bff96f6d4", size = 8264645 }, + { url = "https://files.pythonhosted.org/packages/3d/aa/5bf5a14fe4fed73a4209a155606f8096ff797aad89c6c35179026571133e/matplotlib-3.10.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3225f4e1edcb8c86c884ddf79ebe20ecd0a67d30188f279897554ccd8fded4dc", size = 8802194 }, + { url = "https://files.pythonhosted.org/packages/dd/5e/b4be852d6bba6fd15893fadf91ff26ae49cb91aac789e95dde9d342e664f/matplotlib-3.10.9-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de2445a0c6690d21b7eb6ce071cebad6d40a2e9bdf10d039074a96ba19797b99", size = 9622684 }, + { url = "https://files.pythonhosted.org/packages/4c/3d/ed428c971139112ef730f62770654d609467346d09d4b62617e1afd68a5a/matplotlib-3.10.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:b2b9516251cb89ff618d757daec0e2ed1bf21248013844a853d87ef85ab3081d", size = 9680790 }, + { url = "https://files.pythonhosted.org/packages/e7/09/052e884aaf2b985c63cb79f715f1d5b6a3eaa7de78f6a52b9dbc077d5b53/matplotlib-3.10.9-cp313-cp313t-win_amd64.whl", hash = "sha256:e9fae004b941b23ff2edcf1567a857ed77bafc8086ffa258190462328434faf8", size = 8287571 }, + { url = "https://files.pythonhosted.org/packages/f4/38/ae27288e788c35a4250491422f3db7750366fc8c97d6f36fbdecfc1f5518/matplotlib-3.10.9-cp313-cp313t-win_arm64.whl", hash = "sha256:6b63d9c7c769b88ab81e10dc86e4e0607cf56817b9f9e6cf24b2a5f1693b8e38", size = 8188292 }, + { url = "https://files.pythonhosted.org/packages/2c/2b/0e92ad0ac446633f928a1563db4aa8add407e1924faf0ded5b95b35afb27/matplotlib-3.10.9-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1872fb212a05b729e649754a72d5da61d03e0554d76e80303b6f83d1d2c0552b", size = 8293058 }, + { url = "https://files.pythonhosted.org/packages/4b/23/74682fd369f5299ceda438fea2a0662e6383b85c9383fb9cdfcf04713e07/matplotlib-3.10.9-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:985f2238880e2e69093f588f5fe2e46771747febf0649f3cf7f7b7480875317f", size = 8186627 }, + { url = "https://files.pythonhosted.org/packages/ca/e8/368aab88f3c4cd8992800f31abfe0670c3e47540ba20a97e9fdbcde594b3/matplotlib-3.10.9-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6640f75af2c6148293caa0a2b39dd806a492dd66c8a8b04035813e33d0fd2585", size = 8764117 }, + { url = "https://files.pythonhosted.org/packages/63/e2/9f66ca6a651a52abfe0d4964ce01439ed34f3f1e119de10ff3a07f403043/matplotlib-3.10.9-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:42fb814efabe95c06c1994d8ab5a8385f43a249e23badd3ba931d4308e5bca20", size = 8304420 }, + { url = "https://files.pythonhosted.org/packages/e8/e8/467c03568218792906aa87b5e7bb379b605e056ed0c74fe00c051786d925/matplotlib-3.10.9-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f76e640a5268850bfda54b5131b1b1941cc685e42c5fa98ed9f2d64038308cba", size = 8197981 }, + { url = "https://files.pythonhosted.org/packages/6f/87/afead29192170917537934c6aff4b008c805fff7b1ccea0c79120d96beda/matplotlib-3.10.9-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3fc0364dfbe1d07f6d15c5ebd0c5bf89e126916e5a8667dd4a7a6e84c36653d4", size = 8774002 }, ] [[package]] @@ -2309,6 +2484,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963 }, ] +[[package]] +name = "namex" +version = "0.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0c/c0/ee95b28f029c73f8d49d8f52edaed02a1d4a9acb8b69355737fdb1faa191/namex-0.1.0.tar.gz", hash = "sha256:117f03ccd302cc48e3f5c58a296838f6b89c83455ab8683a1e85f2a430aa4306", size = 6649 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/bc/465daf1de06409cdd4532082806770ee0d8d7df434da79c76564d0f69741/namex-0.1.0-py3-none-any.whl", hash = "sha256:e2012a474502f1e2251267062aae3114611f07df4224b6e06334c57b0f2ce87c", size = 5905 }, +] + [[package]] name = "nbclient" version = "0.10.2" @@ -2390,7 +2574,9 @@ name = "networkx" version = "3.5" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.13'", + "python_full_version >= '3.13' and sys_platform == 'win32'", + "python_full_version >= '3.13' and sys_platform == 'emscripten'", + "python_full_version >= '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.12.*'", "python_full_version == '3.11.*'", ] @@ -2504,7 +2690,9 @@ name = "numpy" version = "2.3.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.13'", + "python_full_version >= '3.13' and sys_platform == 'win32'", + "python_full_version >= '3.13' and sys_platform == 'emscripten'", + "python_full_version >= '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.12.*'", "python_full_version == '3.11.*'", ] @@ -2773,7 +2961,9 @@ name = "onnxruntime" version = "1.26.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.13'", + "python_full_version >= '3.13' and sys_platform == 'win32'", + "python_full_version >= '3.13' and sys_platform == 'emscripten'", + "python_full_version >= '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.12.*'", "python_full_version == '3.11.*'", ] @@ -2881,6 +3071,79 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b9/33/f86091c706db1a5459f501830241afff2ecab3532725c188ea57be6e54de/optax-0.2.5-py3-none-any.whl", hash = "sha256:966deae936207f268ac8f564d8ed228d645ac1aaddefbbf194096d2299b24ba8", size = 354324 }, ] +[[package]] +name = "optree" +version = "0.19.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/44/63/92328a17ab7836562fe0129e605f685a88db35ce98427c34ff48ee4ec157/optree-0.19.1.tar.gz", hash = "sha256:4497d1c9197b8c6842e511368163d318ce536521ebdcff8bebb7551dcdfac532", size = 177531 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/48/53367634a0ab6c2f0e502d83f8d6e27b70b6848ff1e1ff9cf042d1e1f1a0/optree-0.19.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1b28b0d89def1b4554051f3de2a1ed81e20216b6454a59a0d16c9f55c08cff77", size = 398400 }, + { url = "https://files.pythonhosted.org/packages/3d/4f/350c82cd77a510f0f495e38a6f333b4b45a413dbc224142bc59975bc09d6/optree-0.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:14f959bc6bea6e0532f9239c67ea6952f3b8d0755ea9b4dd498284b649275aba", size = 370049 }, + { url = "https://files.pythonhosted.org/packages/cb/e1/81b660daea2a75f574549e62c198d0b4e8e148b5de6f5f72e90a5cc1c334/optree-0.19.1-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1687c962bb1691525178a6e90dde5840197cd7a7ad914b407eb7b635f15d47cb", size = 390143 }, + { url = "https://files.pythonhosted.org/packages/d5/ec/ee009b5a31227b089d72fec2af3bb6bc0efd95bbe87ffe46f11061b9d371/optree-0.19.1-cp310-cp310-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:d7edead66cace8b3b905488e391b38487614f75ae4fa7f3b612c7fe0e54b8a90", size = 445740 }, + { url = "https://files.pythonhosted.org/packages/15/5c/2fe8ac73b7e979f3ed477ad99b7e034a11207d728b84ed2f52da259e7cda/optree-0.19.1-cp310-cp310-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9fb767746231ff279d273e8ff71af2a8f89c0c3870ca367c45fd4526d331ae4b", size = 446631 }, + { url = "https://files.pythonhosted.org/packages/21/42/489fb272de36e0233149d46887879deb9497edc4a0214674bd2a80b8d4ec/optree-0.19.1-cp310-cp310-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:75fe038a1bed44f487a084af7a978874c51bba55f850bc12bf8068f3242463d6", size = 442053 }, + { url = "https://files.pythonhosted.org/packages/90/09/1f0bc2b584a51702407592bbccfe2b404187f6f5ee5b4b0c112a73e1a7ec/optree-0.19.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fff5fd89a9b333d91a05a7ca2e66c8e6632d0bdbc94c1725a341b77001f09511", size = 425490 }, + { url = "https://files.pythonhosted.org/packages/7d/f4/d8685b55323c1f42695c1ed647d6541ee9c289eb821abc6e0cb84b0e4f72/optree-0.19.1-cp310-cp310-manylinux_2_39_riscv64.whl", hash = "sha256:d4bac18638fa56efd2377cf8c43e17cd083aa566e69a31ce10f7fdaefd9676a3", size = 390552 }, + { url = "https://files.pythonhosted.org/packages/07/84/ee12e234ddcf4fd4b7893ce03ec37f3c3edabdac911fd5384aa3f5c04c05/optree-0.19.1-cp310-cp310-win32.whl", hash = "sha256:ef2409d4efda1c5a6eb69f83ffff89fb04d5607fd056704552ec359fb865cd6c", size = 303117 }, + { url = "https://files.pythonhosted.org/packages/91/b5/4e23965aacae04eb4cf42cd8108405a6628e645ee3ab759277e03063af0e/optree-0.19.1-cp310-cp310-win_amd64.whl", hash = "sha256:01c88294235b118b7478b5e80d360e5f110977cdf79f84d61dae21c2eb1d4cdd", size = 327866 }, + { url = "https://files.pythonhosted.org/packages/c3/f2/4671a78193f96e86c1343fe04324091e163973d0058b292c10bc3387bb70/optree-0.19.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a496b864fe1fe0b5ea23d1ee3d1ef958d910704661808db2b2d2e16a0cfac96c", size = 414314 }, + { url = "https://files.pythonhosted.org/packages/d6/93/7decea24656f416d61fa57b7113b1fbdbc042b7ab421399a84e1755676a1/optree-0.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c1667e502e0eda9477925fb17c2ad879b199a2283ac98f18e6453692819b7811", size = 385006 }, + { url = "https://files.pythonhosted.org/packages/af/2e/9d1bd2527481681c4399beeeabba11dca36b16ec814579f2e8cc6bc2af96/optree-0.19.1-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:42e367a9d81e57c31a23247094727987a2f64b708901233a42a24d44d24e93f6", size = 406124 }, + { url = "https://files.pythonhosted.org/packages/df/29/cdb40de6307809fa8e9452e4f9a65881a3140d01d9d589a07e9d054d8e1c/optree-0.19.1-cp311-cp311-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:96fad6c7b3a6fde3a0c8655fd003359cd247f8400749217502591a5ffc328699", size = 466772 }, + { url = "https://files.pythonhosted.org/packages/cb/15/4645e1816e815a1306bbb7e3e2e6ba124f6dc325f8088a2db69301219a0c/optree-0.19.1-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f3a900df0ffb9b8259961b337289754531a7e0a5de2f681e9c80866b6a7cb74e", size = 466203 }, + { url = "https://files.pythonhosted.org/packages/e6/d2/5758c76bdd7034b721d84c7f0fd911f3b39dcb489eeb27f674aaae8a5f5c/optree-0.19.1-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:27c8dc0f89ade9233aa7ed25ce15991da188e6950eb17cc0c313fc1f327c5b0b", size = 465030 }, + { url = "https://files.pythonhosted.org/packages/09/b9/f668bc51129c0fec7728ae8b43180417fe1c1fe99f71d302739f6cc50944/optree-0.19.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:38f2e503fad50aff58cade85db448002d4adc72f4b3b50dcc7f3ef4bcd3b0173", size = 447141 }, + { url = "https://files.pythonhosted.org/packages/a4/08/a7b8862e4465bf250c3ccc78db4d10b9a2cf90ce4db3681cbdf7eb076fb7/optree-0.19.1-cp311-cp311-manylinux_2_39_riscv64.whl", hash = "sha256:5dc35cb31540ab6ed9850b0f8865ccd400994ebd51fcf0c156cc772073f43c04", size = 410016 }, + { url = "https://files.pythonhosted.org/packages/fb/04/04b71a34cf5e663a1df029acceb5efc8a96c8dc4b0b6af6e98486638e913/optree-0.19.1-cp311-cp311-win32.whl", hash = "sha256:d32b1261be71211f77837e839e43a3e3e8fc57707091d2454d0a88590fb6abe8", size = 311810 }, + { url = "https://files.pythonhosted.org/packages/22/64/3cc7b08cb1c0f1949895f9490217ca8db6ced7f3bf75c65a5bf31c07bf1e/optree-0.19.1-cp311-cp311-win_amd64.whl", hash = "sha256:cd28a527bb363a1d7d28e8b2fb62816ace6743418bb86e9c5f27ea6877dcdf6c", size = 337620 }, + { url = "https://files.pythonhosted.org/packages/6c/14/85f4b05765287658529f09ede10461224161dcf0e29e6fce1ae488451cfe/optree-0.19.1-cp311-cp311-win_arm64.whl", hash = "sha256:7853b58aa084e882ea078f390936bd92e46972eb8f9b5e654360b6480ca7283b", size = 349337 }, + { url = "https://files.pythonhosted.org/packages/ba/a7/cb5567029a608a296b0ca224025d03bba0365b41df19085b9b580191f6f2/optree-0.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:96e5c7c3b9144f08ae40c3d9848cfbcfa36b6bead0f8215ad071d5922ee6c4a5", size = 424023 }, + { url = "https://files.pythonhosted.org/packages/b9/a1/3651fb32fa8617108204aa4056d283af742020e0987d106f41402005d800/optree-0.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5d9d198343e1e6ced18bef0cbff84091c1877964fc4a121df33f18840e073a01", size = 394782 }, + { url = "https://files.pythonhosted.org/packages/c2/1e/676470909aa64d7aba7c5edf83b171dc83b7af901d9ebb8e6d7512fe913a/optree-0.19.1-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a1202371d9fe3aa75f3e886b1f871aac4991a655aadb65e54f58a3ae9388ab2", size = 413157 }, + { url = "https://files.pythonhosted.org/packages/f4/41/1a4c58f2af5742b9d9e21ea9e45c6c3c49463b5e2a0537e84ead1e9597ca/optree-0.19.1-cp312-cp312-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:d41ccc4c20bfeae01d1d221c057a6d026e84e32229664952eddcdbe4b9b71417", size = 476923 }, + { url = "https://files.pythonhosted.org/packages/10/c1/f62167bd9d6f6c948b191a0943923404678d47100f777f4a8fb37816e6f8/optree-0.19.1-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7d934f240b109c6891dd06b2e30400b123b8a4b6ed31dcd0db2ae2378d30a6e8", size = 475385 }, + { url = "https://files.pythonhosted.org/packages/30/5e/5323c5fa3024fdd900bdd8f14621139ed844c2247bf1a26e7cf5c1116188/optree-0.19.1-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ddeefb7ca799c09647e332ebc1a5f6c09888a5a0e51f2dff4ca55e65b42a8c14", size = 474406 }, + { url = "https://files.pythonhosted.org/packages/e2/6a/54e4c47e61a51504a5224c933722e0c8a69925aacec4c08175e9675aeb81/optree-0.19.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f0ce49f64f804f7f35f2f9c2a21e3ba94c090199fccdcfd40e3ded4426c5c175", size = 457596 }, + { url = "https://files.pythonhosted.org/packages/a7/12/bba07c0b769586c6bd54e81f1f734cad103dbe30abbadee940fe7d3e330e/optree-0.19.1-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:e0f02600832ab8d0f6c934dcb5c339e17a36938d477641a45798e02625ebe107", size = 417900 }, + { url = "https://files.pythonhosted.org/packages/9f/8f/6ae994bb47f9394b33912a14593f9247737dd6c3303811550e5a3e918107/optree-0.19.1-cp312-cp312-win32.whl", hash = "sha256:f10d58c1a17e1b32f9d9b5e1b9d1ad964d99c1113d9df0b9f62f2fe7dde19909", size = 317302 }, + { url = "https://files.pythonhosted.org/packages/31/97/d7e3ec79dcdde81f785a0446acf75fea77723f5ca4b98556350d7877986f/optree-0.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:06f5c8a4cf356a1a276ce5cec1be44719ed260690f79c036d04b4d427e801258", size = 341362 }, + { url = "https://files.pythonhosted.org/packages/33/97/813afb84a81fd8ae65444730907c05f0775fd6c79d3359c9e84bd3370445/optree-0.19.1-cp312-cp312-win_arm64.whl", hash = "sha256:a33bd23fc5c67ecb9ff491b75fde10cd9b53f47f8a876de842090e8c7a2437e1", size = 351838 }, + { url = "https://files.pythonhosted.org/packages/c2/7b/0f2f3c9d55dda5127624daf68ff802ab624b739dd4b32aef505dac0c8e02/optree-0.19.1-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:f144cfd65fb17c6aa2c51818614eb009e6052d3d6ace91f7e570b1318cdcac4c", size = 929090 }, + { url = "https://files.pythonhosted.org/packages/15/e2/670d260dfd0532d64272dd6f7edd540a09d7040c0342b6cc6cf773568ea4/optree-0.19.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:39a006735d2a0a68751a3bc33d670184fddcd86db63b0293e1e819739e8105e4", size = 391528 }, + { url = "https://files.pythonhosted.org/packages/f4/96/46c15e80b0c97e2ba6aba11339008a37cabc5ccf55c31c6c60aecdb79638/optree-0.19.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d2cb43c36638f469f5d8f4cf638e914de90c62242d8bed29f1b4487e0346ab94", size = 398231 }, + { url = "https://files.pythonhosted.org/packages/7e/39/9d7d22cdaeb9a40ace2485f91c5b7c5f3a7f688575e2621e436561211cc1/optree-0.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e70faa00ab69331f49f8337d45021bed09ae2265d1db72eea9d7817af2b73c64", size = 429852 }, + { url = "https://files.pythonhosted.org/packages/79/4c/1da9e8375e7b7fd9671dc5987682b042f6412c4d6fd9da03296403818d9f/optree-0.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1c5d21176b670407f4555aae40711668832599c4fb0627000c5ce3ed0d6e2dae", size = 398688 }, + { url = "https://files.pythonhosted.org/packages/d3/50/cd2d178099618093f5a9fd1c9de80af2b428879922eae1e9f27f1002c8be/optree-0.19.1-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f658fa46305b2bdccdc5bb2cb07818aeaef88a1085499deda5be48a0a58d2971", size = 417560 }, + { url = "https://files.pythonhosted.org/packages/d7/b0/f22ff5632083b5032caa80208dd202f8e963ed4aac11afa0a0f6a307fd68/optree-0.19.1-cp313-cp313-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:e757079d44a00319447f43df5c51e55bf9b62d9f05eea0e2db5ff7c7ca5ec71d", size = 482937 }, + { url = "https://files.pythonhosted.org/packages/7d/d4/7499d28be8b11eb40668262d27802119fe7e6ec4cd8816b76a1acd7b08f5/optree-0.19.1-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9690c132822d9dee479cf7dff8cc52a67c8af42a4f7529d21f0f4f1d99e4c84e", size = 477864 }, + { url = "https://files.pythonhosted.org/packages/b1/6e/6c6fa6f1159ac68f4ee7666610127fb4c14d47a2fa7a0a48de3aecc24d4b/optree-0.19.1-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:544b70958dbd7e732bc6874e0180c609c9052115937d0ec28123bb49c1a574aa", size = 478319 }, + { url = "https://files.pythonhosted.org/packages/68/b5/8a2427bbe4ee59e2ce26a14125728e3b48c7030c80984ba07d0e5d804d37/optree-0.19.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9dde5b756946c1f1458aeab248a7a9b0c01bb06b5787de9f06d52ad38b745557", size = 462379 }, + { url = "https://files.pythonhosted.org/packages/ee/0c/a073eeaea4d4f68e02d5883ed8268746a296e6749e3c46e0124ca45f306c/optree-0.19.1-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:f1d7838e8b1b62258abd73a5911afad1153ed76822070558c3ba7e0bb5b44192", size = 423061 }, + { url = "https://files.pythonhosted.org/packages/5f/34/637b151d071ca94aea0087322f470ce84c5828ef6b9c0de7dc7b4420a1cf/optree-0.19.1-cp313-cp313-win32.whl", hash = "sha256:9870d33ec50cca0c46c2b431cea24c6247457da15fd4ad66ccb8ab78145c1490", size = 317439 }, + { url = "https://files.pythonhosted.org/packages/50/52/49b8a8d9e94c57c6fa5008953f84a1c36a4119a3b90dcb7df745f1f05a00/optree-0.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:aa0845b725bcd0029e179cf9b4bc2cc016c7358e56fc7c0d2c43bf4d514c96cf", size = 343906 }, + { url = "https://files.pythonhosted.org/packages/c6/a9/1ae0a9685f5301f454f01d2490065b98df6956f90b1b2fd1cea9daa6d820/optree-0.19.1-cp313-cp313-win_arm64.whl", hash = "sha256:6f0b1efc177bed6495f78d39d5aa495ccb31cc20bcf64bb1b806ca4c919f4049", size = 353146 }, + { url = "https://files.pythonhosted.org/packages/9c/77/4c8108cbce2c8ae2aa4b6adc7874082882e32cf131cb64b3a4411f50dec4/optree-0.19.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:b964bcdb5cfe367cdf56447e80ba5a49123098d8c4e8e68b41c20890eec6e58e", size = 469723 }, + { url = "https://files.pythonhosted.org/packages/64/33/ce9b54646ed4ab5773a9dc59767dadfe3de8bb2e97a3ed19205b995a7a31/optree-0.19.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:08ccec0ee5a565eb5aa4fe30383016a358627ea23d968ec8ab28b1f2ce4ce3d8", size = 437071 }, + { url = "https://files.pythonhosted.org/packages/79/55/04260128a726e3550b49467a65bff859452897144b68bae54b2f2e5c27f1/optree-0.19.1-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:672588408906051d3e9a99aca6c0af93c6e0b638137a701418088eaa0bb6c719", size = 433503 }, + { url = "https://files.pythonhosted.org/packages/d6/99/6a4cc29389667efa089a0c476b7c36b7d0a66e10dd2d8c2d19c776977566/optree-0.19.1-cp313-cp313t-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:d16cef4d0555d49ce221d80249f1285a2d3faf932e451c3ce6cb8ccb6a846767", size = 496305 }, + { url = "https://files.pythonhosted.org/packages/7f/46/506aa1a64abce69e2f4cec9cdac3da0cae207cf04c5e70e7f143bf8b29d8/optree-0.19.1-cp313-cp313t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dc2db0b449baff53aa7e583306101de0ade5e5ae9e6fce78400eb2319bbd23dc", size = 492759 }, + { url = "https://files.pythonhosted.org/packages/f5/28/2210de9a68722007fe007da3cae1a5971b92fc8113b5eecef66a04637959/optree-0.19.1-cp313-cp313t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:76b3e9e5d37e6b05ec82fff91758c8c0e27e159b35faea4b33d5eb975d720257", size = 495447 }, + { url = "https://files.pythonhosted.org/packages/d9/61/40c3463e52914d552c66c760ae15e673137c4cc1d1d9f8da0d745656193a/optree-0.19.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03faa8e23fdaf3a18f9a1568c2c0eb0641a6aa05baf3a20639bd11fb34664700", size = 475564 }, + { url = "https://files.pythonhosted.org/packages/0a/66/1603680fa924e68e5697c1229510c0645db0a9c633a12d1a9bfdbfc9cb74/optree-0.19.1-cp313-cp313t-manylinux_2_39_riscv64.whl", hash = "sha256:a9b9c7e9148ec470124dc4c1d1cd1485dbeb35973357b5911b181a79090426d2", size = 442414 }, + { url = "https://files.pythonhosted.org/packages/a5/58/34820bab11f28ba6b03fe9e151880ad591b43f26648f058c94451fbdfc3a/optree-0.19.1-cp313-cp313t-win32.whl", hash = "sha256:ab8ad9803376d553a2958471b6bb6842b7e15888e19cc6aeb76da96c6afd948d", size = 348644 }, + { url = "https://files.pythonhosted.org/packages/d9/2b/0be3f8b9765f366e3e12d0590e9c6514de110d0c5b3b9002f49e56bf15b1/optree-0.19.1-cp313-cp313t-win_amd64.whl", hash = "sha256:afd4abeb2783b2367093287bc6268ac9af244b20c8d9b01696ccfe817483b66c", size = 382445 }, + { url = "https://files.pythonhosted.org/packages/fc/fa/8c0882cdd42e28a23c1998297c8ad1202194510cbba8b050251429c641c0/optree-0.19.1-cp313-cp313t-win_arm64.whl", hash = "sha256:b9120510d3f951e268e417a3f64f335bc1c539e1e80bff2129ddc6fb60ac7b56", size = 388040 }, + { url = "https://files.pythonhosted.org/packages/2b/d4/ffeedc86f8b91e5c17994f38bd1f7aa2e20f9b70a6d3ae906af16414626c/optree-0.19.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3f4f1c276fa06227cdaf58349d22a3231b3dd3d47de1f90a86222ebf831fc397", size = 417543 }, + { url = "https://files.pythonhosted.org/packages/52/0b/80fb1b289940e34858cb89f05bc7ce23d6d1272886c2f78bc7e3ab1a306b/optree-0.19.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:77d93eafbd0046c7350bc592ab8e3814abbd39a6d716b5b1e5d652cc486f445c", size = 390184 }, + { url = "https://files.pythonhosted.org/packages/fc/67/f31784a7a2dcc0c1f84b691afc552ea5b26db5f56657692a12954a828db4/optree-0.19.1-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3507ae5db5827eef3da42d04c5a41df649cedc2e42d5d39dc0f869d36915a00b", size = 409025 }, + { url = "https://files.pythonhosted.org/packages/3e/a5/647b93eb16244cc7f6dfccc025ac495245e306ff4cb8f9ad15718219141a/optree-0.19.1-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:732c4581fb666869b8b391ec4ca13d2729795f9abe72b5aec2e582bcbea1975d", size = 449514 }, + { url = "https://files.pythonhosted.org/packages/c7/8e/d251c9338771ef0f9db8e538bd77810100c495734b57494464c7e223f0d0/optree-0.19.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:e12ee3776a16f6feaa8263b92469ad546b870af71d50602745855d8449219221", size = 341586 }, +] + [[package]] name = "orbax-checkpoint" version = "0.11.19" @@ -2925,7 +3188,7 @@ wheels = [ [[package]] name = "pandas" -version = "2.3.1" +version = "2.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, @@ -2934,42 +3197,42 @@ dependencies = [ { name = "pytz" }, { name = "tzdata" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d1/6f/75aa71f8a14267117adeeed5d21b204770189c0a0025acbdc03c337b28fc/pandas-2.3.1.tar.gz", hash = "sha256:0a95b9ac964fe83ce317827f80304d37388ea77616b1425f0ae41c9d2d0d7bb2", size = 4487493 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c4/ca/aa97b47287221fa37a49634532e520300088e290b20d690b21ce3e448143/pandas-2.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:22c2e866f7209ebc3a8f08d75766566aae02bcc91d196935a1d9e59c7b990ac9", size = 11542731 }, - { url = "https://files.pythonhosted.org/packages/80/bf/7938dddc5f01e18e573dcfb0f1b8c9357d9b5fa6ffdee6e605b92efbdff2/pandas-2.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3583d348546201aff730c8c47e49bc159833f971c2899d6097bce68b9112a4f1", size = 10790031 }, - { url = "https://files.pythonhosted.org/packages/ee/2f/9af748366763b2a494fed477f88051dbf06f56053d5c00eba652697e3f94/pandas-2.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f951fbb702dacd390561e0ea45cdd8ecfa7fb56935eb3dd78e306c19104b9b0", size = 11724083 }, - { url = "https://files.pythonhosted.org/packages/2c/95/79ab37aa4c25d1e7df953dde407bb9c3e4ae47d154bc0dd1692f3a6dcf8c/pandas-2.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd05b72ec02ebfb993569b4931b2e16fbb4d6ad6ce80224a3ee838387d83a191", size = 12342360 }, - { url = "https://files.pythonhosted.org/packages/75/a7/d65e5d8665c12c3c6ff5edd9709d5836ec9b6f80071b7f4a718c6106e86e/pandas-2.3.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1b916a627919a247d865aed068eb65eb91a344b13f5b57ab9f610b7716c92de1", size = 13202098 }, - { url = "https://files.pythonhosted.org/packages/65/f3/4c1dbd754dbaa79dbf8b537800cb2fa1a6e534764fef50ab1f7533226c5c/pandas-2.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fe67dc676818c186d5a3d5425250e40f179c2a89145df477dd82945eaea89e97", size = 13837228 }, - { url = "https://files.pythonhosted.org/packages/3f/d6/d7f5777162aa9b48ec3910bca5a58c9b5927cfd9cfde3aa64322f5ba4b9f/pandas-2.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:2eb789ae0274672acbd3c575b0598d213345660120a257b47b5dafdc618aec83", size = 11336561 }, - { url = "https://files.pythonhosted.org/packages/76/1c/ccf70029e927e473a4476c00e0d5b32e623bff27f0402d0a92b7fc29bb9f/pandas-2.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2b0540963d83431f5ce8870ea02a7430adca100cec8a050f0811f8e31035541b", size = 11566608 }, - { url = "https://files.pythonhosted.org/packages/ec/d3/3c37cb724d76a841f14b8f5fe57e5e3645207cc67370e4f84717e8bb7657/pandas-2.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fe7317f578c6a153912bd2292f02e40c1d8f253e93c599e82620c7f69755c74f", size = 10823181 }, - { url = "https://files.pythonhosted.org/packages/8a/4c/367c98854a1251940edf54a4df0826dcacfb987f9068abf3e3064081a382/pandas-2.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6723a27ad7b244c0c79d8e7007092d7c8f0f11305770e2f4cd778b3ad5f9f85", size = 11793570 }, - { url = "https://files.pythonhosted.org/packages/07/5f/63760ff107bcf5146eee41b38b3985f9055e710a72fdd637b791dea3495c/pandas-2.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3462c3735fe19f2638f2c3a40bd94ec2dc5ba13abbb032dd2fa1f540a075509d", size = 12378887 }, - { url = "https://files.pythonhosted.org/packages/15/53/f31a9b4dfe73fe4711c3a609bd8e60238022f48eacedc257cd13ae9327a7/pandas-2.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:98bcc8b5bf7afed22cc753a28bc4d9e26e078e777066bc53fac7904ddef9a678", size = 13230957 }, - { url = "https://files.pythonhosted.org/packages/e0/94/6fce6bf85b5056d065e0a7933cba2616dcb48596f7ba3c6341ec4bcc529d/pandas-2.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4d544806b485ddf29e52d75b1f559142514e60ef58a832f74fb38e48d757b299", size = 13883883 }, - { url = "https://files.pythonhosted.org/packages/c8/7b/bdcb1ed8fccb63d04bdb7635161d0ec26596d92c9d7a6cce964e7876b6c1/pandas-2.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:b3cd4273d3cb3707b6fffd217204c52ed92859533e31dc03b7c5008aa933aaab", size = 11340212 }, - { url = "https://files.pythonhosted.org/packages/46/de/b8445e0f5d217a99fe0eeb2f4988070908979bec3587c0633e5428ab596c/pandas-2.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:689968e841136f9e542020698ee1c4fbe9caa2ed2213ae2388dc7b81721510d3", size = 11588172 }, - { url = "https://files.pythonhosted.org/packages/1e/e0/801cdb3564e65a5ac041ab99ea6f1d802a6c325bb6e58c79c06a3f1cd010/pandas-2.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:025e92411c16cbe5bb2a4abc99732a6b132f439b8aab23a59fa593eb00704232", size = 10717365 }, - { url = "https://files.pythonhosted.org/packages/51/a5/c76a8311833c24ae61a376dbf360eb1b1c9247a5d9c1e8b356563b31b80c/pandas-2.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b7ff55f31c4fcb3e316e8f7fa194566b286d6ac430afec0d461163312c5841e", size = 11280411 }, - { url = "https://files.pythonhosted.org/packages/da/01/e383018feba0a1ead6cf5fe8728e5d767fee02f06a3d800e82c489e5daaf/pandas-2.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7dcb79bf373a47d2a40cf7232928eb7540155abbc460925c2c96d2d30b006eb4", size = 11988013 }, - { url = "https://files.pythonhosted.org/packages/5b/14/cec7760d7c9507f11c97d64f29022e12a6cc4fc03ac694535e89f88ad2ec/pandas-2.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:56a342b231e8862c96bdb6ab97170e203ce511f4d0429589c8ede1ee8ece48b8", size = 12767210 }, - { url = "https://files.pythonhosted.org/packages/50/b9/6e2d2c6728ed29fb3d4d4d302504fb66f1a543e37eb2e43f352a86365cdf/pandas-2.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ca7ed14832bce68baef331f4d7f294411bed8efd032f8109d690df45e00c4679", size = 13440571 }, - { url = "https://files.pythonhosted.org/packages/80/a5/3a92893e7399a691bad7664d977cb5e7c81cf666c81f89ea76ba2bff483d/pandas-2.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:ac942bfd0aca577bef61f2bc8da8147c4ef6879965ef883d8e8d5d2dc3e744b8", size = 10987601 }, - { url = "https://files.pythonhosted.org/packages/32/ed/ff0a67a2c5505e1854e6715586ac6693dd860fbf52ef9f81edee200266e7/pandas-2.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9026bd4a80108fac2239294a15ef9003c4ee191a0f64b90f170b40cfb7cf2d22", size = 11531393 }, - { url = "https://files.pythonhosted.org/packages/c7/db/d8f24a7cc9fb0972adab0cc80b6817e8bef888cfd0024eeb5a21c0bb5c4a/pandas-2.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6de8547d4fdb12421e2d047a2c446c623ff4c11f47fddb6b9169eb98ffba485a", size = 10668750 }, - { url = "https://files.pythonhosted.org/packages/0f/b0/80f6ec783313f1e2356b28b4fd8d2148c378370045da918c73145e6aab50/pandas-2.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:782647ddc63c83133b2506912cc6b108140a38a37292102aaa19c81c83db2928", size = 11342004 }, - { url = "https://files.pythonhosted.org/packages/e9/e2/20a317688435470872885e7fc8f95109ae9683dec7c50be29b56911515a5/pandas-2.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ba6aff74075311fc88504b1db890187a3cd0f887a5b10f5525f8e2ef55bfdb9", size = 12050869 }, - { url = "https://files.pythonhosted.org/packages/55/79/20d746b0a96c67203a5bee5fb4e00ac49c3e8009a39e1f78de264ecc5729/pandas-2.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e5635178b387bd2ba4ac040f82bc2ef6e6b500483975c4ebacd34bec945fda12", size = 12750218 }, - { url = "https://files.pythonhosted.org/packages/7c/0f/145c8b41e48dbf03dd18fdd7f24f8ba95b8254a97a3379048378f33e7838/pandas-2.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6f3bf5ec947526106399a9e1d26d40ee2b259c66422efdf4de63c848492d91bb", size = 13416763 }, - { url = "https://files.pythonhosted.org/packages/b2/c0/54415af59db5cdd86a3d3bf79863e8cc3fa9ed265f0745254061ac09d5f2/pandas-2.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:1c78cf43c8fde236342a1cb2c34bcff89564a7bfed7e474ed2fffa6aed03a956", size = 10987482 }, - { url = "https://files.pythonhosted.org/packages/48/64/2fd2e400073a1230e13b8cd604c9bc95d9e3b962e5d44088ead2e8f0cfec/pandas-2.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8dfc17328e8da77be3cf9f47509e5637ba8f137148ed0e9b5241e1baf526e20a", size = 12029159 }, - { url = "https://files.pythonhosted.org/packages/d8/0a/d84fd79b0293b7ef88c760d7dca69828d867c89b6d9bc52d6a27e4d87316/pandas-2.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ec6c851509364c59a5344458ab935e6451b31b818be467eb24b0fe89bd05b6b9", size = 11393287 }, - { url = "https://files.pythonhosted.org/packages/50/ae/ff885d2b6e88f3c7520bb74ba319268b42f05d7e583b5dded9837da2723f/pandas-2.3.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:911580460fc4884d9b05254b38a6bfadddfcc6aaef856fb5859e7ca202e45275", size = 11309381 }, - { url = "https://files.pythonhosted.org/packages/85/86/1fa345fc17caf5d7780d2699985c03dbe186c68fee00b526813939062bb0/pandas-2.3.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f4d6feeba91744872a600e6edbbd5b033005b431d5ae8379abee5bcfa479fab", size = 11883998 }, - { url = "https://files.pythonhosted.org/packages/81/aa/e58541a49b5e6310d89474333e994ee57fea97c8aaa8fc7f00b873059bbf/pandas-2.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:fe37e757f462d31a9cd7580236a82f353f5713a80e059a29753cf938c6775d96", size = 12704705 }, - { url = "https://files.pythonhosted.org/packages/d5/f9/07086f5b0f2a19872554abeea7658200824f5835c58a106fa8f2ae96a46c/pandas-2.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5db9637dbc24b631ff3707269ae4559bce4b7fd75c1c4d7e13f40edc42df4444", size = 13189044 }, +sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/f7/f425a00df4fcc22b292c6895c6831c0c8ae1d9fac1e024d16f98a9ce8749/pandas-2.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:376c6446ae31770764215a6c937f72d917f214b43560603cd60da6408f183b6c", size = 11555763 }, + { url = "https://files.pythonhosted.org/packages/13/4f/66d99628ff8ce7857aca52fed8f0066ce209f96be2fede6cef9f84e8d04f/pandas-2.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e19d192383eab2f4ceb30b412b22ea30690c9e618f78870357ae1d682912015a", size = 10801217 }, + { url = "https://files.pythonhosted.org/packages/1d/03/3fc4a529a7710f890a239cc496fc6d50ad4a0995657dccc1d64695adb9f4/pandas-2.3.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5caf26f64126b6c7aec964f74266f435afef1c1b13da3b0636c7518a1fa3e2b1", size = 12148791 }, + { url = "https://files.pythonhosted.org/packages/40/a8/4dac1f8f8235e5d25b9955d02ff6f29396191d4e665d71122c3722ca83c5/pandas-2.3.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd7478f1463441ae4ca7308a70e90b33470fa593429f9d4c578dd00d1fa78838", size = 12769373 }, + { url = "https://files.pythonhosted.org/packages/df/91/82cc5169b6b25440a7fc0ef3a694582418d875c8e3ebf796a6d6470aa578/pandas-2.3.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4793891684806ae50d1288c9bae9330293ab4e083ccd1c5e383c34549c6e4250", size = 13200444 }, + { url = "https://files.pythonhosted.org/packages/10/ae/89b3283800ab58f7af2952704078555fa60c807fff764395bb57ea0b0dbd/pandas-2.3.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28083c648d9a99a5dd035ec125d42439c6c1c525098c58af0fc38dd1a7a1b3d4", size = 13858459 }, + { url = "https://files.pythonhosted.org/packages/85/72/530900610650f54a35a19476eca5104f38555afccda1aa11a92ee14cb21d/pandas-2.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:503cf027cf9940d2ceaa1a93cfb5f8c8c7e6e90720a2850378f0b3f3b1e06826", size = 11346086 }, + { url = "https://files.pythonhosted.org/packages/c1/fa/7ac648108144a095b4fb6aa3de1954689f7af60a14cf25583f4960ecb878/pandas-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:602b8615ebcc4a0c1751e71840428ddebeb142ec02c786e8ad6b1ce3c8dec523", size = 11578790 }, + { url = "https://files.pythonhosted.org/packages/9b/35/74442388c6cf008882d4d4bdfc4109be87e9b8b7ccd097ad1e7f006e2e95/pandas-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8fe25fc7b623b0ef6b5009149627e34d2a4657e880948ec3c840e9402e5c1b45", size = 10833831 }, + { url = "https://files.pythonhosted.org/packages/fe/e4/de154cbfeee13383ad58d23017da99390b91d73f8c11856f2095e813201b/pandas-2.3.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b468d3dad6ff947df92dcb32ede5b7bd41a9b3cceef0a30ed925f6d01fb8fa66", size = 12199267 }, + { url = "https://files.pythonhosted.org/packages/bf/c9/63f8d545568d9ab91476b1818b4741f521646cbdd151c6efebf40d6de6f7/pandas-2.3.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b98560e98cb334799c0b07ca7967ac361a47326e9b4e5a7dfb5ab2b1c9d35a1b", size = 12789281 }, + { url = "https://files.pythonhosted.org/packages/f2/00/a5ac8c7a0e67fd1a6059e40aa08fa1c52cc00709077d2300e210c3ce0322/pandas-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37b5848ba49824e5c30bedb9c830ab9b7751fd049bc7914533e01c65f79791", size = 13240453 }, + { url = "https://files.pythonhosted.org/packages/27/4d/5c23a5bc7bd209231618dd9e606ce076272c9bc4f12023a70e03a86b4067/pandas-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db4301b2d1f926ae677a751eb2bd0e8c5f5319c9cb3f88b0becbbb0b07b34151", size = 13890361 }, + { url = "https://files.pythonhosted.org/packages/8e/59/712db1d7040520de7a4965df15b774348980e6df45c129b8c64d0dbe74ef/pandas-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:f086f6fe114e19d92014a1966f43a3e62285109afe874f067f5abbdcbb10e59c", size = 11348702 }, + { url = "https://files.pythonhosted.org/packages/9c/fb/231d89e8637c808b997d172b18e9d4a4bc7bf31296196c260526055d1ea0/pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53", size = 11597846 }, + { url = "https://files.pythonhosted.org/packages/5c/bd/bf8064d9cfa214294356c2d6702b716d3cf3bb24be59287a6a21e24cae6b/pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35", size = 10729618 }, + { url = "https://files.pythonhosted.org/packages/57/56/cf2dbe1a3f5271370669475ead12ce77c61726ffd19a35546e31aa8edf4e/pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908", size = 11737212 }, + { url = "https://files.pythonhosted.org/packages/e5/63/cd7d615331b328e287d8233ba9fdf191a9c2d11b6af0c7a59cfcec23de68/pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89", size = 12362693 }, + { url = "https://files.pythonhosted.org/packages/a6/de/8b1895b107277d52f2b42d3a6806e69cfef0d5cf1d0ba343470b9d8e0a04/pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98", size = 12771002 }, + { url = "https://files.pythonhosted.org/packages/87/21/84072af3187a677c5893b170ba2c8fbe450a6ff911234916da889b698220/pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084", size = 13450971 }, + { url = "https://files.pythonhosted.org/packages/86/41/585a168330ff063014880a80d744219dbf1dd7a1c706e75ab3425a987384/pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b", size = 10992722 }, + { url = "https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713", size = 11544671 }, + { url = "https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8", size = 10680807 }, + { url = "https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d", size = 11709872 }, + { url = "https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac", size = 12306371 }, + { url = "https://files.pythonhosted.org/packages/33/81/a3afc88fca4aa925804a27d2676d22dcd2031c2ebe08aabd0ae55b9ff282/pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c", size = 12765333 }, + { url = "https://files.pythonhosted.org/packages/8d/0f/b4d4ae743a83742f1153464cf1a8ecfafc3ac59722a0b5c8602310cb7158/pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493", size = 13418120 }, + { url = "https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee", size = 10993991 }, + { url = "https://files.pythonhosted.org/packages/f9/ca/3f8d4f49740799189e1395812f3bf23b5e8fc7c190827d55a610da72ce55/pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5", size = 12048227 }, + { url = "https://files.pythonhosted.org/packages/0e/5a/f43efec3e8c0cc92c4663ccad372dbdff72b60bdb56b2749f04aa1d07d7e/pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21", size = 11411056 }, + { url = "https://files.pythonhosted.org/packages/46/b1/85331edfc591208c9d1a63a06baa67b21d332e63b7a591a5ba42a10bb507/pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78", size = 11645189 }, + { url = "https://files.pythonhosted.org/packages/44/23/78d645adc35d94d1ac4f2a3c4112ab6f5b8999f4898b8cdf01252f8df4a9/pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110", size = 12121912 }, + { url = "https://files.pythonhosted.org/packages/53/da/d10013df5e6aaef6b425aa0c32e1fc1f3e431e4bcabd420517dceadce354/pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86", size = 12712160 }, + { url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233 }, ] [[package]] @@ -3019,7 +3282,7 @@ name = "pexpect" version = "4.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ptyprocess" }, + { name = "ptyprocess", marker = "(python_full_version < '3.13' and sys_platform == 'emscripten') or (python_full_version < '3.13' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450 } wheels = [ @@ -4009,7 +4272,9 @@ name = "scipy" version = "1.16.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.13'", + "python_full_version >= '3.13' and sys_platform == 'win32'", + "python_full_version >= '3.13' and sys_platform == 'emscripten'", + "python_full_version >= '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.12.*'", "python_full_version == '3.11.*'", ] @@ -4056,6 +4321,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/eb/c4/231cac7a8385394ebbbb4f1ca662203e9d8c332825ab4f36ffc3ead09a42/scipy-1.16.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f56296fefca67ba605fd74d12f7bd23636267731a72cb3947963e76b8c0a25db", size = 38515076 }, ] +[[package]] +name = "seaborn" +version = "0.13.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "matplotlib" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "pandas" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/86/59/a451d7420a77ab0b98f7affa3a1d78a313d2f7281a57afb1a34bae8ab412/seaborn-0.13.2.tar.gz", hash = "sha256:93e60a40988f4d65e9f4885df477e2fdaff6b73a9ded434c1ab356dd57eefff7", size = 1457696 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl", hash = "sha256:636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987", size = 294914 }, +] + [[package]] name = "send2trash" version = "1.8.3"