From 778fb05213e4426a54abdb9c71d2abd0cdadcbeb Mon Sep 17 00:00:00 2001 From: Amitai Date: Fri, 6 Mar 2026 17:12:36 +0200 Subject: [PATCH 01/10] fix: correct four bugs in core clustering and utility code - _cluster.py: get_random_batch sampled indices in [0, batch_size) instead of the full dataset; now uses randperm(n)[:batch_size] - _spectralnet_model.py: orthonorm_weights was never initialised in __init__, causing AttributeError on the first forward pass when should_update_orth_weights=False; initialise to None and auto-compute on first call - _utils.py: get_nearest_neighbors always queried X against itself, ignoring the Y parameter; fix to use kneighbors(Y_np) - _trainers: AETrainer and SiameseTrainer used hardcoded relative paths for weight files that broke when called from any directory other than the project root; use __file__-relative paths with os.makedirs --- src/spectralnet/_cluster.py | 6 +++-- src/spectralnet/_models/_spectralnet_model.py | 3 ++- src/spectralnet/_trainers/_ae_trainer.py | 8 +++--- .../_trainers/_siamesenet_trainer.py | 5 +++- src/spectralnet/_utils.py | 27 ++++++++++--------- 5 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/spectralnet/_cluster.py b/src/spectralnet/_cluster.py index a4a8e93..b8ca5ad 100644 --- a/src/spectralnet/_cluster.py +++ b/src/spectralnet/_cluster.py @@ -269,8 +269,10 @@ def get_random_batch(self, batch_size: int = 1024) -> tuple: The raw batch and the encoded batch. """ - permuted_indices = torch.randperm(batch_size) - X_raw = self._X.view(self._X.size(0), -1) + n = len(self._X) + batch_size = min(batch_size, n) + permuted_indices = torch.randperm(n)[:batch_size] + X_raw = self._X.view(n, -1) X_encoded = X_raw if self.should_use_ae: diff --git a/src/spectralnet/_models/_spectralnet_model.py b/src/spectralnet/_models/_spectralnet_model.py index e8bfb5f..d645de6 100644 --- a/src/spectralnet/_models/_spectralnet_model.py +++ b/src/spectralnet/_models/_spectralnet_model.py @@ -9,6 +9,7 @@ def __init__(self, architecture: dict, input_dim: int): self.architecture = architecture self.layers = nn.ModuleList() self.input_dim = input_dim + self.orthonorm_weights = None current_dim = self.input_dim for i, layer in enumerate(self.architecture): @@ -77,7 +78,7 @@ def forward( x = layer(x) Y_tilde = x - if should_update_orth_weights: + if should_update_orth_weights or self.orthonorm_weights is None: self.orthonorm_weights = self._make_orthonorm_weights(Y_tilde) Y = Y_tilde @ self.orthonorm_weights diff --git a/src/spectralnet/_trainers/_ae_trainer.py b/src/spectralnet/_trainers/_ae_trainer.py index c36a815..dc4f5af 100644 --- a/src/spectralnet/_trainers/_ae_trainer.py +++ b/src/spectralnet/_trainers/_ae_trainer.py @@ -20,10 +20,10 @@ def __init__(self, config: dict, device: torch.device): self.patience = self.ae_config["patience"] self.architecture = self.ae_config["hiddens"] self.batch_size = self.ae_config["batch_size"] - self.weights_dir = "spectralnet/_trainers/weights" - self.weights_path = "spectralnet/_trainers/weights/ae_weights.pth" - if not os.path.exists(self.weights_dir): - os.makedirs(self.weights_dir) + _here = os.path.dirname(os.path.abspath(__file__)) + self.weights_dir = os.path.join(_here, "weights") + self.weights_path = os.path.join(self.weights_dir, "ae_weights.pth") + os.makedirs(self.weights_dir, exist_ok=True) def train(self, X: torch.Tensor) -> AEModel: self.X = X.view(X.size(0), -1) diff --git a/src/spectralnet/_trainers/_siamesenet_trainer.py b/src/spectralnet/_trainers/_siamesenet_trainer.py index b92ef42..66726a4 100644 --- a/src/spectralnet/_trainers/_siamesenet_trainer.py +++ b/src/spectralnet/_trainers/_siamesenet_trainer.py @@ -49,7 +49,10 @@ def __init__(self, config: dict, device: torch.device): self.architecture = self.siamese_config["hiddens"] self.batch_size = self.siamese_config["batch_size"] self.use_approx = self.siamese_config["use_approx"] - self.weights_path = "spectralnet/_trainers/weights/siamese_weights.pth" + _here = os.path.dirname(os.path.abspath(__file__)) + _weights_dir = os.path.join(_here, "weights") + os.makedirs(_weights_dir, exist_ok=True) + self.weights_path = os.path.join(_weights_dir, "siamese_weights.pth") def train(self, X: torch.Tensor) -> SiameseNetModel: self.X = X.view(X.size(0), -1) diff --git a/src/spectralnet/_utils.py b/src/spectralnet/_utils.py index a4f12ab..fcf8de6 100644 --- a/src/spectralnet/_utils.py +++ b/src/spectralnet/_utils.py @@ -281,10 +281,10 @@ def get_nearest_neighbors( Y = X if len(X) < k: k = len(X) - X = X.cpu().detach().numpy() - Y = Y.cpu().detach().numpy() - nbrs = NearestNeighbors(n_neighbors=k).fit(X) - Dis, Ids = nbrs.kneighbors(X) + X_np = X.cpu().detach().numpy() + Y_np = Y.cpu().detach().numpy() + nbrs = NearestNeighbors(n_neighbors=k).fit(X_np) + Dis, Ids = nbrs.kneighbors(Y_np) return Dis, Ids @@ -375,8 +375,9 @@ def get_gaussian_kernel( """ if not is_local: - # global scale - W = torch.exp(-torch.pow(D, 2) / (scale**2)) + # global scale — ensure scale is a plain Python float to avoid + # NumPy scalar __array_wrap__ deprecation with torch operations + W = torch.exp(-torch.pow(D, 2) / (float(scale) ** 2)) else: # local scales W = torch.exp( @@ -385,9 +386,10 @@ def get_gaussian_kernel( ) if Ids is not None: n, k = Ids.shape - mask = torch.zeros([n, n]).to(device=device) - for i in range(len(Ids)): - mask[i, Ids[i]] = 1 + mask = torch.zeros([n, n], device=device) + row_idx = torch.arange(n, device=device).repeat_interleave(k) + col_idx = torch.tensor(Ids, device=device).reshape(-1) + mask[row_idx, col_idx] = 1 W = W * mask sym_W = (W + torch.t(W)) / 2.0 return sym_W @@ -419,9 +421,10 @@ def get_t_kernel( W = torch.pow(1 + torch.pow(D, 2), -1) if Ids is not None: n, k = Ids.shape - mask = torch.zeros([n, n]).to(device=device) - for i in range(len(Ids)): - mask[i, Ids[i]] = 1 + mask = torch.zeros([n, n], device=device) + row_idx = torch.arange(n, device=device).repeat_interleave(k) + col_idx = torch.tensor(Ids, device=device).reshape(-1) + mask[row_idx, col_idx] = 1 W = W * mask sym_W = (W + W.T) / 2.0 return sym_W From e66ed7fa056d0cc4b1e4da7d75021a7a2b7134c0 Mon Sep 17 00:00:00 2001 From: Amitai Date: Fri, 6 Mar 2026 17:12:58 +0200 Subject: [PATCH 02/10] test: add comprehensive test suite (74 tests, 0 warnings) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add five test modules covering all major components: - test_models.py (15): SpectralNetModel orthonormality, layer types, SiameseNet shared weights, AEModel encoder/decoder symmetry - test_losses.py (10): SpectralNetLoss and ContrastiveLoss properties — scalar output, non-negativity, zero-affinity, perfect-cluster, margin - test_utils.py (29): Laplacian (row sums, PSD, symmetry), nearest neighbours (shapes, Y param), scale, Gaussian/t-kernels, Grassmann distance, cost matrix - test_metrics.py (7): ACC and NMI correctness - test_spectralnet.py (13): end-to-end fit/predict/transform on blobs Add conftest.py global seed fixture for deterministic results. --- src/tests/conftest.py | 10 ++ src/tests/test_losses.py | 106 ++++++++++++++++ src/tests/test_metrics.py | 58 +++++++++ src/tests/test_models.py | 137 +++++++++++++++++++++ src/tests/test_spectralnet.py | 110 +++++++++++++++++ src/tests/test_utils.py | 224 ++++++++++++++++++++++++++++++++++ 6 files changed, 645 insertions(+) create mode 100644 src/tests/conftest.py create mode 100644 src/tests/test_losses.py create mode 100644 src/tests/test_metrics.py create mode 100644 src/tests/test_models.py create mode 100644 src/tests/test_spectralnet.py create mode 100644 src/tests/test_utils.py diff --git a/src/tests/conftest.py b/src/tests/conftest.py new file mode 100644 index 0000000..aec8753 --- /dev/null +++ b/src/tests/conftest.py @@ -0,0 +1,10 @@ +import torch +import random +import numpy as np + + +def pytest_configure(config): + """Set global seeds for reproducibility across all tests.""" + torch.manual_seed(42) + np.random.seed(42) + random.seed(42) diff --git a/src/tests/test_losses.py b/src/tests/test_losses.py new file mode 100644 index 0000000..433f39e --- /dev/null +++ b/src/tests/test_losses.py @@ -0,0 +1,106 @@ +"""Tests for SpectralNet and Siamese loss functions.""" +import pytest +import torch + +from spectralnet._losses._spectralnet_loss import SpectralNetLoss +from spectralnet._losses._siamese_loss import ContrastiveLoss + + +class TestSpectralNetLoss: + def setup_method(self): + self.criterion = SpectralNetLoss() + + def test_loss_is_scalar(self): + W = torch.rand(16, 16) + W = (W + W.T) / 2 # symmetric + Y = torch.randn(16, 4) + loss = self.criterion(W, Y) + assert loss.shape == () + + def test_loss_is_non_negative(self): + W = torch.rand(16, 16) + W = (W + W.T) / 2 + W = W.abs() + Y = torch.randn(16, 4) + loss = self.criterion(W, Y) + assert loss.item() >= 0.0 + + def test_zero_affinity_gives_zero_loss(self): + W = torch.zeros(16, 16) + Y = torch.randn(16, 4) + loss = self.criterion(W, Y) + assert loss.item() == pytest.approx(0.0, abs=1e-6) + + def test_identical_embeddings_give_low_loss(self): + """If all embeddings are the same point, pairwise distances are 0, + so the loss should be 0 regardless of W.""" + n, d = 16, 4 + W = torch.rand(n, n) + W = (W + W.T) / 2 + Y = torch.ones(n, d) + loss = self.criterion(W, Y) + assert loss.item() == pytest.approx(0.0, abs=1e-5) + + def test_loss_decreases_with_better_clustering(self): + """Embeddings perfectly aligned with clusters should have lower loss + than random embeddings for a block-diagonal affinity.""" + n = 32 + # Block-diagonal W: two equal clusters + W = torch.zeros(n, n) + W[:16, :16] = 1.0 + W[16:, 16:] = 1.0 + + # Perfect 2-cluster embeddings (orthogonal) + Y_good = torch.zeros(n, 2) + Y_good[:16, 0] = 1.0 + Y_good[16:, 1] = 1.0 + + Y_bad = torch.randn(n, 2) + + loss_good = self.criterion(W, Y_good) + loss_bad = self.criterion(W, Y_bad) + assert loss_good.item() < loss_bad.item() + + +class TestContrastiveLoss: + def setup_method(self): + self.criterion = ContrastiveLoss(margin=1.0) + + def test_loss_is_scalar(self): + o1 = torch.randn(16, 8) + o2 = torch.randn(16, 8) + labels = torch.randint(0, 2, (16,)).float() + loss = self.criterion(o1, o2, labels) + assert loss.shape == () + + def test_loss_is_non_negative(self): + o1 = torch.randn(32, 8) + o2 = torch.randn(32, 8) + labels = torch.randint(0, 2, (32,)).float() + loss = self.criterion(o1, o2, labels) + assert loss.item() >= 0.0 + + def test_identical_positive_pairs_zero_loss(self): + """Same vectors as positive pairs should give 0 loss.""" + x = torch.randn(16, 8) + labels = torch.ones(16) + loss = self.criterion(x, x, labels) + assert loss.item() == pytest.approx(0.0, abs=1e-6) + + def test_far_negative_pairs_zero_loss(self): + """Very far-apart vectors as negative pairs should give 0 loss + (clamped margin term).""" + o1 = torch.zeros(16, 8) + o2 = torch.ones(16, 8) * 100 # far away + labels = torch.zeros(16) + loss = self.criterion(o1, o2, labels) + assert loss.item() == pytest.approx(0.0, abs=1e-4) + + def test_custom_margin(self): + crit = ContrastiveLoss(margin=2.0) + o1 = torch.zeros(8, 4) + # distance = 1.0 (< margin=2), so negative loss should be (2-1)^2 = 1 + o2 = torch.ones(8, 4) + labels = torch.zeros(8) + loss = crit(o1, o2, labels) + assert loss.item() > 0.0 diff --git a/src/tests/test_metrics.py b/src/tests/test_metrics.py new file mode 100644 index 0000000..e313b8e --- /dev/null +++ b/src/tests/test_metrics.py @@ -0,0 +1,58 @@ +"""Tests for the Metrics class (ACC and NMI).""" +import pytest +import numpy as np + +from spectralnet._metrics import Metrics + + +class TestAccScore: + def test_perfect_assignment(self): + y = np.array([0, 0, 1, 1, 2, 2]) + assignments = np.array([0, 0, 1, 1, 2, 2]) + acc = Metrics.acc_score(assignments, y, n_clusters=3) + assert acc == pytest.approx(1.0) + + def test_permuted_assignment_still_perfect(self): + """Cluster labels are arbitrary; a permutation should still score 1.0.""" + y = np.array([0, 0, 1, 1, 2, 2]) + # clusters 0->2, 1->0, 2->1 + assignments = np.array([2, 2, 0, 0, 1, 1]) + acc = Metrics.acc_score(assignments, y, n_clusters=3) + assert acc == pytest.approx(1.0) + + def test_worst_assignment(self): + """No correct assignment should give low accuracy.""" + y = np.array([0] * 5 + [1] * 5) + assignments = np.array([1] * 5 + [0] * 5) + acc = Metrics.acc_score(assignments, y, n_clusters=2) + # After optimal matching, this is actually perfect (permutation) + assert acc == pytest.approx(1.0) + + def test_random_returns_float(self): + rng = np.random.default_rng(42) + y = rng.integers(0, 5, 50) + assignments = rng.integers(0, 5, 50) + acc = Metrics.acc_score(assignments, y, n_clusters=5) + assert isinstance(acc, float) + assert 0.0 <= acc <= 1.0 + + +class TestNmiScore: + def test_perfect_nmi(self): + y = np.array([0, 0, 1, 1, 2, 2]) + acc = Metrics.nmi_score(y, y) + assert acc == pytest.approx(1.0) + + def test_random_labels_low_nmi(self): + rng = np.random.default_rng(0) + y = rng.integers(0, 5, 100) + assignments = rng.integers(0, 5, 100) + nmi = Metrics.nmi_score(assignments, y) + # NMI should be small for random assignments (not guaranteed to be + # 0 but typically << 0.5 for random data with 5 classes) + assert 0.0 <= nmi <= 1.0 + + def test_nmi_symmetric(self): + y = np.array([0, 0, 1, 1, 2, 2]) + p = np.array([0, 1, 1, 2, 2, 0]) + assert Metrics.nmi_score(y, p) == pytest.approx(Metrics.nmi_score(p, y)) diff --git a/src/tests/test_models.py b/src/tests/test_models.py new file mode 100644 index 0000000..cfd4ec9 --- /dev/null +++ b/src/tests/test_models.py @@ -0,0 +1,137 @@ +"""Tests for SpectralNet, SiameseNet, and AE model architectures.""" +import pytest +import torch +import torch.nn as nn + +from spectralnet._models._spectralnet_model import SpectralNetModel +from spectralnet._models._siamesenet_model import SiameseNetModel +from spectralnet._models._ae_model import AEModel + + +# --------------------------------------------------------------------------- +# SpectralNetModel +# --------------------------------------------------------------------------- + +class TestSpectralNetModel: + def _make_model(self, arch=(32, 16, 4), input_dim=8): + return SpectralNetModel(architecture=list(arch), input_dim=input_dim) + + def test_orthonorm_weights_initialized_to_none(self): + model = self._make_model() + assert model.orthonorm_weights is None + + def test_forward_update_orth_weights(self): + model = self._make_model(arch=(16, 4), input_dim=8) + x = torch.randn(32, 8) + y = model(x, should_update_orth_weights=True) + assert y.shape == (32, 4) + assert model.orthonorm_weights is not None + + def test_forward_no_update_uses_existing_weights(self): + """Calling with should_update_orth_weights=False on a fresh model + should still work by auto-initialising weights on first call.""" + model = self._make_model(arch=(16, 4), input_dim=8) + x = torch.randn(32, 8) + # should not raise AttributeError + y = model(x, should_update_orth_weights=False) + assert y.shape == (32, 4) + + def test_output_is_orthonormal(self): + """Columns of Y should be approximately orthonormal (Y^T Y ~ I).""" + model = self._make_model(arch=(32, 4), input_dim=8) + x = torch.randn(128, 8) + y = model(x, should_update_orth_weights=True) + gram = (y.T @ y) / y.shape[0] + eye = torch.eye(4) + assert torch.allclose(gram, eye, atol=1e-4), f"Gram matrix not close to I:\n{gram}" + + def test_layer_count_matches_architecture(self): + arch = [64, 32, 16, 4] + model = SpectralNetModel(architecture=arch, input_dim=10) + assert len(model.layers) == len(arch) + + def test_last_layer_uses_tanh(self): + model = self._make_model(arch=(16, 4), input_dim=8) + last_layer = model.layers[-1] + activations = [m for m in last_layer.modules() if isinstance(m, nn.Tanh)] + assert len(activations) == 1 + + def test_hidden_layers_use_leaky_relu(self): + model = self._make_model(arch=(16, 8, 4), input_dim=8) + for layer in list(model.layers)[:-1]: + activations = [m for m in layer.modules() if isinstance(m, nn.LeakyReLU)] + assert len(activations) == 1 + + +# --------------------------------------------------------------------------- +# SiameseNetModel +# --------------------------------------------------------------------------- + +class TestSiameseNetModel: + def _make_model(self, arch=(32, 16, 8), input_dim=10): + return SiameseNetModel(architecture=list(arch), input_dim=input_dim) + + def test_forward_returns_two_outputs(self): + model = self._make_model() + x1 = torch.randn(16, 10) + x2 = torch.randn(16, 10) + o1, o2 = model(x1, x2) + assert o1.shape == (16, 8) + assert o2.shape == (16, 8) + + def test_forward_once_output_shape(self): + model = self._make_model(arch=(32, 8), input_dim=10) + x = torch.randn(20, 10) + out = model.forward_once(x) + assert out.shape == (20, 8) + + def test_shared_weights(self): + """forward_once with x1 and x2 separately must equal forward(x1, x2).""" + model = self._make_model() + x1, x2 = torch.randn(8, 10), torch.randn(8, 10) + o1_direct = model.forward_once(x1) + o2_direct = model.forward_once(x2) + o1, o2 = model(x1, x2) + assert torch.allclose(o1, o1_direct) + assert torch.allclose(o2, o2_direct) + + def test_identical_inputs_produce_identical_outputs(self): + model = self._make_model() + x = torch.randn(8, 10) + o1, o2 = model(x, x) + assert torch.allclose(o1, o2) + + +# --------------------------------------------------------------------------- +# AEModel +# --------------------------------------------------------------------------- + +class TestAEModel: + def _make_model(self, arch=(32, 16, 4), input_dim=64): + return AEModel(architecture=list(arch), input_dim=input_dim) + + def test_encode_output_shape(self): + model = self._make_model(arch=(32, 8), input_dim=64) + x = torch.randn(16, 64) + z = model.encode(x) + assert z.shape == (16, 8) + + def test_decode_output_shape(self): + model = self._make_model(arch=(32, 8), input_dim=64) + z = torch.randn(16, 8) + x_hat = model.decode(z) + assert x_hat.shape == (16, 64) + + def test_forward_roundtrip_shape(self): + model = self._make_model(arch=(32, 16, 8), input_dim=64) + x = torch.randn(16, 64) + x_hat = model(x) + assert x_hat.shape == x.shape + + def test_encoder_decoder_symmetry(self): + """Encoder + decoder should reconstruct the input dimension.""" + arch = [128, 64, 32, 8] + input_dim = 256 + model = AEModel(architecture=arch, input_dim=input_dim) + x = torch.randn(4, input_dim) + assert model(x).shape == (4, input_dim) diff --git a/src/tests/test_spectralnet.py b/src/tests/test_spectralnet.py new file mode 100644 index 0000000..40ea87e --- /dev/null +++ b/src/tests/test_spectralnet.py @@ -0,0 +1,110 @@ +"""Integration tests for the SpectralNet clustering pipeline.""" +import pytest +import numpy as np +import torch +from sklearn.datasets import make_blobs + +from spectralnet import SpectralNet + + +def _make_blobs_tensor(n_samples=200, n_features=4, centers=3, seed=0): + X, y = make_blobs( + n_samples=n_samples, n_features=n_features, centers=centers, random_state=seed + ) + return torch.tensor(X, dtype=torch.float32), torch.tensor(y, dtype=torch.long) + + +class TestSpectralNetInit: + def test_default_construction(self): + # Default spectral_hiddens ends with 10, so n_clusters must match + model = SpectralNet(n_clusters=10) + assert model.n_clusters == 10 + + def test_hidden_size_mismatch_raises(self): + with pytest.raises(ValueError): + SpectralNet(n_clusters=5, spectral_hiddens=[64, 32, 4]) # last != 5 + + def test_hidden_size_match_ok(self): + SpectralNet(n_clusters=4, spectral_hiddens=[64, 4]) + + def test_device_set(self): + model = SpectralNet(n_clusters=10) + assert isinstance(model.device, torch.device) + + +class TestSpectralNetFitPredict: + """These tests use small networks / few epochs so they run quickly.""" + + @pytest.fixture + def small_model(self): + return SpectralNet( + n_clusters=3, + spectral_hiddens=[32, 3], + spectral_epochs=3, + spectral_batch_size=64, + spectral_n_nbg=5, + spectral_scale_k=5, + ) + + def test_fit_runs_without_error(self, small_model): + X, y = _make_blobs_tensor(n_samples=150, n_features=4, centers=3) + small_model.fit(X, y) + + def test_predict_returns_correct_shape(self, small_model): + X, y = _make_blobs_tensor(n_samples=150, n_features=4, centers=3) + small_model.fit(X) + assignments = small_model.predict(X) + assert assignments.shape == (150,) + + def test_predict_integer_labels(self, small_model): + X, _ = _make_blobs_tensor(n_samples=150, n_features=4, centers=3) + small_model.fit(X) + assignments = small_model.predict(X) + assert assignments.dtype in (np.int32, np.int64, int) + + def test_predict_label_range(self, small_model): + X, _ = _make_blobs_tensor(n_samples=150, n_features=4, centers=3) + small_model.fit(X) + assignments = small_model.predict(X) + assert set(np.unique(assignments)).issubset(set(range(3))) + + def test_embeddings_stored_after_predict(self, small_model): + X, _ = _make_blobs_tensor(n_samples=150, n_features=4, centers=3) + small_model.fit(X) + small_model.predict(X) + assert hasattr(small_model, "embeddings_") + assert small_model.embeddings_.shape == (150, 3) + + def test_fit_without_labels(self, small_model): + X, _ = _make_blobs_tensor(n_samples=150, n_features=4, centers=3) + small_model.fit(X) # no y + assignments = small_model.predict(X) + assert assignments.shape == (150,) + + +class TestSpectralNetGetRandomBatch: + def test_batch_size_respected(self): + model = SpectralNet( + n_clusters=3, + spectral_hiddens=[16, 3], + spectral_epochs=1, + spectral_batch_size=32, + ) + X, y = _make_blobs_tensor(n_samples=100, n_features=4, centers=3) + model.fit(X, y) + X_raw, X_enc = model.get_random_batch(batch_size=20) + assert X_raw.shape[0] == 20 + assert X_enc.shape[0] == 20 + + def test_batch_size_clamped_to_dataset(self): + """batch_size larger than dataset should return the whole dataset.""" + model = SpectralNet( + n_clusters=3, + spectral_hiddens=[16, 3], + spectral_epochs=1, + spectral_batch_size=32, + ) + X, y = _make_blobs_tensor(n_samples=50, n_features=4, centers=3) + model.fit(X, y) + X_raw, X_enc = model.get_random_batch(batch_size=9999) + assert X_raw.shape[0] == 50 diff --git a/src/tests/test_utils.py b/src/tests/test_utils.py new file mode 100644 index 0000000..94b51e9 --- /dev/null +++ b/src/tests/test_utils.py @@ -0,0 +1,224 @@ +"""Tests for spectralnet utility functions.""" +import pytest +import numpy as np +import torch + +from spectralnet._utils import ( + get_laplacian, + get_nearest_neighbors, + get_affinity_matrix, + get_gaussian_kernel, + get_t_kernel, + compute_scale, + get_grassman_distance, + calculate_cost_matrix, + get_cluster_labels_from_indices, +) + + +class TestGetLaplacian: + def test_output_shape(self): + W = torch.rand(10, 10) + W = (W + W.T) / 2 + L = get_laplacian(W) + assert L.shape == (10, 10) + + def test_row_sums_zero(self): + """Laplacian rows must sum to zero.""" + W = torch.rand(8, 8) + W = (W + W.T) / 2 + L = get_laplacian(W) + assert np.allclose(L.sum(axis=1), 0.0, atol=1e-6) + + def test_symmetric(self): + W = torch.rand(8, 8) + W = (W + W.T) / 2 + L = get_laplacian(W) + assert np.allclose(L, L.T, atol=1e-6) + + def test_positive_semidefinite(self): + """All eigenvalues should be >= 0.""" + W = torch.rand(8, 8).abs() + W = (W + W.T) / 2 + L = get_laplacian(W) + eigvals = np.linalg.eigvalsh(L) + assert np.all(eigvals >= -1e-6) + + +class TestGetNearestNeighbors: + def test_output_shapes(self): + X = torch.randn(20, 4) + Dis, Ids = get_nearest_neighbors(X, k=3) + assert Dis.shape == (20, 3) + assert Ids.shape == (20, 3) + + def test_distances_non_negative(self): + X = torch.randn(20, 4) + Dis, _ = get_nearest_neighbors(X, k=3) + assert np.all(Dis >= 0) + + def test_self_query_with_Y(self): + """When Y is provided, neighbors should be found from X for each query in Y.""" + X = torch.randn(20, 4) + Y = torch.randn(5, 4) + Dis, Ids = get_nearest_neighbors(X, Y=Y, k=3) + assert Dis.shape == (5, 3) + assert Ids.shape == (5, 3) + + def test_k_clamped_to_dataset_size(self): + """When k > len(X), it should clamp to len(X).""" + X = torch.randn(5, 4) + Dis, Ids = get_nearest_neighbors(X, k=20) + assert Ids.shape[0] == 5 + + +class TestComputeScale: + def test_local_scale_shape(self): + Dis = np.random.rand(20, 5) + scale = compute_scale(Dis, is_local=True) + assert scale.shape == (20,) + + def test_global_scale_scalar(self): + Dis = np.random.rand(20, 5) + scale = compute_scale(Dis, k=3, is_local=False) + assert np.isscalar(scale) or scale.shape == () + + def test_local_scale_median(self): + Dis = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]) + scale = compute_scale(Dis, is_local=True, med=True) + expected = np.median(Dis, axis=1) + np.testing.assert_allclose(scale, expected) + + def test_local_scale_max(self): + Dis = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]) + scale = compute_scale(Dis, is_local=True, med=False) + expected = np.max(Dis, axis=1) + np.testing.assert_allclose(scale, expected) + + +class TestGetGaussianKernel: + def _make_data(self, n=16, d=4, k=3): + X = torch.randn(n, d) + Dis, Ids = get_nearest_neighbors(X, k=k) + Dx = torch.cdist(X, X) + scale = compute_scale(Dis, is_local=True) + device = torch.device("cpu") + return Dx, scale, Ids, device + + def test_output_shape(self): + Dx, scale, Ids, device = self._make_data() + W = get_gaussian_kernel(Dx, scale, Ids, device, is_local=True) + assert W.shape == (16, 16) + + def test_symmetric(self): + Dx, scale, Ids, device = self._make_data() + W = get_gaussian_kernel(Dx, scale, Ids, device, is_local=True) + assert torch.allclose(W, W.T, atol=1e-6) + + def test_values_in_01(self): + Dx, scale, Ids, device = self._make_data() + W = get_gaussian_kernel(Dx, scale, Ids, device, is_local=True) + assert W.min().item() >= 0.0 + assert W.max().item() <= 1.0 + 1e-6 + + def test_diagonal_is_one(self): + """Self-similarity should be 1 (zero distance, exp(0)=1).""" + Dx, _local_scale, Ids, device = self._make_data() + global_scale = 1.0 # scalar, as expected by is_local=False + W = get_gaussian_kernel(Dx, global_scale, None, device, is_local=False) + diag = torch.diag(W) + assert torch.allclose(diag, torch.ones(16, dtype=diag.dtype), atol=1e-5) + + +class TestGetTKernel: + def _make_data(self, n=16, d=4, k=3): + X = torch.randn(n, d) + Dis, Ids = get_nearest_neighbors(X, k=k) + Dx = torch.cdist(X, X) + device = torch.device("cpu") + return Dx, Ids, device + + def test_output_shape(self): + Dx, Ids, device = self._make_data() + W = get_t_kernel(Dx, Ids, device) + assert W.shape == (16, 16) + + def test_symmetric(self): + Dx, Ids, device = self._make_data() + W = get_t_kernel(Dx, Ids, device) + assert torch.allclose(W, W.T, atol=1e-6) + + def test_values_in_01(self): + Dx, Ids, device = self._make_data() + W = get_t_kernel(Dx, Ids, device) + assert W.min().item() >= 0.0 + assert W.max().item() <= 1.0 + 1e-6 + + +class TestGetAffinityMatrix: + def test_output_shape(self): + X = torch.randn(20, 4) + W = get_affinity_matrix(X, n_neighbors=5, device=torch.device("cpu")) + assert W.shape == (20, 20) + + def test_symmetric(self): + X = torch.randn(20, 4) + W = get_affinity_matrix(X, n_neighbors=5, device=torch.device("cpu")) + assert torch.allclose(W, W.T, atol=1e-6) + + def test_non_negative(self): + X = torch.randn(20, 4) + W = get_affinity_matrix(X, n_neighbors=5, device=torch.device("cpu")) + assert W.min().item() >= 0.0 + + +class TestGetGrassmanDistance: + def test_same_subspace_zero_distance(self): + A = np.linalg.qr(np.random.randn(8, 3))[0] + dist = get_grassman_distance(A, A) + assert dist == pytest.approx(0.0, abs=1e-5) + + def test_orthogonal_subspaces(self): + """Orthogonal subspaces should have maximum Grassmann distance.""" + A = np.eye(6)[:, :3] + B = np.eye(6)[:, 3:] + dist = get_grassman_distance(A, B) + # All singular values of A^T B are 0, so distance = sum of (1-0^2) = 3 + assert dist == pytest.approx(3.0, abs=1e-5) + + def test_non_negative(self): + A = np.linalg.qr(np.random.randn(8, 3))[0] + B = np.linalg.qr(np.random.randn(8, 3))[0] + dist = get_grassman_distance(A, B) + assert dist >= 0.0 + + +class TestCalculateCostMatrix: + def test_output_shape(self): + C = np.random.randint(0, 100, (5, 5)) + cost = calculate_cost_matrix(C, n_clusters=5) + assert cost.shape == (5, 5) + + def test_cost_non_negative(self): + C = np.random.randint(0, 100, (4, 4)) + cost = calculate_cost_matrix(C, n_clusters=4) + assert np.all(cost >= 0) + + def test_perfect_confusion_matrix_zero_diagonal_cost(self): + """A diagonal confusion matrix should yield zero cost on the diagonal.""" + n = 4 + C = np.diag([100] * n) + cost = calculate_cost_matrix(C, n_clusters=n) + assert np.all(np.diag(cost) == 0) + + +class TestGetClusterLabelsFromIndices: + def test_basic(self): + indices = [(0, 2), (1, 0), (2, 3), (3, 1)] + labels = get_cluster_labels_from_indices(indices) + np.testing.assert_array_equal(labels, [2, 0, 3, 1]) + + def test_length(self): + indices = [(i, i) for i in range(6)] + labels = get_cluster_labels_from_indices(indices) + assert len(labels) == 6 From c7786fca9a183845d746c6903a4d7e0e80be969e Mon Sep 17 00:00:00 2001 From: Amitai Date: Fri, 6 Mar 2026 17:13:06 +0200 Subject: [PATCH 03/10] ci: add GitHub Actions CI and semantic-release workflows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ci.yml: run pytest on Python 3.11 and 3.12 in parallel on every push and PR to main; upload test results as artifacts. release.yml: on push to main, run python-semantic-release which reads conventional commit prefixes to decide the version bump (fix→patch, feat→minor, BREAKING CHANGE→major), creates a git tag and GitHub release, then builds and publishes to PyPI via OIDC Trusted Publisher (no API token required). --- .github/workflows/ci.yml | 40 ++++++++++++++++++++++++ .github/workflows/release.yml | 57 +++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ef5bcc4 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,40 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + test: + name: Test (Python ${{ matrix.python-version }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.11", "3.12"] + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + cache: pip + + - name: Install package with test dependencies + run: | + pip install --upgrade pip + pip install -e ".[dev]" + + - name: Run tests + run: pytest src/tests -v --tb=short --no-header + + - name: Upload test results + if: always() + uses: actions/upload-artifact@v4 + with: + name: test-results-${{ matrix.python-version }} + path: .pytest_cache/ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..abaa89d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,57 @@ +name: Release + +on: + push: + branches: [main] + +permissions: + contents: write # push tags / GitHub release + id-token: write # OIDC token for trusted PyPI publishing + +jobs: + release: + name: Semantic release & publish + runs-on: ubuntu-latest + # Only run when CI passes — avoid publishing broken code + needs: [] + + concurrency: + group: release + cancel-in-progress: false + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # full history so PSR can read all commits + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + cache: pip + + - name: Install build tools + run: pip install python-semantic-release build + + # python-semantic-release reads commit history, bumps the version in + # setup.cfg, creates a tag, a GitHub release, and builds the package. + - name: Run semantic-release + id: semrel + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: semantic-release version + + # Build sdist + wheel only when a new version was actually released + - name: Build distribution + if: steps.semrel.outputs.released == 'true' + run: python -m build + + # Publish to PyPI via OIDC (no API token needed — configure trusted + # publisher on pypi.org: owner=, + # repo=SpectralNet, workflow=release.yml, environment=pypi) + - name: Publish to PyPI + if: steps.semrel.outputs.released == 'true' + uses: pypa/gh-action-pypi-publish@release/v1 + with: + print-hash: true From 3ace05ed2ef96dda1abd5334c95e80525b88ea4d Mon Sep 17 00:00:00 2001 From: Amitai Date: Fri, 6 Mar 2026 17:13:18 +0200 Subject: [PATCH 04/10] chore: add pixi environment, semantic-release config, and dev extras - pixi.toml + pixi.lock: reproducible conda/PyPI environment via pixi; defines tasks 'test' and 'test-fast' for quick iteration - pyproject.toml: remove heavy runtime deps from [build-system] requires (only setuptools + wheel needed at build time); add [tool.semantic_release] config pointing version bump at setup.cfg - setup.cfg: add [options.extras_require] dev group (pytest, pytest-cov) and [tool:pytest] section so plain 'pytest' works from any directory --- pixi.lock | 4266 ++++++++++++++++++++++++++++++++++++++++++++++++ pixi.toml | 29 + pyproject.toml | 38 +- setup.cfg | 11 +- 4 files changed, 4331 insertions(+), 13 deletions(-) create mode 100644 pixi.lock create mode 100644 pixi.toml diff --git a/pixi.lock b/pixi.lock new file mode 100644 index 0000000..fedb366 --- /dev/null +++ b/pixi.lock @@ -0,0 +1,4266 @@ +version: 6 +environments: + default: + channels: + - url: https://conda.anaconda.org/conda-forge/ + - url: https://conda.anaconda.org/pytorch/ + indexes: + - https://pypi.org/simple + options: + pypi-prerelease-mode: if-necessary-or-explicit + packages: + linux-64: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-7_kmp_llvm.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.15.3-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aom-3.9.1-hac33072_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-hed03a55_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-he90730b_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py312h0a2e395_4.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.12.13-py312hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hac629b4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/dav1d-1.2.1-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h24cb091_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.4.0-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.25.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-12.1.0-hff5e90c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.17.1-h27c8c51_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.61.1-py312h8a5da7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.2-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gmp-6.3.0-hac33072_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gmpy2-2.2.1-py312hcaba1f9_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.14-hecca717_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/h5py-3.15.1-nompi_py312ha4f8f14_101.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-12.3.2-h6083320_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.6-nompi_h19486de_106.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.2-h33c6efd_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.9-py312h0a2e395_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-ha1258a1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.18-h0c24ade_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_101.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h0aef613_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260107.1-cxx17_h7b12aa8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.5-h088129d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libavif16-1.4.0-hcfa2d63_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h5875eb1_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-5_hfef963f_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp22.1-22.1.0-default_h99862b1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-22.1.0-default_h746c552_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h7a8fb5f_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.18.0-hcf29cc6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libde265-1.0.15-h00ab1b0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.125-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.4-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.2-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.2-h73754d4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_18.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_18.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_18.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_18.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.86.4-h6548e54_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libheif-1.19.7-gpl_hc18d805_100.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.12.2-default_hafda6a7_1000.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.2-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-5_h5e43f62_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm22-22.1.0-hf7376ad_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.2-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.67.0-had1ee68_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.8-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.18-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.55-h421ea60_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.3-h9abb657_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-6.33.5-h2b00c02_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.2-hf4e2dac_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_18.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_18.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.1-h9d88235_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtorch-2.10.0-cpu_mkl_h7058990_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-h5347b49_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.51.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.341.0-h5279c79_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.1-hca5e8e5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.2-hca6bf5a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.2-he237659_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.43-h711ed8c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-22.1.0-h4922eb0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py312h8a5da7c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.8-py312h7900ff3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.8-py312he3d6523_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/mkl-2025.3.0-h0e700b2_463.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/mpc-1.3.1-h24ddda3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/mpfr-4.2.1-h90cbb55_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mpmath-1.3.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.2-py312h33ff503_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-hbde042b_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.1-h35e630c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/optree-0.19.0-py312hd9148b4_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-haa7fec5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.1.1-py312h50c33e8_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.0.1-pyh8b19718_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-3.0.1-pyh7a1b43c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-abi-11-hc364b38_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-global-3.0.1-pyhc7ab6ef_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.10.2-py312h9da60e5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.13-hd63d673_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-2.10.0-cpu_mkl_py312_hca44ed5_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.10.2-h17e89b9_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rav1e-0.8.1-h1fbca29_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.8.0-np2py312h3226591_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.17.1-py312h54fa4ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.0-pyh332efcf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sleef-3.9.0-ha0421bc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/svt-av1-4.0.1-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sympy-1.14.0-pyh2585a3b_106.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tbb-2022.3.0-hb700be7_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/torchvision-0.25.0-cpu_py312_h843e820_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/torchvision-extra-decoders-0.0.2-py312h1b2fc9e_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.3-py312h4c3975b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.1-py312h4c3975b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.24.0-hd6090a7_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.46.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/x265-3.5-h924138e_3.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.6-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.1-hb711507_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.10-hb711507_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.47-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.7-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.7-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.2-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.5-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.7-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hceb46e0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda + - pypi: https://files.pythonhosted.org/packages/07/38/e321b0e05d8cc068a594279fb7c097efb1df66231c295d482d7ad51b6473/annoy-1.17.3.tar.gz + - pypi: https://files.pythonhosted.org/packages/5d/a0/2ea570925524ef4e00bb6c82649f5682a77fac5ab910a65c9284de422600/coverage-7.13.4-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl + - pypi: ./ + osx-arm64: + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aom-3.9.1-h7bae524_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.2.0-h7d5ae5b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.2.0-hc919400_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.6-hc919400_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py312h3093aea_4.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.12.13-py312hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/dav1d-1.2.1-hb547adb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.25.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fmt-12.1.0-h403dcb5_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fonttools-4.61.1-py312h5748b74_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.2-hce30654_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-5.2.2-h93a5062_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gmp-6.3.0-h7bae524_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gmpy2-2.2.1-py312hee6aa52_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/h5py-3.15.1-nompi_py312h4eecd6b_101.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hdf5-1.14.6-nompi_had3affe_106.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.2-hef89b57_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.4.9-py312hd8c8125_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-h385eeb1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.18-hdfa7624_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.0.0-hd64df32_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260107.1-cxx17_h2062a1b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libaec-1.1.5-h8664d51_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libavif16-1.4.0-hfce71f6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-5_h51639a9_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-hc919400_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-hc919400_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-hc919400_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-5_hb0561ab_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.18.0-hd5a2499_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.0-h55c6f16_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libde265-1.0.15-h2ffa867_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-hc11a715_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321hafb1f1b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.7.4-hf6b4638_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.2-hce30654_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.2-hdfa99f5_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_18.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_18.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_18.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libheif-1.19.7-gpl_h79e6334_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.1.2-hc919400_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-5_hd9741b5_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.2-h8088a28_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.67.0-hc438710_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.30-openmp_ha158390_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.55-h132b30e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-6.33.5-h4a5acfd_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.51.2-h1ae2325_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h1590b86_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.1-h4030677_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtorch-2.10.0-cpu_generic_hf7cc835_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.51.0-h6caf38d_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h07db88b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.0-hc7d1edf_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py312h04c11ed_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.10.8-py312h1f38498_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.10.8-py312h605b88b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/mpc-1.3.1-h8f1351a_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/mpfr-4.2.1-hb693164_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mpmath-1.3.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nomkl-1.0-h5ca1d4c_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.2-py312he281c53_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.1-hd24854e_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/optree-0.19.0-py312h766f71e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.1.1-py312h4e908a4_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.0.1-pyh8b19718_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-3.0.1-pyh7a1b43c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-abi-11-hc364b38_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-global-3.0.1-pyhc7ab6ef_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.13-h8561d8f_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pytorch-2.10.0-cpu_generic_py312_h2470ad0_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/qhull-2020.2-h420ef59_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rav1e-0.8.1-h8246384_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.8.0-np2py312he5ca3e3_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.17.1-py312h0f234b1_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.0-pyh332efcf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sleef-3.9.0-hb028509_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/svt-av1-4.0.1-h0cb729a_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sympy-1.14.0-pyh2585a3b_106.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/torchvision-0.25.0-cpu_py312_h215c760_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/torchvision-extra-decoders-0.0.2-py312hf15907b_6.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.4-py312h4409184_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py312h2bbb03f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.46.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/x265-3.5-hbc6ce65_3.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-hc919400_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hc919400_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda + - pypi: https://files.pythonhosted.org/packages/07/38/e321b0e05d8cc068a594279fb7c097efb1df66231c295d482d7ad51b6473/annoy-1.17.3.tar.gz + - pypi: https://files.pythonhosted.org/packages/5d/96/5238b1efc5922ddbdc9b0db9243152c09777804fb7c02ad1741eb18a11c0/coverage-7.13.4-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl + - pypi: ./ +packages: +- conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-7_kmp_llvm.conda + build_number: 7 + sha256: c0cddb66070dd6355311f7667ce2acccf70d1013edaa6e97f22859502fefdb22 + md5: 887b70e1d607fba7957aa02f9ee0d939 + depends: + - llvm-openmp >=9.0.1 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 8244 + timestamp: 1764092331208 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda + build_number: 7 + sha256: 7acaa2e0782cad032bdaf756b536874346ac1375745fb250e9bdd6a48a7ab3cd + md5: a44032f282e7d2acdeb1c240308052dd + depends: + - llvm-openmp >=9.0.1 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 8325 + timestamp: 1764092507920 +- conda: https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.15.3-hb03c661_0.conda + sha256: d88aa7ae766cf584e180996e92fef2aa7d8e0a0a5ab1d4d49c32390c1b5fff31 + md5: dcdc58c15961dbf17a0621312b01f5cb + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: LGPL-2.1-or-later + license_family: GPL + purls: [] + size: 584660 + timestamp: 1768327524772 +- pypi: https://files.pythonhosted.org/packages/07/38/e321b0e05d8cc068a594279fb7c097efb1df66231c295d482d7ad51b6473/annoy-1.17.3.tar.gz + name: annoy + version: 1.17.3 + sha256: 9cbfebefe0a5f843eba29c6be4c84d601f4f41ad4ded0486f1b88c3b07739c15 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aom-3.9.1-hac33072_0.conda + sha256: b08ef033817b5f9f76ce62dfcac7694e7b6b4006420372de22494503decac855 + md5: 346722a0be40f6edc53f12640d301338 + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 2706396 + timestamp: 1718551242397 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aom-3.9.1-h7bae524_0.conda + sha256: ec238f18ce8140485645252351a0eca9ef4f7a1c568a420f240a585229bc12ef + md5: 7adba36492a1bb22d98ffffe4f6fc6de + depends: + - __osx >=11.0 + - libcxx >=16 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 2235747 + timestamp: 1718551382432 +- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-hed03a55_1.conda + sha256: e511644d691f05eb12ebe1e971fd6dc3ae55a4df5c253b4e1788b789bdf2dfa6 + md5: 8ccf913aaba749a5496c17629d859ed1 + depends: + - __glibc >=2.17,<3.0.a0 + - brotli-bin 1.2.0 hb03c661_1 + - libbrotlidec 1.2.0 hb03c661_1 + - libbrotlienc 1.2.0 hb03c661_1 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 20103 + timestamp: 1764017231353 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.2.0-h7d5ae5b_1.conda + sha256: 422ac5c91f8ef07017c594d9135b7ae068157393d2a119b1908c7e350938579d + md5: 48ece20aa479be6ac9a284772827d00c + depends: + - __osx >=11.0 + - brotli-bin 1.2.0 hc919400_1 + - libbrotlidec 1.2.0 hc919400_1 + - libbrotlienc 1.2.0 hc919400_1 + license: MIT + license_family: MIT + purls: [] + size: 20237 + timestamp: 1764018058424 +- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-hb03c661_1.conda + sha256: 64b137f30b83b1dd61db6c946ae7511657eead59fdf74e84ef0ded219605aa94 + md5: af39b9a8711d4a8d437b52c1d78eb6a1 + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlidec 1.2.0 hb03c661_1 + - libbrotlienc 1.2.0 hb03c661_1 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 21021 + timestamp: 1764017221344 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.2.0-hc919400_1.conda + sha256: e2d142052a83ff2e8eab3fe68b9079cad80d109696dc063a3f92275802341640 + md5: 377d015c103ad7f3371be1777f8b584c + depends: + - __osx >=11.0 + - libbrotlidec 1.2.0 hc919400_1 + - libbrotlienc 1.2.0 hc919400_1 + license: MIT + license_family: MIT + purls: [] + size: 18628 + timestamp: 1764018033635 +- conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda + sha256: 0b75d45f0bba3e95dc693336fa51f40ea28c980131fec438afb7ce6118ed05f6 + md5: d2ffd7602c02f2b316fd921d39876885 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: bzip2-1.0.6 + license_family: BSD + purls: [] + size: 260182 + timestamp: 1771350215188 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda + sha256: 540fe54be35fac0c17feefbdc3e29725cce05d7367ffedfaaa1bdda234b019df + md5: 620b85a3f45526a8bc4d23fd78fc22f0 + depends: + - __osx >=11.0 + license: bzip2-1.0.6 + license_family: BSD + purls: [] + size: 124834 + timestamp: 1771350416561 +- conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda + sha256: cc9accf72fa028d31c2a038460787751127317dcfa991f8d1f1babf216bb454e + md5: 920bb03579f15389b9e512095ad995b7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 207882 + timestamp: 1765214722852 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.6-hc919400_0.conda + sha256: 2995f2aed4e53725e5efbc28199b46bf311c3cab2648fc4f10c2227d6d5fa196 + md5: bcb3cba70cf1eec964a03b4ba7775f01 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 180327 + timestamp: 1765215064054 +- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-hbd8a1cb_0.conda + sha256: 67cc7101b36421c5913a1687ef1b99f85b5d6868da3abbf6ec1a4181e79782fc + md5: 4492fd26db29495f0ba23f146cd5638d + depends: + - __unix + license: ISC + purls: [] + size: 147413 + timestamp: 1772006283803 +- conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 + noarch: python + sha256: 561e6660f26c35d137ee150187d89767c988413c978e1b712d53f27ddf70ea17 + md5: 9b347a7ec10940d3f7941ff6c460b551 + depends: + - cached_property >=1.5.2,<1.5.3.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 4134 + timestamp: 1615209571450 +- conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 + sha256: 6dbf7a5070cc43d90a1e4c2ec0c541c69d8e30a0e25f50ce9f6e4a432e42c5d7 + md5: 576d629e47797577ab0f1b351297ef4a + depends: + - python >=3.6 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/cached-property?source=hash-mapping + size: 11065 + timestamp: 1615209567874 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-he90730b_1.conda + sha256: 06525fa0c4e4f56e771a3b986d0fdf0f0fc5a3270830ee47e127a5105bde1b9a + md5: bb6c4808bfa69d6f7f6b07e5846ced37 + depends: + - __glibc >=2.17,<3.0.a0 + - fontconfig >=2.15.0,<3.0a0 + - fonts-conda-ecosystem + - icu >=78.1,<79.0a0 + - libexpat >=2.7.3,<3.0a0 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - libgcc >=14 + - libglib >=2.86.3,<3.0a0 + - libpng >=1.6.53,<1.7.0a0 + - libstdcxx >=14 + - libxcb >=1.17.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - pixman >=0.46.4,<1.0a0 + - xorg-libice >=1.1.2,<2.0a0 + - xorg-libsm >=1.2.6,<2.0a0 + - xorg-libx11 >=1.8.12,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxrender >=0.9.12,<0.10.0a0 + license: LGPL-2.1-only or MPL-1.1 + purls: [] + size: 989514 + timestamp: 1766415934926 +- conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py312h0a2e395_4.conda + sha256: 62447faf7e8eb691e407688c0b4b7c230de40d5ecf95bf301111b4d05c5be473 + md5: 43c2bc96af3ae5ed9e8a10ded942aa50 + depends: + - numpy >=1.25 + - python + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/contourpy?source=compressed-mapping + size: 320386 + timestamp: 1769155979897 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py312h3093aea_4.conda + sha256: fa1b3967c644c1ffaf8beba3d7aee2301a8db32c0e9a56649a0e496cf3abd27c + md5: f9cce0bc86b46533489a994a47d3c7d2 + depends: + - numpy >=1.25 + - python + - python 3.12.* *_cpython + - __osx >=11.0 + - libcxx >=19 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/contourpy?source=compressed-mapping + size: 286084 + timestamp: 1769156157865 +- pypi: https://files.pythonhosted.org/packages/5d/96/5238b1efc5922ddbdc9b0db9243152c09777804fb7c02ad1741eb18a11c0/coverage-7.13.4-cp312-cp312-macosx_11_0_arm64.whl + name: coverage + version: 7.13.4 + sha256: 40aa8808140e55dc022b15d8aa7f651b6b3d68b365ea0398f1441e0b04d859c3 + requires_dist: + - tomli ; python_full_version <= '3.11' and extra == 'toml' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/5d/a0/2ea570925524ef4e00bb6c82649f5682a77fac5ab910a65c9284de422600/coverage-7.13.4-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl + name: coverage + version: 7.13.4 + sha256: 2c048ea43875fbf8b45d476ad79f179809c590ec7b79e2035c662e7afa3192e3 + requires_dist: + - tomli ; python_full_version <= '3.11' and extra == 'toml' + requires_python: '>=3.10' +- conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.12.13-py312hd8ed1ab_0.conda + noarch: generic + sha256: d3e9bbd7340199527f28bbacf947702368f31de60c433a16446767d3c6aaf6fe + md5: f54c1ffb8ecedb85a8b7fcde3a187212 + depends: + - python >=3.12,<3.13.0a0 + - python_abi * *_cp312 + license: Python-2.0 + purls: [] + size: 46463 + timestamp: 1772728929620 +- conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda + sha256: bb47aec5338695ff8efbddbc669064a3b10fe34ad881fb8ad5d64fbfa6910ed1 + md5: 4c2a8fef270f6c69591889b93f9f55c1 + depends: + - python >=3.10 + - python + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/cycler?source=compressed-mapping + size: 14778 + timestamp: 1764466758386 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hac629b4_1.conda + sha256: 7684da83306bb69686c0506fb09aa7074e1a55ade50c3a879e4e5df6eebb1009 + md5: af491aae930edc096b58466c51c4126c + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.22.2,<1.23.0a0 + - libgcc >=13 + - libntlm >=1.8,<2.0a0 + - libstdcxx >=13 + - libxcrypt >=4.4.36 + - openssl >=3.5.5,<4.0a0 + license: BSD-3-Clause-Attribution + license_family: BSD + purls: [] + size: 210103 + timestamp: 1771943128249 +- conda: https://conda.anaconda.org/conda-forge/linux-64/dav1d-1.2.1-hd590300_0.conda + sha256: 22053a5842ca8ee1cf8e1a817138cdb5e647eb2c46979f84153f6ad7bde73020 + md5: 418c6ca5929a611cbd69204907a83995 + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 760229 + timestamp: 1685695754230 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/dav1d-1.2.1-hb547adb_0.conda + sha256: 93e077b880a85baec8227e8c72199220c7f87849ad32d02c14fb3807368260b8 + md5: 5a74cdee497e6b65173e10d94582fae6 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 316394 + timestamp: 1685695959391 +- conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h24cb091_1.conda + sha256: 8bb557af1b2b7983cf56292336a1a1853f26555d9c6cecf1e5b2b96838c9da87 + md5: ce96f2f470d39bd96ce03945af92e280 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - libglib >=2.86.2,<3.0a0 + - libexpat >=2.7.3,<3.0a0 + license: AFL-2.1 OR GPL-2.0-or-later + purls: [] + size: 447649 + timestamp: 1764536047944 +- conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.4.0-hecca717_0.conda + sha256: 40cdd1b048444d3235069d75f9c8e1f286db567f6278a93b4f024e5642cfaecc + md5: dbe3ec0f120af456b3477743ffd99b74 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 71809 + timestamp: 1765193127016 +- conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.25.0-pyhd8ed1ab_0.conda + sha256: 55162ec0ff4e22d8f762b3e774f08f79f234ba37ab5e64d7a1421ed9216a222c + md5: 49a92015e912176999ae81bea11ea778 + depends: + - python >=3.10 + license: Unlicense + purls: + - pkg:pypi/filelock?source=compressed-mapping + size: 25656 + timestamp: 1772380968183 +- conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-12.1.0-hff5e90c_0.conda + sha256: d4e92ba7a7b4965341dc0fca57ec72d01d111b53c12d11396473115585a9ead6 + md5: f7d7a4104082b39e3b3473fbd4a38229 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + purls: [] + size: 198107 + timestamp: 1767681153946 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fmt-12.1.0-h403dcb5_0.conda + sha256: dba5d4a93dc62f20e4c2de813ccf7beefed1fb54313faff9c4f2383e4744c8e5 + md5: ae2f556fbb43e5a75cc80a47ac942a8e + depends: + - __osx >=11.0 + - libcxx >=19 + license: MIT + license_family: MIT + purls: [] + size: 180970 + timestamp: 1767681372955 +- conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 + sha256: 58d7f40d2940dd0a8aa28651239adbf5613254df0f75789919c4e6762054403b + md5: 0c96522c6bdaed4b1566d11387caaf45 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 397370 + timestamp: 1566932522327 +- conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 + sha256: c52a29fdac682c20d252facc50f01e7c2e7ceac52aa9817aaf0bb83f7559ec5c + md5: 34893075a5c9e55cdafac56607368fc6 + license: OFL-1.1 + license_family: Other + purls: [] + size: 96530 + timestamp: 1620479909603 +- conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 + sha256: 00925c8c055a2275614b4d983e1df637245e19058d79fc7dd1a93b8d9fb4b139 + md5: 4d59c254e01d9cde7957100457e2d5fb + license: OFL-1.1 + license_family: Other + purls: [] + size: 700814 + timestamp: 1620479612257 +- conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda + sha256: 2821ec1dc454bd8b9a31d0ed22a7ce22422c0aef163c59f49dfdf915d0f0ca14 + md5: 49023d73832ef61042f6a237cb2687e7 + license: LicenseRef-Ubuntu-Font-Licence-Version-1.0 + license_family: Other + purls: [] + size: 1620504 + timestamp: 1727511233259 +- conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.17.1-h27c8c51_0.conda + sha256: aa4a44dba97151221100a637c7f4bde619567afade9c0265f8e1c8eed8d7bd8c + md5: 867127763fbe935bab59815b6e0b7b5c + depends: + - __glibc >=2.17,<3.0.a0 + - libexpat >=2.7.4,<3.0a0 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - libgcc >=14 + - libuuid >=2.41.3,<3.0a0 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 270705 + timestamp: 1771382710863 +- conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 + sha256: a997f2f1921bb9c9d76e6fa2f6b408b7fa549edd349a77639c9fe7a23ea93e61 + md5: fee5683a3f04bd15cbd8318b096a27ab + depends: + - fonts-conda-forge + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 3667 + timestamp: 1566974674465 +- conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda + sha256: 54eea8469786bc2291cc40bca5f46438d3e062a399e8f53f013b6a9f50e98333 + md5: a7970cd949a077b7cb9696379d338681 + depends: + - font-ttf-ubuntu + - font-ttf-inconsolata + - font-ttf-dejavu-sans-mono + - font-ttf-source-code-pro + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 4059 + timestamp: 1762351264405 +- conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.61.1-py312h8a5da7c_0.conda + sha256: c73cd238e0f6b2183c5168b64aa35a7eb66bb145192a9b26bb9041a4152844a3 + md5: 3bf8fb959dc598c67dac0430b4aff57a + depends: + - __glibc >=2.17,<3.0.a0 + - brotli + - libgcc >=14 + - munkres + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - unicodedata2 >=15.1.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/fonttools?source=hash-mapping + size: 2932702 + timestamp: 1765632761555 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fonttools-4.61.1-py312h5748b74_0.conda + sha256: d87752e84621f90e9350262200fef55f054472f7779323f51717b557208e2a16 + md5: c14625bf00c41c00cea174f459287fc4 + depends: + - __osx >=11.0 + - brotli + - munkres + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - unicodedata2 >=15.1.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/fonttools?source=hash-mapping + size: 2859891 + timestamp: 1765633073562 +- conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.2-ha770c72_0.conda + sha256: 36857701b46828b6760c3c1652414ee504e7fc12740261ac6fcff3959b72bd7a + md5: eeec961fec28e747e1e1dc0446277452 + depends: + - libfreetype 2.14.2 ha770c72_0 + - libfreetype6 2.14.2 h73754d4_0 + license: GPL-2.0-only OR FTL + purls: [] + size: 174292 + timestamp: 1772757205296 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.2-hce30654_0.conda + sha256: 3c02ecdbfd94d25721811f51d0f400bf705005a728011e19db9975a8985e1021 + md5: ca730d8e7d1de1f71013edfef0e08f13 + depends: + - libfreetype 2.14.2 hce30654_0 + - libfreetype6 2.14.2 hdfa99f5_0 + license: GPL-2.0-only OR FTL + purls: [] + size: 173786 + timestamp: 1772756361577 +- conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.2.0-pyhd8ed1ab_0.conda + sha256: 239b67edf1c5e5caed52cf36e9bed47cb21b37721779828c130e6b3fd9793c1b + md5: 496c6c9411a6284addf55c898d6ed8d7 + depends: + - python >=3.10 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/fsspec?source=compressed-mapping + size: 148757 + timestamp: 1770387898414 +- conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-hd590300_0.conda + sha256: aac402a8298f0c0cc528664249170372ef6b37ac39fdc92b40601a6aed1e32ff + md5: 3bf7b9fd5a7136126e0234db4b87c8b6 + depends: + - libgcc-ng >=12 + license: MIT + license_family: MIT + purls: [] + size: 77248 + timestamp: 1712692454246 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-5.2.2-h93a5062_0.conda + sha256: 843b3f364ff844137e37d5c0a181f11f6d51adcedd216f019d074e5aa5d7e09c + md5: 95fa1486c77505330c20f7202492b913 + license: MIT + license_family: MIT + purls: [] + size: 71613 + timestamp: 1712692611426 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gmp-6.3.0-hac33072_2.conda + sha256: 309cf4f04fec0c31b6771a5809a1909b4b3154a2208f52351e1ada006f4c750c + md5: c94a5994ef49749880a8139cf9afcbe1 + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: GPL-2.0-or-later OR LGPL-3.0-or-later + purls: [] + size: 460055 + timestamp: 1718980856608 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/gmp-6.3.0-h7bae524_2.conda + sha256: 76e222e072d61c840f64a44e0580c2503562b009090f55aa45053bf1ccb385dd + md5: eed7278dfbab727b56f2c0b64330814b + depends: + - __osx >=11.0 + - libcxx >=16 + license: GPL-2.0-or-later OR LGPL-3.0-or-later + purls: [] + size: 365188 + timestamp: 1718981343258 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gmpy2-2.2.1-py312hcaba1f9_2.conda + sha256: c9dfe9be6716d992b4ddd2e8219ad8afbe33dc04f69748ac4302954ba2e29e84 + md5: dd6f5de6249439ab97a6e9e873741294 + depends: + - __glibc >=2.17,<3.0.a0 + - gmp >=6.3.0,<7.0a0 + - libgcc >=14 + - mpc >=1.3.1,<2.0a0 + - mpfr >=4.2.1,<5.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: LGPL-3.0-or-later + license_family: LGPL + purls: + - pkg:pypi/gmpy2?source=hash-mapping + size: 214554 + timestamp: 1762946924209 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/gmpy2-2.2.1-py312hee6aa52_2.conda + sha256: e2f72ddb929fcd161d68729891f25241d62ab1a9d4e37d0284f2b2fce88935fa + md5: bed6eebc8d1690f205a781c993f9bc65 + depends: + - __osx >=11.0 + - gmp >=6.3.0,<7.0a0 + - mpc >=1.3.1,<2.0a0 + - mpfr >=4.2.1,<5.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: LGPL-3.0-or-later + license_family: LGPL + purls: + - pkg:pypi/gmpy2?source=hash-mapping + size: 161203 + timestamp: 1762947199346 +- conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.14-hecca717_2.conda + sha256: 25ba37da5c39697a77fce2c9a15e48cf0a84f1464ad2aafbe53d8357a9f6cc8c + md5: 2cd94587f3a401ae05e03a6caf09539d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: LGPL-2.0-or-later + license_family: LGPL + purls: [] + size: 99596 + timestamp: 1755102025473 +- conda: https://conda.anaconda.org/conda-forge/linux-64/h5py-3.15.1-nompi_py312ha4f8f14_101.conda + sha256: bb5cefbe5b54195a54f749189fc6797568d52e8790b2f542143c681b98a92b71 + md5: 23965cb240cb534649dfe2327ecec4fa + depends: + - __glibc >=2.17,<3.0.a0 + - cached-property + - hdf5 >=1.14.6,<1.14.7.0a0 + - libgcc >=14 + - numpy >=1.23,<3 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/h5py?source=hash-mapping + size: 1290741 + timestamp: 1764016665782 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/h5py-3.15.1-nompi_py312h4eecd6b_101.conda + sha256: 914d4f00a4d8cb86a70ce60241acc631a0e9d0cd939c0091b06de2d6cef51a3b + md5: 1f19a033f9c3f388c8f3d3c1643d6611 + depends: + - __osx >=11.0 + - cached-property + - hdf5 >=1.14.6,<1.14.7.0a0 + - numpy >=1.23,<3 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/h5py?source=hash-mapping + size: 1139768 + timestamp: 1764017732485 +- conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-12.3.2-h6083320_0.conda + sha256: 92015faf283f9c0a8109e2761042cd47ae7a4505e24af42a53bc3767cb249912 + md5: d170a70fc1d5c605fcebdf16851bd54a + depends: + - __glibc >=2.17,<3.0.a0 + - cairo >=1.18.4,<2.0a0 + - graphite2 >=1.3.14,<2.0a0 + - icu >=78.2,<79.0a0 + - libexpat >=2.7.3,<3.0a0 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - libgcc >=14 + - libglib >=2.86.3,<3.0a0 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 2035859 + timestamp: 1769445400168 +- conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.6-nompi_h19486de_106.conda + sha256: 1fc50ce3b86710fba3ec9c5714f1612b5ffa4230d70bfe43e2a1436eacba1621 + md5: c223ee1429ba538f3e48cfb4a0b97357 + depends: + - __glibc >=2.17,<3.0.a0 + - libaec >=1.1.5,<2.0a0 + - libcurl >=8.18.0,<9.0a0 + - libgcc >=14 + - libgfortran + - libgfortran5 >=14.3.0 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.5,<4.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 3708864 + timestamp: 1770390337946 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/hdf5-1.14.6-nompi_had3affe_106.conda + sha256: e91c2b8fe62d73bb56bdb9b5adcdcbedbd164ced288e0f361b8eb3f017ddcd7b + md5: 2d1270d283403c542680e969bea70355 + depends: + - __osx >=11.0 + - libaec >=1.1.5,<2.0a0 + - libcurl >=8.18.0,<9.0a0 + - libcxx >=19 + - libgfortran + - libgfortran5 >=14.3.0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.5,<4.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 3299759 + timestamp: 1770390513189 +- conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.2-h33c6efd_0.conda + sha256: 142a722072fa96cf16ff98eaaf641f54ab84744af81754c292cb81e0881c0329 + md5: 186a18e3ba246eccfc7cff00cd19a870 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + purls: [] + size: 12728445 + timestamp: 1767969922681 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.2-hef89b57_0.conda + sha256: 24bc62335106c30fecbcc1dba62c5eba06d18b90ea1061abd111af7b9c89c2d7 + md5: 114e6bfe7c5ad2525eb3597acdbf2300 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 12389400 + timestamp: 1772209104304 +- pypi: https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl + name: iniconfig + version: 2.3.0 + sha256: f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12 + requires_python: '>=3.10' +- conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda + sha256: fc9ca7348a4f25fed2079f2153ecdcf5f9cf2a0bc36c4172420ca09e1849df7b + md5: 04558c96691bed63104678757beb4f8d + depends: + - markupsafe >=2.0 + - python >=3.10 + - python + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/jinja2?source=compressed-mapping + size: 120685 + timestamp: 1764517220861 +- conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda + sha256: 301539229d7be6420c084490b8145583291123f0ce6b92f56be5948a2c83a379 + md5: 615de2a4d97af50c350e5cf160149e77 + depends: + - python >=3.10 + - setuptools + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/joblib?source=hash-mapping + size: 226448 + timestamp: 1765794135253 +- conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda + sha256: 0960d06048a7185d3542d850986d807c6e37ca2e644342dd0c72feefcf26c2a4 + md5: b38117a3c920364aff79f870c984b4a3 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: LGPL-2.1-or-later + purls: [] + size: 134088 + timestamp: 1754905959823 +- conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.9-py312h0a2e395_2.conda + sha256: 170d76b7ac7197012bb048e1021482a7b2455f3592a5e8d97c96f285ebad064b + md5: 3a3004fddd39e3bb1a631b08d7045156 + depends: + - python + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/kiwisolver?source=hash-mapping + size: 77682 + timestamp: 1762488738724 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.4.9-py312hd8c8125_2.conda + sha256: 8d68f6ec4d947902034fe9ed9d4a4c1180b5767bd9731af940f5a0e436bc3dfd + md5: ddf4775023a2466ee308792ed80ca408 + depends: + - python + - python 3.12.* *_cpython + - libcxx >=19 + - __osx >=11.0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/kiwisolver?source=hash-mapping + size: 67752 + timestamp: 1762488827477 +- conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-ha1258a1_0.conda + sha256: 3e307628ca3527448dd1cb14ad7bb9d04d1d28c7d4c5f97ba196ae984571dd25 + md5: fb53fb07ce46a575c5d004bbc96032c2 + depends: + - __glibc >=2.17,<3.0.a0 + - keyutils >=1.6.3,<2.0a0 + - libedit >=3.1.20250104,<3.2.0a0 + - libedit >=3.1.20250104,<4.0a0 + - libgcc >=14 + - libstdcxx >=14 + - openssl >=3.5.5,<4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 1386730 + timestamp: 1769769569681 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-h385eeb1_0.conda + sha256: c0a0bf028fe7f3defcdcaa464e536cf1b202d07451e18ad83fdd169d15bef6ed + md5: e446e1822f4da8e5080a9de93474184d + depends: + - __osx >=11.0 + - libcxx >=19 + - libedit >=3.1.20250104,<3.2.0a0 + - libedit >=3.1.20250104,<4.0a0 + - openssl >=3.5.5,<4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 1160828 + timestamp: 1769770119811 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.18-h0c24ade_0.conda + sha256: 836ec4b895352110335b9fdcfa83a8dcdbe6c5fb7c06c4929130600caea91c0a + md5: 6f2e2c8f58160147c4d1c6f4c14cbac4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libjpeg-turbo >=3.1.2,<4.0a0 + - libtiff >=4.7.1,<4.8.0a0 + license: MIT + license_family: MIT + purls: [] + size: 249959 + timestamp: 1768184673131 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.18-hdfa7624_0.conda + sha256: d768da024ab74a4b30642401877fa914a68bdc238667f16b1ec2e0e98b2451a6 + md5: 6631a7bd2335bb9699b1dbc234b19784 + depends: + - __osx >=11.0 + - libjpeg-turbo >=3.1.2,<4.0a0 + - libtiff >=4.7.1,<4.8.0a0 + license: MIT + license_family: MIT + purls: [] + size: 211756 + timestamp: 1768184994800 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_101.conda + sha256: 565941ac1f8b0d2f2e8f02827cbca648f4d18cd461afc31f15604cd291b5c5f3 + md5: 12bd9a3f089ee6c9266a37dab82afabd + depends: + - __glibc >=2.17,<3.0.a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - binutils_impl_linux-64 2.45.1 + license: GPL-3.0-only + license_family: GPL + purls: [] + size: 725507 + timestamp: 1770267139900 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h0aef613_1.conda + sha256: 412381a43d5ff9bbed82cd52a0bbca5b90623f62e41007c9c42d3870c60945ff + md5: 9344155d33912347b37f0ae6c410a835 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 264243 + timestamp: 1745264221534 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.0.0-hd64df32_1.conda + sha256: 12361697f8ffc9968907d1a7b5830e34c670e4a59b638117a2cdfed8f63a38f8 + md5: a74332d9b60b62905e3d30709df08bf1 + depends: + - __osx >=11.0 + - libcxx >=18 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 188306 + timestamp: 1745264362794 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260107.1-cxx17_h7b12aa8_0.conda + sha256: a7a4481a4d217a3eadea0ec489826a69070fcc3153f00443aa491ed21527d239 + md5: 6f7b4302263347698fd24565fbf11310 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + constrains: + - libabseil-static =20260107.1=cxx17* + - abseil-cpp =20260107.1 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 1384817 + timestamp: 1770863194876 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260107.1-cxx17_h2062a1b_0.conda + sha256: 756611fbb8d2957a5b4635d9772bd8432cb6ddac05580a6284cca6fdc9b07fca + md5: bb65152e0d7c7178c0f1ee25692c9fd1 + depends: + - __osx >=11.0 + - libcxx >=19 + constrains: + - abseil-cpp =20260107.1 + - libabseil-static =20260107.1=cxx17* + license: Apache-2.0 + license_family: Apache + purls: [] + size: 1229639 + timestamp: 1770863511331 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.5-h088129d_0.conda + sha256: 822e4ae421a7e9c04e841323526321185f6659222325e1a9aedec811c686e688 + md5: 86f7414544ae606282352fa1e116b41f + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 36544 + timestamp: 1769221884824 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libaec-1.1.5-h8664d51_0.conda + sha256: af9cd8db11eb719e38a3340c88bb4882cf19b5b4237d93845224489fc2a13b46 + md5: 13e6d9ae0efbc9d2e9a01a91f4372b41 + depends: + - __osx >=11.0 + - libcxx >=19 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 30390 + timestamp: 1769222133373 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libavif16-1.4.0-hcfa2d63_0.conda + sha256: 918fd09af66968361c8fa40a76f864b7febb8286dd5dcb1419517b9db950c84c + md5: e226d3dbe1e2482fd8e15cb924fd1e7c + depends: + - __glibc >=2.17,<3.0.a0 + - aom >=3.9.1,<3.10.0a0 + - dav1d >=1.2.1,<1.2.2.0a0 + - libgcc >=14 + - rav1e >=0.8.1,<0.9.0a0 + - svt-av1 >=4.0.1,<4.0.2.0a0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 148589 + timestamp: 1772682433596 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libavif16-1.4.0-hfce71f6_0.conda + sha256: fec27b037c7931f913f57f20796faafa5bd10bd46521b811f95ddb2fab0f896b + md5: 7c843d9c17399718f50f47f771a2e568 + depends: + - __osx >=11.0 + - aom >=3.9.1,<3.10.0a0 + - dav1d >=1.2.1,<1.2.2.0a0 + - rav1e >=0.8.1,<0.9.0a0 + - svt-av1 >=4.0.1,<4.0.2.0a0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 119125 + timestamp: 1772682918981 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h5875eb1_mkl.conda + build_number: 5 + sha256: 328d64d4eb51047c39a8039a30eb47695855829d0a11b72d932171cb1dcdfad3 + md5: 9d2f2e3a943d38f972ceef9cde8ba4bf + depends: + - mkl >=2025.3.0,<2026.0a0 + constrains: + - liblapack 3.11.0 5*_mkl + - liblapacke 3.11.0 5*_mkl + - libcblas 3.11.0 5*_mkl + - blas 2.305 mkl + track_features: + - blas_mkl + - blas_mkl_2 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 18744 + timestamp: 1765818556597 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-5_h51639a9_openblas.conda + build_number: 5 + sha256: 620a6278f194dcabc7962277da6835b1e968e46ad0c8e757736255f5ddbfca8d + md5: bcc025e2bbaf8a92982d20863fe1fb69 + depends: + - libopenblas >=0.3.30,<0.3.31.0a0 + - libopenblas >=0.3.30,<1.0a0 + constrains: + - libcblas 3.11.0 5*_openblas + - liblapack 3.11.0 5*_openblas + - liblapacke 3.11.0 5*_openblas + - blas 2.305 openblas + - mkl <2026 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 18546 + timestamp: 1765819094137 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda + sha256: 318f36bd49ca8ad85e6478bd8506c88d82454cc008c1ac1c6bf00a3c42fa610e + md5: 72c8fd1af66bd67bf580645b426513ed + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 79965 + timestamp: 1764017188531 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-hc919400_1.conda + sha256: a7cb9e660531cf6fbd4148cff608c85738d0b76f0975c5fc3e7d5e92840b7229 + md5: 006e7ddd8a110771134fcc4e1e3a6ffa + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 79443 + timestamp: 1764017945924 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hb03c661_1.conda + sha256: 12fff21d38f98bc446d82baa890e01fd82e3b750378fedc720ff93522ffb752b + md5: 366b40a69f0ad6072561c1d09301c886 + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlicommon 1.2.0 hb03c661_1 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 34632 + timestamp: 1764017199083 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-hc919400_1.conda + sha256: 2eae444039826db0454b19b52a3390f63bfe24f6b3e63089778dd5a5bf48b6bf + md5: 079e88933963f3f149054eec2c487bc2 + depends: + - __osx >=11.0 + - libbrotlicommon 1.2.0 hc919400_1 + license: MIT + license_family: MIT + purls: [] + size: 29452 + timestamp: 1764017979099 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-hb03c661_1.conda + sha256: a0c15c79997820bbd3fbc8ecf146f4fe0eca36cc60b62b63ac6cf78857f1dd0d + md5: 4ffbb341c8b616aa2494b6afb26a0c5f + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlicommon 1.2.0 hb03c661_1 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 298378 + timestamp: 1764017210931 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-hc919400_1.conda + sha256: 01436c32bb41f9cb4bcf07dda647ce4e5deb8307abfc3abdc8da5317db8189d1 + md5: b2b7c8288ca1a2d71ff97a8e6a1e8883 + depends: + - __osx >=11.0 + - libbrotlicommon 1.2.0 hc919400_1 + license: MIT + license_family: MIT + purls: [] + size: 290754 + timestamp: 1764018009077 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-5_hfef963f_mkl.conda + build_number: 5 + sha256: 8352f472c49c42a83a20387b5f6addab1f910c5a62f4f5b8998d7dc89131ba2e + md5: 9b6cb3aa4b7912121c64b97a76ca43d5 + depends: + - libblas 3.11.0 5_h5875eb1_mkl + constrains: + - liblapack 3.11.0 5*_mkl + - liblapacke 3.11.0 5*_mkl + - blas 2.305 mkl + track_features: + - blas_mkl + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 18385 + timestamp: 1765818571086 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-5_hb0561ab_openblas.conda + build_number: 5 + sha256: 38809c361bbd165ecf83f7f05fae9b791e1baa11e4447367f38ae1327f402fc0 + md5: efd8bd15ca56e9d01748a3beab8404eb + depends: + - libblas 3.11.0 5_h51639a9_openblas + constrains: + - liblapacke 3.11.0 5*_openblas + - liblapack 3.11.0 5*_openblas + - blas 2.305 openblas + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 18548 + timestamp: 1765819108956 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp22.1-22.1.0-default_h99862b1_0.conda + sha256: 914da94dbf829192b2bb360a7684b32e46f047a57de96a2f5ab39a011aeae6ea + md5: d966a23335e090a5410cc4f0dec8d00a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libllvm22 >=22.1.0,<22.2.0a0 + - libstdcxx >=14 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 21661249 + timestamp: 1772101075353 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-22.1.0-default_h746c552_0.conda + sha256: 4a9dd814492a129f2ff40cd4ab0b942232c9e3c6dbc0d0aaf861f1f65e99cc7d + md5: 140459a7413d8f6884eb68205ce39a0d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libllvm22 >=22.1.0,<22.2.0a0 + - libstdcxx >=14 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 12817500 + timestamp: 1772101411287 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h7a8fb5f_6.conda + sha256: 205c4f19550f3647832ec44e35e6d93c8c206782bdd620c1d7cf66237580ff9c + md5: 49c553b47ff679a6a1e9fc80b9c5a2d4 + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.22.2,<1.23.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 4518030 + timestamp: 1770902209173 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.18.0-hcf29cc6_1.conda + sha256: c84e8dccb65ad5149c0121e4b54bdc47fa39303fd5f4979b8c44bb51b39a369b + md5: 1707cdd636af2ff697b53186572c9f77 + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.22.2,<1.23.0a0 + - libgcc >=14 + - libnghttp2 >=1.67.0,<2.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.5,<4.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: curl + license_family: MIT + purls: [] + size: 463621 + timestamp: 1770892808818 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.18.0-hd5a2499_1.conda + sha256: dbc34552fc6f040bbcd52b4246ec068ce8d82be0e76bfe45c6984097758d37c2 + md5: 2742a933ef07e91f38e3d33ad6fe937c + depends: + - __osx >=11.0 + - krb5 >=1.22.2,<1.23.0a0 + - libnghttp2 >=1.67.0,<2.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.5,<4.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: curl + license_family: MIT + purls: [] + size: 402616 + timestamp: 1770893178846 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.0-h55c6f16_1.conda + sha256: ce1049fa6fda9cf08ff1c50fb39573b5b0ea6958375d8ea7ccd8456ab81a0bcb + md5: e9c56daea841013e7774b5cd46f41564 + depends: + - __osx >=11.0 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 568910 + timestamp: 1772001095642 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libde265-1.0.15-h00ab1b0_0.conda + sha256: 7cf7e294e1a7c8219065885e186d8f52002fb900bf384d815f159b5874204e3d + md5: 407fee7a5d7ab2dca12c9ca7f62310ad + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: LGPL-3.0-or-later + license_family: LGPL + purls: [] + size: 411814 + timestamp: 1703088639063 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libde265-1.0.15-h2ffa867_0.conda + sha256: 13747fa634f7f16d7f222b7d3869e3c1aab9d3a2791edeb2fc632a87663950e0 + md5: 7c718ee6d8497702145612fa0898a12d + depends: + - libcxx >=15 + license: LGPL-3.0-or-later + license_family: LGPL + purls: [] + size: 277861 + timestamp: 1703089176970 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda + sha256: aa8e8c4be9a2e81610ddf574e05b64ee131fab5e0e3693210c9d6d2fba32c680 + md5: 6c77a605a7a689d17d4819c0f8ac9a00 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 73490 + timestamp: 1761979956660 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-hc11a715_0.conda + sha256: 5e0b6961be3304a5f027a8c00bd0967fc46ae162cffb7553ff45c70f51b8314c + md5: a6130c709305cd9828b4e1bd9ba0000c + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 55420 + timestamp: 1761980066242 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.125-hb03c661_1.conda + sha256: c076a213bd3676cc1ef22eeff91588826273513ccc6040d9bea68bccdc849501 + md5: 9314bc5a1fe7d1044dc9dfd3ef400535 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libpciaccess >=0.18,<0.19.0a0 + license: MIT + license_family: MIT + purls: [] + size: 310785 + timestamp: 1757212153962 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda + sha256: d789471216e7aba3c184cd054ed61ce3f6dac6f87a50ec69291b9297f8c18724 + md5: c277e0a4d549b03ac1e9d6cbbe3d017b + depends: + - ncurses + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - ncurses >=6.5,<7.0a0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 134676 + timestamp: 1738479519902 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321hafb1f1b_0.conda + sha256: 66aa216a403de0bb0c1340a88d1a06adaff66bae2cfd196731aa24db9859d631 + md5: 44083d2d2c2025afca315c7a172eab2b + depends: + - ncurses + - __osx >=11.0 + - ncurses >=6.5,<7.0a0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 107691 + timestamp: 1738479560845 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_2.conda + sha256: 7fd5408d359d05a969133e47af580183fbf38e2235b562193d427bb9dad79723 + md5: c151d5eb730e9b7480e6d48c0fc44048 + depends: + - __glibc >=2.17,<3.0.a0 + - libglvnd 1.7.0 ha4b6fd6_2 + license: LicenseRef-libglvnd + purls: [] + size: 44840 + timestamp: 1731330973553 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda + sha256: 1cd6048169fa0395af74ed5d8f1716e22c19a81a8a36f934c110ca3ad4dd27b4 + md5: 172bf1cd1ff8629f2b1179945ed45055 + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 112766 + timestamp: 1702146165126 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda + sha256: 95cecb3902fbe0399c3a7e67a5bed1db813e5ab0e22f4023a5e0f722f2cc214f + md5: 36d33e440c31857372a72137f78bacf5 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 107458 + timestamp: 1702146414478 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.4-hecca717_0.conda + sha256: d78f1d3bea8c031d2f032b760f36676d87929b18146351c4464c66b0869df3f5 + md5: e7f7ce06ec24cfcfb9e36d28cf82ba57 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - expat 2.7.4.* + license: MIT + license_family: MIT + purls: [] + size: 76798 + timestamp: 1771259418166 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.7.4-hf6b4638_0.conda + sha256: 03887d8080d6a8fe02d75b80929271b39697ecca7628f0657d7afaea87761edf + md5: a92e310ae8dfc206ff449f362fc4217f + depends: + - __osx >=11.0 + constrains: + - expat 2.7.4.* + license: MIT + license_family: MIT + purls: [] + size: 68199 + timestamp: 1771260020767 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda + sha256: 31f19b6a88ce40ebc0d5a992c131f57d919f73c0b92cd1617a5bec83f6e961e6 + md5: a360c33a5abe61c07959e449fa1453eb + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 58592 + timestamp: 1769456073053 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda + sha256: 6686a26466a527585e6a75cc2a242bf4a3d97d6d6c86424a441677917f28bec7 + md5: 43c04d9cb46ef176bb2a4c77e324d599 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 40979 + timestamp: 1769456747661 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.2-ha770c72_0.conda + sha256: 2e1bfe1e856eb707d258f669ef6851af583ceaffab5e64821b503b0f7cd09e9e + md5: 26c746d14402a3b6c684d045b23b9437 + depends: + - libfreetype6 >=2.14.2 + license: GPL-2.0-only OR FTL + purls: [] + size: 8035 + timestamp: 1772757210108 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.2-hce30654_0.conda + sha256: 6061ef5321b8e697d5577d8dfe7a4c75bfe3e706c956d0d84bfec6bea3ed9f77 + md5: a3a53232936b55ffea76806aefe19e8b + depends: + - libfreetype6 >=2.14.2 + license: GPL-2.0-only OR FTL + purls: [] + size: 8076 + timestamp: 1772756349852 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.2-h73754d4_0.conda + sha256: aba65b94bdbed52de17ec3d0c6f2ebac2ef77071ad22d6900d1614d0dd702a0c + md5: 8eaba3d1a4d7525c6814e861614457fd + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libpng >=1.6.55,<1.7.0a0 + - libzlib >=1.3.1,<2.0a0 + constrains: + - freetype >=2.14.2 + license: GPL-2.0-only OR FTL + purls: [] + size: 386316 + timestamp: 1772757193822 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.2-hdfa99f5_0.conda + sha256: 24dd0e0bee56e87935f885929f67659f1d3b8a01e7546568de2919cffd9e2e36 + md5: e726e134a392ae5d7bafa6cc4a3d5725 + depends: + - __osx >=11.0 + - libpng >=1.6.55,<1.7.0a0 + - libzlib >=1.3.1,<2.0a0 + constrains: + - freetype >=2.14.2 + license: GPL-2.0-only OR FTL + purls: [] + size: 338032 + timestamp: 1772756347899 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_18.conda + sha256: faf7d2017b4d718951e3a59d081eb09759152f93038479b768e3d612688f83f5 + md5: 0aa00f03f9e39fb9876085dee11a85d4 + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex >=4.5 + constrains: + - libgcc-ng ==15.2.0=*_18 + - libgomp 15.2.0 he0feb66_18 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 1041788 + timestamp: 1771378212382 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_18.conda + sha256: 1d9c4f35586adb71bcd23e31b68b7f3e4c4ab89914c26bed5f2859290be5560e + md5: 92df6107310b1fff92c4cc84f0de247b + depends: + - _openmp_mutex + constrains: + - libgcc-ng ==15.2.0=*_18 + - libgomp 15.2.0 18 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 401974 + timestamp: 1771378877463 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_18.conda + sha256: e318a711400f536c81123e753d4c797a821021fb38970cebfb3f454126016893 + md5: d5e96b1ed75ca01906b3d2469b4ce493 + depends: + - libgcc 15.2.0 he0feb66_18 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 27526 + timestamp: 1771378224552 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_18.conda + sha256: d2c9fad338fd85e4487424865da8e74006ab2e2475bd788f624d7a39b2a72aee + md5: 9063115da5bc35fdc3e1002e69b9ef6e + depends: + - libgfortran5 15.2.0 h68bc16d_18 + constrains: + - libgfortran-ng ==15.2.0=*_18 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 27523 + timestamp: 1771378269450 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_18.conda + sha256: 63f89087c3f0c8621c5c89ecceec1e56e5e1c84f65fc9c5feca33a07c570a836 + md5: 26981599908ed2205366e8fc91b37fc6 + depends: + - libgfortran5 15.2.0 hdae7583_18 + constrains: + - libgfortran-ng ==15.2.0=*_18 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 138973 + timestamp: 1771379054939 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_18.conda + sha256: 539b57cf50ec85509a94ba9949b7e30717839e4d694bc94f30d41c9d34de2d12 + md5: 646855f357199a12f02a87382d429b75 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15.2.0 + constrains: + - libgfortran 15.2.0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 2482475 + timestamp: 1771378241063 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_18.conda + sha256: 91033978ba25e6a60fb86843cf7e1f7dc8ad513f9689f991c9ddabfaf0361e7e + md5: c4a6f7989cffb0544bfd9207b6789971 + depends: + - libgcc >=15.2.0 + constrains: + - libgfortran 15.2.0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 598634 + timestamp: 1771378886363 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_2.conda + sha256: dc2752241fa3d9e40ce552c1942d0a4b5eeb93740c9723873f6fcf8d39ef8d2d + md5: 928b8be80851f5d8ffb016f9c81dae7a + depends: + - __glibc >=2.17,<3.0.a0 + - libglvnd 1.7.0 ha4b6fd6_2 + - libglx 1.7.0 ha4b6fd6_2 + license: LicenseRef-libglvnd + purls: [] + size: 134712 + timestamp: 1731330998354 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.86.4-h6548e54_1.conda + sha256: a27e44168a1240b15659888ce0d9b938ed4bdb49e9ea68a7c1ff27bcea8b55ce + md5: bb26456332b07f68bf3b7622ed71c0da + depends: + - __glibc >=2.17,<3.0.a0 + - libffi >=3.5.2,<3.6.0a0 + - libgcc >=14 + - libiconv >=1.18,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - pcre2 >=10.47,<10.48.0a0 + constrains: + - glib 2.86.4 *_1 + license: LGPL-2.1-or-later + purls: [] + size: 4398701 + timestamp: 1771863239578 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_2.conda + sha256: 1175f8a7a0c68b7f81962699751bb6574e6f07db4c9f72825f978e3016f46850 + md5: 434ca7e50e40f4918ab701e3facd59a0 + depends: + - __glibc >=2.17,<3.0.a0 + license: LicenseRef-libglvnd + purls: [] + size: 132463 + timestamp: 1731330968309 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_2.conda + sha256: 2d35a679624a93ce5b3e9dd301fff92343db609b79f0363e6d0ceb3a6478bfa7 + md5: c8013e438185f33b13814c5c488acd5c + depends: + - __glibc >=2.17,<3.0.a0 + - libglvnd 1.7.0 ha4b6fd6_2 + - xorg-libx11 >=1.8.10,<2.0a0 + license: LicenseRef-libglvnd + purls: [] + size: 75504 + timestamp: 1731330988898 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libheif-1.19.7-gpl_hc18d805_100.conda + sha256: ec9797d57088aeed7ca4905777d4f3e70a4dbe90853590eef7006b0ab337af3f + md5: 1db2693fa6a50bef58da2df97c5204cb + depends: + - __glibc >=2.17,<3.0.a0 + - aom >=3.9.1,<3.10.0a0 + - dav1d >=1.2.1,<1.2.2.0a0 + - libavif16 >=1.2.0,<2.0a0 + - libde265 >=1.0.15,<1.0.16.0a0 + - libgcc >=13 + - libstdcxx >=13 + - x265 >=3.5,<3.6.0a0 + license: LGPL-3.0-or-later + license_family: LGPL + purls: [] + size: 596714 + timestamp: 1741306859216 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libheif-1.19.7-gpl_h79e6334_100.conda + sha256: 19384a0c0922cbded842e1fa14d8c40a344cb735d1d85598b11f67dc0cd1f4cc + md5: 4f5369442ff2de5983831d321f584eb4 + depends: + - __osx >=11.0 + - aom >=3.9.1,<3.10.0a0 + - dav1d >=1.2.1,<1.2.2.0a0 + - libavif16 >=1.2.0,<2.0a0 + - libcxx >=18 + - libde265 >=1.0.15,<1.0.16.0a0 + - x265 >=3.5,<3.6.0a0 + license: LGPL-3.0-or-later + license_family: LGPL + purls: [] + size: 442619 + timestamp: 1741307000158 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.12.2-default_hafda6a7_1000.conda + sha256: 2cf160794dda62cf93539adf16d26cfd31092829f2a2757dbdd562984c1b110a + md5: 0ed3aa3e3e6bc85050d38881673a692f + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - libxml2 + - libxml2-16 >=2.14.6 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 2449916 + timestamp: 1765103845133 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda + sha256: c467851a7312765447155e071752d7bf9bf44d610a5687e32706f480aad2833f + md5: 915f5995e94f60e9a4826e0b0920ee88 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: LGPL-2.1-only + purls: [] + size: 790176 + timestamp: 1754908768807 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.2-hb03c661_0.conda + sha256: cc9aba923eea0af8e30e0f94f2ad7156e2984d80d1e8e7fe6be5a1f257f0eb32 + md5: 8397539e3a0bbd1695584fb4f927485a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + purls: [] + size: 633710 + timestamp: 1762094827865 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.1.2-hc919400_0.conda + sha256: 6c061c56058bb10374daaef50e81b39cf43e8aee21f0037022c0c39c4f31872f + md5: f0695fbecf1006f27f4395d64bd0c4b8 + depends: + - __osx >=11.0 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + purls: [] + size: 551197 + timestamp: 1762095054358 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-5_h5e43f62_mkl.conda + build_number: 5 + sha256: b411a9dccb21cd6231f8f66b63916a6520a7b23363e6f9d1d111e8660f2798b0 + md5: 88155c848e1278b0990692e716c9eab4 + depends: + - libblas 3.11.0 5_h5875eb1_mkl + constrains: + - liblapacke 3.11.0 5*_mkl + - libcblas 3.11.0 5*_mkl + - blas 2.305 mkl + track_features: + - blas_mkl + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 18398 + timestamp: 1765818583873 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-5_hd9741b5_openblas.conda + build_number: 5 + sha256: 735a6e6f7d7da6f718b6690b7c0a8ae4815afb89138aa5793abe78128e951dbb + md5: ca9d752201b7fa1225bca036ee300f2b + depends: + - libblas 3.11.0 5_h51639a9_openblas + constrains: + - libcblas 3.11.0 5*_openblas + - blas 2.305 openblas + - liblapacke 3.11.0 5*_openblas + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 18551 + timestamp: 1765819121855 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm22-22.1.0-hf7376ad_0.conda + sha256: 2efe1d8060c6afeb2df037fc61c182fb84e10f49cdbd29ed672e112d4d4ce2d7 + md5: 213f51bbcce2964ff2ec00d0fdd38541 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - libxml2 + - libxml2-16 >=2.14.6 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 44236214 + timestamp: 1772009776202 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.2-hb03c661_0.conda + sha256: 755c55ebab181d678c12e49cced893598f2bab22d582fbbf4d8b83c18be207eb + md5: c7c83eecbb72d88b940c249af56c8b17 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - xz 5.8.2.* + license: 0BSD + purls: [] + size: 113207 + timestamp: 1768752626120 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.2-h8088a28_0.conda + sha256: 7bfc7ffb2d6a9629357a70d4eadeadb6f88fa26ebc28f606b1c1e5e5ed99dc7e + md5: 009f0d956d7bfb00de86901d16e486c7 + depends: + - __osx >=11.0 + constrains: + - xz 5.8.2.* + license: 0BSD + purls: [] + size: 92242 + timestamp: 1768752982486 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.67.0-had1ee68_0.conda + sha256: a4a7dab8db4dc81c736e9a9b42bdfd97b087816e029e221380511960ac46c690 + md5: b499ce4b026493a13774bcf0f4c33849 + depends: + - __glibc >=2.17,<3.0.a0 + - c-ares >=1.34.5,<2.0a0 + - libev >=4.33,<4.34.0a0 + - libev >=4.33,<5.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.2,<4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 666600 + timestamp: 1756834976695 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.67.0-hc438710_0.conda + sha256: a07cb53b5ffa2d5a18afc6fd5a526a5a53dd9523fbc022148bd2f9395697c46d + md5: a4b4dd73c67df470d091312ab87bf6ae + depends: + - __osx >=11.0 + - c-ares >=1.34.5,<2.0a0 + - libcxx >=19 + - libev >=4.33,<4.34.0a0 + - libev >=4.33,<5.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.2,<4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 575454 + timestamp: 1756835746393 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda + sha256: 927fe72b054277cde6cb82597d0fcf6baf127dcbce2e0a9d8925a68f1265eef5 + md5: d864d34357c3b65a4b731f78c0801dc4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: LGPL-2.1-only + license_family: GPL + purls: [] + size: 33731 + timestamp: 1750274110928 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.8-hb9d3cd8_0.conda + sha256: 3b3f19ced060013c2dd99d9d46403be6d319d4601814c772a3472fe2955612b0 + md5: 7c7927b404672409d9917d49bff5f2d6 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: LGPL-2.1-or-later + purls: [] + size: 33418 + timestamp: 1734670021371 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.30-openmp_ha158390_4.conda + sha256: ebbbc089b70bcde87c4121a083c724330f02a690fb9d7c6cd18c30f1b12504fa + md5: a6f6d3a31bb29e48d37ce65de54e2df0 + depends: + - __osx >=11.0 + - libgfortran + - libgfortran5 >=14.3.0 + - llvm-openmp >=19.1.7 + constrains: + - openblas >=0.3.30,<0.3.31.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 4284132 + timestamp: 1768547079205 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_2.conda + sha256: 215086c108d80349e96051ad14131b751d17af3ed2cb5a34edd62fa89bfe8ead + md5: 7df50d44d4a14d6c31a2c54f2cd92157 + depends: + - __glibc >=2.17,<3.0.a0 + - libglvnd 1.7.0 ha4b6fd6_2 + license: LicenseRef-libglvnd + purls: [] + size: 50757 + timestamp: 1731330993524 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.18-hb9d3cd8_0.conda + sha256: 0bd91de9b447a2991e666f284ae8c722ffb1d84acb594dbd0c031bd656fa32b2 + md5: 70e3400cbbfa03e96dcde7fc13e38c7b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + purls: [] + size: 28424 + timestamp: 1749901812541 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.55-h421ea60_0.conda + sha256: 36ade759122cdf0f16e2a2562a19746d96cf9c863ffaa812f2f5071ebbe9c03c + md5: 5f13ffc7d30ffec87864e678df9957b4 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libzlib >=1.3.1,<2.0a0 + license: zlib-acknowledgement + purls: [] + size: 317669 + timestamp: 1770691470744 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.55-h132b30e_0.conda + sha256: 7a4fd29a6ee2d7f7a6e610754dfdf7410ed08f40d8d8b488a27bc0f9981d5abb + md5: 871dc88b0192ac49b6a5509932c31377 + depends: + - __osx >=11.0 + - libzlib >=1.3.1,<2.0a0 + license: zlib-acknowledgement + purls: [] + size: 288950 + timestamp: 1770691485950 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.3-h9abb657_0.conda + sha256: c7e61b86c273ec1ce92c0e087d1a0f3ed3b9485507c6cd35e03bc63de1b6b03f + md5: 405ec206d230d9d37ad7c2636114cbf4 + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=78.2,<79.0a0 + - krb5 >=1.22.2,<1.23.0a0 + - libgcc >=14 + - openldap >=2.6.10,<2.7.0a0 + - openssl >=3.5.5,<4.0a0 + license: PostgreSQL + purls: [] + size: 2865686 + timestamp: 1772136328077 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-6.33.5-h2b00c02_0.conda + sha256: afbf195443269ae10a940372c1d37cda749355d2bd96ef9587a962abd87f2429 + md5: 11ac478fa72cf12c214199b8a96523f4 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20260107.0,<20260108.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 3638698 + timestamp: 1769749419271 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-6.33.5-h4a5acfd_0.conda + sha256: 626852cd50690526c9eac216a9f467edd4cbb01060d0efe41b7def10b54bdb08 + md5: b839e3295b66434f20969c8b940f056a + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20260107.0,<20260108.0a0 + - libcxx >=19 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 2713660 + timestamp: 1769748299578 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.2-hf4e2dac_0.conda + sha256: 04596fcee262a870e4b7c9807224680ff48d4d0cc0dac076a602503d3dc6d217 + md5: da5be73701eecd0e8454423fd6ffcf30 + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=78.2,<79.0a0 + - libgcc >=14 + - libzlib >=1.3.1,<2.0a0 + license: blessing + purls: [] + size: 942808 + timestamp: 1768147973361 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.51.2-h1ae2325_0.conda + sha256: 6e9b9f269732cbc4698c7984aa5b9682c168e2a8d1e0406e1ff10091ca046167 + md5: 4b0bf313c53c3e89692f020fb55d5f2c + depends: + - __osx >=11.0 + - icu >=78.2,<79.0a0 + - libzlib >=1.3.1,<2.0a0 + license: blessing + purls: [] + size: 909777 + timestamp: 1768148320535 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda + sha256: fa39bfd69228a13e553bd24601332b7cfeb30ca11a3ca50bb028108fe90a7661 + md5: eecce068c7e4eddeb169591baac20ac4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.0,<4.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 304790 + timestamp: 1745608545575 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h1590b86_0.conda + sha256: 8bfe837221390ffc6f111ecca24fa12d4a6325da0c8d131333d63d6c37f27e0a + md5: b68e8f66b94b44aaa8de4583d3d4cc40 + depends: + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.0,<4.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 279193 + timestamp: 1745608793272 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_18.conda + sha256: 78668020064fdaa27e9ab65cd2997e2c837b564ab26ce3bf0e58a2ce1a525c6e + md5: 1b08cd684f34175e4514474793d44bcb + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc 15.2.0 he0feb66_18 + constrains: + - libstdcxx-ng ==15.2.0=*_18 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 5852330 + timestamp: 1771378262446 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_18.conda + sha256: 3c902ffd673cb3c6ddde624cdb80f870b6c835f8bf28384b0016e7d444dd0145 + md5: 6235adb93d064ecdf3d44faee6f468de + depends: + - libstdcxx 15.2.0 h934c35e_18 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 27575 + timestamp: 1771378314494 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.1-h9d88235_1.conda + sha256: e5f8c38625aa6d567809733ae04bb71c161a42e44a9fa8227abe61fa5c60ebe0 + md5: cd5a90476766d53e901500df9215e927 + depends: + - __glibc >=2.17,<3.0.a0 + - lerc >=4.0.0,<5.0a0 + - libdeflate >=1.25,<1.26.0a0 + - libgcc >=14 + - libjpeg-turbo >=3.1.0,<4.0a0 + - liblzma >=5.8.1,<6.0a0 + - libstdcxx >=14 + - libwebp-base >=1.6.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: HPND + purls: [] + size: 435273 + timestamp: 1762022005702 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.1-h4030677_1.conda + sha256: e9248077b3fa63db94caca42c8dbc6949c6f32f94d1cafad127f9005d9b1507f + md5: e2a72ab2fa54ecb6abab2b26cde93500 + depends: + - __osx >=11.0 + - lerc >=4.0.0,<5.0a0 + - libcxx >=19 + - libdeflate >=1.25,<1.26.0a0 + - libjpeg-turbo >=3.1.0,<4.0a0 + - liblzma >=5.8.1,<6.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: HPND + purls: [] + size: 373892 + timestamp: 1762022345545 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libtorch-2.10.0-cpu_mkl_h7058990_103.conda + sha256: 92d93119edc7a058987a72984e07a434f999031fcc7a0c851d28cb87c372128a + md5: 2df90510834746b1f52c5299bc99a81f + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex * *_llvm + - _openmp_mutex >=4.5 + - fmt >=12.1.0,<12.2.0a0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libblas * *mkl + - libcblas >=3.11.0,<4.0a0 + - libgcc >=14 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - libstdcxx >=14 + - libuv >=1.51.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - llvm-openmp >=22.1.0 + - mkl >=2025.3.0,<2026.0a0 + - pybind11-abi 11 + - sleef >=3.9.0,<4.0a0 + constrains: + - pytorch-cpu 2.10.0 + - pytorch-gpu <0.0a0 + - pytorch 2.10.0 cpu_mkl_*_103 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 61645121 + timestamp: 1772260200165 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtorch-2.10.0-cpu_generic_hf7cc835_3.conda + sha256: a47f1ec77004982a78e3e1533d841bea0930c22594c12bc67e2cb74cd7709b97 + md5: 98f89ad42eaba858443d31336677aed2 + depends: + - __osx >=11.0 + - fmt >=12.1.0,<12.2.0a0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libcxx >=19 + - liblapack >=3.9.0,<4.0a0 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - libuv >=1.51.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - llvm-openmp >=19.1.7 + - pybind11-abi 11 + - sleef >=3.9.0,<4.0a0 + constrains: + - pytorch-cpu 2.10.0 + - openblas * openmp_* + - pytorch-gpu <0.0a0 + - libopenblas * openmp_* + - pytorch 2.10.0 cpu_generic_*_3 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 30298089 + timestamp: 1772181525404 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-h5347b49_0.conda + sha256: 1a7539cfa7df00714e8943e18de0b06cceef6778e420a5ee3a2a145773758aee + md5: db409b7c1720428638e7c0d509d3e1b5 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 40311 + timestamp: 1766271528534 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.51.0-hb03c661_1.conda + sha256: c180f4124a889ac343fc59d15558e93667d894a966ec6fdb61da1604481be26b + md5: 0f03292cc56bf91a077a134ea8747118 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 895108 + timestamp: 1753948278280 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.51.0-h6caf38d_1.conda + sha256: 042c7488ad97a5629ec0a991a8b2a3345599401ecc75ad6a5af73b60e6db9689 + md5: c0d87c3c8e075daf1daf6c31b53e8083 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 421195 + timestamp: 1753948426421 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.341.0-h5279c79_0.conda + sha256: a68280d57dfd29e3d53400409a39d67c4b9515097eba733aa6fe00c880620e2b + md5: 31ad065eda3c2d88f8215b1289df9c89 + depends: + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + - xorg-libx11 >=1.8.12,<2.0a0 + - xorg-libxrandr >=1.5.5,<2.0a0 + constrains: + - libvulkan-headers 1.4.341.0.* + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 199795 + timestamp: 1770077125520 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda + sha256: 3aed21ab28eddffdaf7f804f49be7a7d701e8f0e46c856d801270b470820a37b + md5: aea31d2e5b1091feca96fcfe945c3cf9 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - libwebp 1.6.0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 429011 + timestamp: 1752159441324 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h07db88b_0.conda + sha256: a4de3f371bb7ada325e1f27a4ef7bcc81b2b6a330e46fac9c2f78ac0755ea3dd + md5: e5e7d467f80da752be17796b87fe6385 + depends: + - __osx >=11.0 + constrains: + - libwebp 1.6.0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 294974 + timestamp: 1752159906788 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda + sha256: 666c0c431b23c6cec6e492840b176dde533d48b7e6fb8883f5071223433776aa + md5: 92ed62436b625154323d40d5f2f11dd7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - pthread-stubs + - xorg-libxau >=1.0.11,<2.0a0 + - xorg-libxdmcp + license: MIT + license_family: MIT + purls: [] + size: 395888 + timestamp: 1727278577118 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda + sha256: bd3816218924b1e43b275863e21a3e13a5db4a6da74cca8e60bc3c213eb62f71 + md5: af523aae2eca6dfa1c8eec693f5b9a79 + depends: + - __osx >=11.0 + - pthread-stubs + - xorg-libxau >=1.0.11,<2.0a0 + - xorg-libxdmcp + license: MIT + license_family: MIT + purls: [] + size: 323658 + timestamp: 1727278733917 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda + sha256: 6ae68e0b86423ef188196fff6207ed0c8195dd84273cb5623b85aa08033a410c + md5: 5aa797f8787fe7a17d1b0821485b5adc + depends: + - libgcc-ng >=12 + license: LGPL-2.1-or-later + purls: [] + size: 100393 + timestamp: 1702724383534 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.1-hca5e8e5_0.conda + sha256: d2195b5fbcb0af1ff7b345efdf89290c279b8d1d74f325ae0ac98148c375863c + md5: 2bca1fbb221d9c3c8e3a155784bbc2e9 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - libxcb >=1.17.0,<2.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + - xkeyboard-config + - xorg-libxau >=1.0.12,<2.0a0 + license: MIT/X11 Derivative + license_family: MIT + purls: [] + size: 837922 + timestamp: 1764794163823 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.2-he237659_0.conda + sha256: 275c324f87bda1a3b67d2f4fcc3555eeff9e228a37655aa001284a7ceb6b0392 + md5: e49238a1609f9a4a844b09d9926f2c3d + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=78.2,<79.0a0 + - libgcc >=14 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.2,<6.0a0 + - libxml2-16 2.15.2 hca6bf5a_0 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 45968 + timestamp: 1772704614539 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.2-hca6bf5a_0.conda + sha256: 08d2b34b49bec9613784f868209bb7c3bb8840d6cf835ff692e036b09745188c + md5: f3bc152cb4f86babe30f3a4bf0dbef69 + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=78.2,<79.0a0 + - libgcc >=14 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.2,<6.0a0 + - libzlib >=1.3.1,<2.0a0 + constrains: + - libxml2 2.15.2 + license: MIT + license_family: MIT + purls: [] + size: 557492 + timestamp: 1772704601644 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.43-h711ed8c_1.conda + sha256: 0694760a3e62bdc659d90a14ae9c6e132b525a7900e59785b18a08bb52a5d7e5 + md5: 87e6096ec6d542d1c1f8b33245fe8300 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libxml2 + - libxml2-16 >=2.14.6 + license: MIT + license_family: MIT + purls: [] + size: 245434 + timestamp: 1757963724977 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + sha256: d4bfe88d7cb447768e31650f06257995601f89076080e76df55e3112d4e47dc4 + md5: edb0dca6bc32e4f4789199455a1dbeb8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + constrains: + - zlib 1.3.1 *_2 + license: Zlib + license_family: Other + purls: [] + size: 60963 + timestamp: 1727963148474 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda + sha256: ce34669eadaba351cd54910743e6a2261b67009624dbc7daeeafdef93616711b + md5: 369964e85dc26bfe78f41399b366c435 + depends: + - __osx >=11.0 + constrains: + - zlib 1.3.1 *_2 + license: Zlib + license_family: Other + purls: [] + size: 46438 + timestamp: 1727963202283 +- conda: https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-22.1.0-h4922eb0_0.conda + sha256: 543c9f17cf6ee6d7b635823fb9009df421d510c36739534df6ae43eadaf6ff4e + md5: 5e7da5333653c631d27732893b934351 + depends: + - __glibc >=2.17,<3.0.a0 + constrains: + - intel-openmp <0.0a0 + - openmp 22.1.0|22.1.0.* + license: Apache-2.0 WITH LLVM-exception + license_family: APACHE + purls: [] + size: 6136884 + timestamp: 1772024545 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.0-hc7d1edf_0.conda + sha256: 0daeedb3872ad0fdd6f0d7e7165c63488e8a315d7057907434145fba0c1e7b3d + md5: ff0820b5588b20be3b858552ecf8ffae + depends: + - __osx >=11.0 + constrains: + - openmp 22.1.0|22.1.0.* + - intel-openmp <0.0a0 + license: Apache-2.0 WITH LLVM-exception + license_family: APACHE + purls: [] + size: 285558 + timestamp: 1772028716784 +- conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py312h8a5da7c_1.conda + sha256: 5f3aad1f3a685ed0b591faad335957dbdb1b73abfd6fc731a0d42718e0653b33 + md5: 93a4752d42b12943a355b682ee43285b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - jinja2 >=3.0.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/markupsafe?source=compressed-mapping + size: 26057 + timestamp: 1772445297924 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py312h04c11ed_1.conda + sha256: 330394fb9140995b29ae215a19fad46fcc6691bdd1b7654513d55a19aaa091c1 + md5: 11d95ab83ef0a82cc2de12c1e0b47fe4 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - jinja2 >=3.0.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/markupsafe?source=compressed-mapping + size: 25564 + timestamp: 1772445846939 +- conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.8-py312h7900ff3_0.conda + sha256: 6d66175e1a4ffb91ed954e2c11066d2e03a05bce951a808275069836ddfc993e + md5: 2a7663896e5aab10b60833a768c4c272 + depends: + - matplotlib-base >=3.10.8,<3.10.9.0a0 + - pyside6 >=6.7.2 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - tornado >=5 + license: PSF-2.0 + license_family: PSF + purls: [] + size: 17415 + timestamp: 1763055550515 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.10.8-py312h1f38498_0.conda + sha256: e3e8448b10273807bf1aa9b1aa6a4ee3a686ccfd0c296560b51b1d1581bb42ae + md5: 534ed7eb4471c088285fdb382805e6ef + depends: + - matplotlib-base >=3.10.8,<3.10.9.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - tornado >=5 + license: PSF-2.0 + license_family: PSF + purls: [] + size: 17526 + timestamp: 1763060540928 +- conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.8-py312he3d6523_0.conda + sha256: 70cf0e7bfd50ef50eb712a6ca1eef0ef0d63b7884292acc81353327b434b548c + md5: b8dc157bbbb69c1407478feede8b7b42 + depends: + - __glibc >=2.17,<3.0.a0 + - contourpy >=1.0.1 + - cycler >=0.10 + - fonttools >=4.22.0 + - freetype + - kiwisolver >=1.3.1 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - libgcc >=14 + - libstdcxx >=14 + - numpy >=1.23 + - numpy >=1.23,<3 + - packaging >=20.0 + - pillow >=8 + - pyparsing >=2.3.1 + - python >=3.12,<3.13.0a0 + - python-dateutil >=2.7 + - python_abi 3.12.* *_cp312 + - qhull >=2020.2,<2020.3.0a0 + - tk >=8.6.13,<8.7.0a0 + license: PSF-2.0 + license_family: PSF + purls: + - pkg:pypi/matplotlib?source=hash-mapping + size: 8442149 + timestamp: 1763055517581 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.10.8-py312h605b88b_0.conda + sha256: 3c96c85dd723a4c16fce4446d1f0dc7d64e46b6ae4629c66d65984b8593ee999 + md5: fbc4f90b3d63ea4e6c30f7733a0b5bfd + depends: + - __osx >=11.0 + - contourpy >=1.0.1 + - cycler >=0.10 + - fonttools >=4.22.0 + - freetype + - kiwisolver >=1.3.1 + - libcxx >=19 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - numpy >=1.23 + - numpy >=1.23,<3 + - packaging >=20.0 + - pillow >=8 + - pyparsing >=2.3.1 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python-dateutil >=2.7 + - python_abi 3.12.* *_cp312 + - qhull >=2020.2,<2020.3.0a0 + license: PSF-2.0 + license_family: PSF + purls: + - pkg:pypi/matplotlib?source=hash-mapping + size: 8243636 + timestamp: 1763060482877 +- conda: https://conda.anaconda.org/conda-forge/linux-64/mkl-2025.3.0-h0e700b2_463.conda + sha256: 659d79976f06d2b796a0836414573a737a0856b05facfa77e5cc114081a8b3d4 + md5: f121ddfc96e6a93a26d85906adf06208 + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex * *_llvm + - _openmp_mutex >=4.5 + - libgcc >=14 + - libstdcxx >=14 + - llvm-openmp >=21.1.8 + - tbb >=2022.3.0 + license: LicenseRef-IntelSimplifiedSoftwareOct2022 + license_family: Proprietary + purls: [] + size: 125728406 + timestamp: 1767634121080 +- conda: https://conda.anaconda.org/conda-forge/linux-64/mpc-1.3.1-h24ddda3_1.conda + sha256: 1bf794ddf2c8b3a3e14ae182577c624fa92dea975537accff4bc7e5fea085212 + md5: aa14b9a5196a6d8dd364164b7ce56acf + depends: + - __glibc >=2.17,<3.0.a0 + - gmp >=6.3.0,<7.0a0 + - libgcc >=13 + - mpfr >=4.2.1,<5.0a0 + license: LGPL-3.0-or-later + license_family: LGPL + purls: [] + size: 116777 + timestamp: 1725629179524 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/mpc-1.3.1-h8f1351a_1.conda + sha256: 2700899ad03302a1751dbf2bca135407e470dd83ac897ab91dd8675d4300f158 + md5: a5635df796b71f6ca400fc7026f50701 + depends: + - __osx >=11.0 + - gmp >=6.3.0,<7.0a0 + - mpfr >=4.2.1,<5.0a0 + license: LGPL-3.0-or-later + license_family: LGPL + purls: [] + size: 104766 + timestamp: 1725629165420 +- conda: https://conda.anaconda.org/conda-forge/linux-64/mpfr-4.2.1-h90cbb55_3.conda + sha256: f25d2474dd557ca66c6231c8f5ace5af312efde1ba8290a6ea5e1732a4e669c0 + md5: 2eeb50cab6652538eee8fc0bc3340c81 + depends: + - __glibc >=2.17,<3.0.a0 + - gmp >=6.3.0,<7.0a0 + - libgcc >=13 + license: LGPL-3.0-only + license_family: LGPL + purls: [] + size: 634751 + timestamp: 1725746740014 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/mpfr-4.2.1-hb693164_3.conda + sha256: 4463e4e2aba7668e37a1b8532859191b4477a6f3602a5d6b4d64ad4c4baaeac5 + md5: 4e4ea852d54cc2b869842de5044662fb + depends: + - __osx >=11.0 + - gmp >=6.3.0,<7.0a0 + license: LGPL-3.0-only + license_family: LGPL + purls: [] + size: 345517 + timestamp: 1725746730583 +- conda: https://conda.anaconda.org/conda-forge/noarch/mpmath-1.3.0-pyhd8ed1ab_1.conda + sha256: 7d7aa3fcd6f42b76bd711182f3776a02bef09a68c5f117d66b712a6d81368692 + md5: 3585aa87c43ab15b167b574cd73b057b + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/mpmath?source=hash-mapping + size: 439705 + timestamp: 1733302781386 +- conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda + sha256: d09c47c2cf456de5c09fa66d2c3c5035aa1fa228a1983a433c47b876aa16ce90 + md5: 37293a85a0f4f77bbd9cf7aaefc62609 + depends: + - python >=3.9 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/munkres?source=hash-mapping + size: 15851 + timestamp: 1749895533014 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda + sha256: 3fde293232fa3fca98635e1167de6b7c7fda83caf24b9d6c91ec9eefb4f4d586 + md5: 47e340acb35de30501a76c7c799c41d7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: X11 AND BSD-3-Clause + purls: [] + size: 891641 + timestamp: 1738195959188 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda + sha256: 2827ada40e8d9ca69a153a45f7fd14f32b2ead7045d3bbb5d10964898fe65733 + md5: 068d497125e4bf8a66bf707254fff5ae + depends: + - __osx >=11.0 + license: X11 AND BSD-3-Clause + purls: [] + size: 797030 + timestamp: 1738196177597 +- conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda + sha256: f6a82172afc50e54741f6f84527ef10424326611503c64e359e25a19a8e4c1c6 + md5: a2c1eeadae7a309daed9d62c96012a2b + depends: + - python >=3.11 + - python + constrains: + - numpy >=1.25 + - scipy >=1.11.2 + - matplotlib-base >=3.8 + - pandas >=2.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/networkx?source=hash-mapping + size: 1587439 + timestamp: 1765215107045 +- conda: https://conda.anaconda.org/conda-forge/noarch/nomkl-1.0-h5ca1d4c_0.tar.bz2 + sha256: d38542a151a90417065c1a234866f97fd1ea82a81de75ecb725955ab78f88b4b + md5: 9a66894dfd07c4510beb6b3f9672ccc0 + constrains: + - mkl <0.a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 3843 + timestamp: 1582593857545 +- conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.2-py312h33ff503_1.conda + sha256: fec4d37e1a7c677ddc07bb968255df74902733398b77acc1d05f9dc599e879df + md5: 3569a8fca2dd3202e4ab08f42499f6d3 + depends: + - python + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - liblapack >=3.9.0,<4.0a0 + - python_abi 3.12.* *_cp312 + - libcblas >=3.9.0,<4.0a0 + - libblas >=3.9.0,<4.0a0 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/numpy?source=hash-mapping + size: 8757566 + timestamp: 1770098484112 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.2-py312he281c53_1.conda + sha256: 7fd2f1a33b244129dcc2163304d103a7062fc38f01fe13945c9ea95cef12b954 + md5: 4afbe6ffff0335d25f3c5cc78b1350a4 + depends: + - python + - libcxx >=19 + - __osx >=11.0 + - python 3.12.* *_cpython + - libblas >=3.9.0,<4.0a0 + - python_abi 3.12.* *_cp312 + - liblapack >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/numpy?source=hash-mapping + size: 6840961 + timestamp: 1770098400654 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda + sha256: 3900f9f2dbbf4129cf3ad6acf4e4b6f7101390b53843591c53b00f034343bc4d + md5: 11b3379b191f63139e29c0d19dee24cd + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libpng >=1.6.50,<1.7.0a0 + - libstdcxx >=14 + - libtiff >=4.7.1,<4.8.0a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 355400 + timestamp: 1758489294972 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda + sha256: 60aca8b9f94d06b852b296c276b3cf0efba5a6eb9f25feb8708570d3a74f00e4 + md5: 4b5d3a91320976eec71678fad1e3569b + depends: + - __osx >=11.0 + - libcxx >=19 + - libpng >=1.6.55,<1.7.0a0 + - libtiff >=4.7.1,<4.8.0a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-2-Clause + purls: [] + size: 319697 + timestamp: 1772625397692 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-hbde042b_1.conda + sha256: 2e185a3dc2bdc4525dd68559efa3f24fa9159a76c40473e320732b35115163b2 + md5: 3c40a106eadf7c14c6236ceddb267893 + depends: + - __glibc >=2.17,<3.0.a0 + - cyrus-sasl >=2.1.28,<3.0a0 + - krb5 >=1.22.2,<1.23.0a0 + - libgcc >=14 + - libstdcxx >=14 + - openssl >=3.5.5,<4.0a0 + license: OLDAP-2.8 + license_family: BSD + purls: [] + size: 785570 + timestamp: 1771970256722 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.1-h35e630c_1.conda + sha256: 44c877f8af015332a5d12f5ff0fb20ca32f896526a7d0cdb30c769df1144fb5c + md5: f61eb8cd60ff9057122a3d338b99c00f + depends: + - __glibc >=2.17,<3.0.a0 + - ca-certificates + - libgcc >=14 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 3164551 + timestamp: 1769555830639 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.1-hd24854e_1.conda + sha256: 361f5c5e60052abc12bdd1b50d7a1a43e6a6653aab99a2263bf2288d709dcf67 + md5: f4f6ad63f98f64191c3e77c5f5f29d76 + depends: + - __osx >=11.0 + - ca-certificates + license: Apache-2.0 + license_family: Apache + purls: [] + size: 3104268 + timestamp: 1769556384749 +- conda: https://conda.anaconda.org/conda-forge/linux-64/optree-0.19.0-py312hd9148b4_0.conda + sha256: e5cb326247948543cc2b0495e5da4d9b724ebd6e6b6907c9ed6adc1b969aeb2a + md5: 6103697b406b6605e744623d9f3d0e0b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - typing-extensions >=4.12 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/optree?source=hash-mapping + size: 482838 + timestamp: 1771868357488 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/optree-0.19.0-py312h766f71e_0.conda + sha256: 76ea1f736255aeea8b917ea826a0c186ab267656b156709ca374df5dc8ff4d26 + md5: a98fa17883d7862ef3049f171a03b9dc + depends: + - __osx >=11.0 + - libcxx >=19 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - typing-extensions >=4.12 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/optree?source=compressed-mapping + size: 429640 + timestamp: 1771868574672 +- conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.0-pyhcf101f3_0.conda + sha256: c1fc0f953048f743385d31c468b4a678b3ad20caffdeaa94bed85ba63049fd58 + md5: b76541e68fea4d511b1ac46a28dcd2c6 + depends: + - python >=3.8 + - python + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/packaging?source=compressed-mapping + size: 72010 + timestamp: 1769093650580 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-haa7fec5_0.conda + sha256: 5e6f7d161356fefd981948bea5139c5aa0436767751a6930cb1ca801ebb113ff + md5: 7a3bff861a6583f1889021facefc08b1 + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - libgcc >=14 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 1222481 + timestamp: 1763655398280 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.1.1-py312h50c33e8_0.conda + sha256: 782b6b578a0e61f6ef5cca5be993d902db775a2eb3d0328a3c4ff515858e7f2c + md5: c5eff3ada1a829f0bdb780dc4b62bbae + depends: + - python + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libjpeg-turbo >=3.1.2,<4.0a0 + - tk >=8.6.13,<8.7.0a0 + - libxcb >=1.17.0,<2.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - lcms2 >=2.18,<3.0a0 + - python_abi 3.12.* *_cp312 + - zlib-ng >=2.3.3,<2.4.0a0 + - libtiff >=4.7.1,<4.8.0a0 + - openjpeg >=2.5.4,<3.0a0 + license: HPND + purls: + - pkg:pypi/pillow?source=compressed-mapping + size: 1029755 + timestamp: 1770794002406 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.1.1-py312h4e908a4_0.conda + sha256: 4729476631c025dfce555a5fd97f1b0b97e765e7c01aee5b7e59b880d8335006 + md5: 537e6079e50e219252d016254b0d2573 + depends: + - python + - __osx >=11.0 + - python 3.12.* *_cpython + - libwebp-base >=1.6.0,<2.0a0 + - openjpeg >=2.5.4,<3.0a0 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - python_abi 3.12.* *_cp312 + - libjpeg-turbo >=3.1.2,<4.0a0 + - tk >=8.6.13,<8.7.0a0 + - libxcb >=1.17.0,<2.0a0 + - libtiff >=4.7.1,<4.8.0a0 + - zlib-ng >=2.3.3,<2.4.0a0 + - lcms2 >=2.18,<3.0a0 + license: HPND + purls: + - pkg:pypi/pillow?source=compressed-mapping + size: 954096 + timestamp: 1770794152238 +- conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.0.1-pyh8b19718_0.conda + sha256: 8e1497814a9997654ed7990a79c054ea5a42545679407acbc6f7e809c73c9120 + md5: 67bdec43082fd8a9cffb9484420b39a2 + depends: + - python >=3.10,<3.13.0a0 + - setuptools + - wheel + license: MIT + license_family: MIT + purls: + - pkg:pypi/pip?source=compressed-mapping + size: 1181790 + timestamp: 1770270305795 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_1.conda + sha256: 43d37bc9ca3b257c5dd7bf76a8426addbdec381f6786ff441dc90b1a49143b6a + md5: c01af13bdc553d1a8fbfff6e8db075f0 + depends: + - libgcc >=14 + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + license: MIT + license_family: MIT + purls: [] + size: 450960 + timestamp: 1754665235234 +- pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl + name: pluggy + version: 1.6.0 + sha256: e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 + requires_dist: + - pre-commit ; extra == 'dev' + - tox ; extra == 'dev' + - pytest ; extra == 'testing' + - pytest-benchmark ; extra == 'testing' + - coverage ; extra == 'testing' + requires_python: '>=3.9' +- conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda + sha256: 9c88f8c64590e9567c6c80823f0328e58d3b1efb0e1c539c0315ceca764e0973 + md5: b3c17d95b5a10c6e64a21fa17573e70e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + purls: [] + size: 8252 + timestamp: 1726802366959 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda + sha256: 8ed65e17fbb0ca944bfb8093b60086e3f9dd678c3448b5de212017394c247ee3 + md5: 415816daf82e0b23a736a069a75e9da7 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 8381 + timestamp: 1726802424786 +- conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-3.0.1-pyh7a1b43c_0.conda + sha256: 2558727093f13d4c30e124724566d16badd7de532fd8ee7483628977117d02be + md5: 70ece62498c769280f791e836ac53fff + depends: + - python >=3.8 + - pybind11-global ==3.0.1 *_0 + - python + constrains: + - pybind11-abi ==11 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/pybind11?source=hash-mapping + size: 232875 + timestamp: 1755953378112 +- conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-abi-11-hc364b38_1.conda + sha256: 9e7fe12f727acd2787fb5816b2049cef4604b7a00ad3e408c5e709c298ce8bf1 + md5: f0599959a2447c1e544e216bddf393fa + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 14671 + timestamp: 1752769938071 +- conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-global-3.0.1-pyhc7ab6ef_0.conda + sha256: f11a5903879fe3a24e0d28329cb2b1945127e85a4cdb444b45545cf079f99e2d + md5: fe10b422ce8b5af5dab3740e4084c3f9 + depends: + - python >=3.8 + - __unix + - python + constrains: + - pybind11-abi ==11 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/pybind11-global?source=hash-mapping + size: 228871 + timestamp: 1755953338243 +- pypi: https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl + name: pygments + version: 2.19.2 + sha256: 86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b + requires_dist: + - colorama>=0.4.6 ; extra == 'windows-terminal' + requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda + sha256: 417fba4783e528ee732afa82999300859b065dc59927344b4859c64aae7182de + md5: 3687cc0b82a8b4c17e1f0eb7e47163d5 + depends: + - python >=3.10 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/pyparsing?source=compressed-mapping + size: 110893 + timestamp: 1769003998136 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.10.2-py312h9da60e5_0.conda + sha256: 5e00f3e0a273e18b4ec0ae7f4fff507333a354dd5ecc31dd9f3b02ab1ee77163 + md5: 52412f1ae11e89b721784f2118188902 + depends: + - __glibc >=2.17,<3.0.a0 + - libclang13 >=21.1.8 + - libegl >=1.7.0,<2.0a0 + - libgcc >=14 + - libgl >=1.7.0,<2.0a0 + - libopengl >=1.7.0,<2.0a0 + - libstdcxx >=14 + - libvulkan-loader >=1.4.341.0,<2.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + - libxslt >=1.1.43,<2.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - qt6-main 6.10.2.* + - qt6-main >=6.10.2,<6.11.0a0 + license: LGPL-3.0-only + license_family: LGPL + purls: + - pkg:pypi/pyside6?source=hash-mapping + - pkg:pypi/shiboken6?source=hash-mapping + size: 11679474 + timestamp: 1770081343622 +- pypi: https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl + name: pytest + version: 9.0.2 + sha256: 711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b + requires_dist: + - colorama>=0.4 ; sys_platform == 'win32' + - exceptiongroup>=1 ; python_full_version < '3.11' + - iniconfig>=1.0.1 + - packaging>=22 + - pluggy>=1.5,<2 + - pygments>=2.7.2 + - tomli>=1 ; python_full_version < '3.11' + - argcomplete ; extra == 'dev' + - attrs>=19.2 ; extra == 'dev' + - hypothesis>=3.56 ; extra == 'dev' + - mock ; extra == 'dev' + - requests ; extra == 'dev' + - setuptools ; extra == 'dev' + - xmlschema ; extra == 'dev' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl + name: pytest-cov + version: 7.0.0 + sha256: 3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861 + requires_dist: + - coverage[toml]>=7.10.6 + - pluggy>=1.2 + - pytest>=7 + - process-tests ; extra == 'testing' + - pytest-xdist ; extra == 'testing' + - virtualenv ; extra == 'testing' + requires_python: '>=3.9' +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.13-hd63d673_0_cpython.conda + sha256: a44655c1c3e1d43ed8704890a91e12afd68130414ea2c0872e154e5633a13d7e + md5: 7eccb41177e15cc672e1babe9056018e + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - ld_impl_linux-64 >=2.36.1 + - libexpat >=2.7.4,<3.0a0 + - libffi >=3.5.2,<3.6.0a0 + - libgcc >=14 + - liblzma >=5.8.2,<6.0a0 + - libnsl >=2.0.1,<2.1.0a0 + - libsqlite >=3.51.2,<4.0a0 + - libuuid >=2.41.3,<3.0a0 + - libxcrypt >=4.4.36 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.5.5,<4.0a0 + - readline >=8.3,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + constrains: + - python_abi 3.12.* *_cp312 + license: Python-2.0 + purls: [] + size: 31608571 + timestamp: 1772730708989 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.13-h8561d8f_0_cpython.conda + sha256: e658e647a4a15981573d6018928dec2c448b10c77c557c29872043ff23c0eb6a + md5: 8e7608172fa4d1b90de9a745c2fd2b81 + depends: + - __osx >=11.0 + - bzip2 >=1.0.8,<2.0a0 + - libexpat >=2.7.4,<3.0a0 + - libffi >=3.5.2,<3.6.0a0 + - liblzma >=5.8.2,<6.0a0 + - libsqlite >=3.51.2,<4.0a0 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.5.5,<4.0a0 + - readline >=8.3,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + constrains: + - python_abi 3.12.* *_cp312 + license: Python-2.0 + purls: [] + size: 12127424 + timestamp: 1772730755512 +- conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + sha256: d6a17ece93bbd5139e02d2bd7dbfa80bee1a4261dced63f65f679121686bf664 + md5: 5b8d21249ff20967101ffa321cab24e8 + depends: + - python >=3.9 + - six >=1.5 + - python + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/python-dateutil?source=hash-mapping + size: 233310 + timestamp: 1751104122689 +- conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda + build_number: 8 + sha256: 80677180dd3c22deb7426ca89d6203f1c7f1f256f2d5a94dc210f6e758229809 + md5: c3efd25ac4d74b1584d2f7a57195ddf1 + constrains: + - python 3.12.* *_cpython + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 6958 + timestamp: 1752805918820 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-2.10.0-cpu_mkl_py312_hca44ed5_103.conda + sha256: 0ac2511916f1e82e436e432e9e2c08471e58480f95435716f28ec5bd618ee9ce + md5: 4ecb7390be8580b0b2116bbc1fdda486 + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex * *_llvm + - _openmp_mutex >=4.5 + - filelock + - fmt >=12.1.0,<12.2.0a0 + - fsspec + - jinja2 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libblas * *mkl + - libcblas >=3.11.0,<4.0a0 + - libgcc >=14 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - libstdcxx >=14 + - libtorch 2.10.0 cpu_mkl_h7058990_103 + - libuv >=1.51.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - llvm-openmp >=22.1.0 + - mkl >=2025.3.0,<2026.0a0 + - mpmath <1.4 + - networkx + - numpy >=1.23,<3 + - optree >=0.13.0 + - pybind11 <3.0.2 + - pybind11-abi 11 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - setuptools + - sleef >=3.9.0,<4.0a0 + - sympy >=1.13.3 + - typing_extensions >=4.10.0 + constrains: + - pytorch-cpu 2.10.0 + - pytorch-gpu <0.0a0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/torch?source=hash-mapping + size: 24291825 + timestamp: 1772260740719 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pytorch-2.10.0-cpu_generic_py312_h2470ad0_3.conda + sha256: 6da6392a6d89f043c914c658cdaf98c49e6860ab7e91a0afcb8463723bca4364 + md5: b4c7ecd785628fbd767d9cb854ec2c0c + depends: + - __osx >=11.0 + - filelock + - fmt >=12.1.0,<12.2.0a0 + - fsspec + - jinja2 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libcblas >=3.9.0,<4.0a0 + - libcxx >=19 + - liblapack >=3.9.0,<4.0a0 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - libtorch 2.10.0 cpu_generic_hf7cc835_3 + - libuv >=1.51.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - llvm-openmp >=19.1.7 + - mpmath <1.4 + - networkx + - nomkl + - numpy >=1.23,<3 + - optree >=0.13.0 + - pybind11 <3.0.2 + - pybind11-abi 11 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - setuptools + - sleef >=3.9.0,<4.0a0 + - sympy >=1.13.3 + - typing_extensions >=4.10.0 + constrains: + - pytorch-cpu 2.10.0 + - pytorch-gpu <0.0a0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/torch?source=hash-mapping + size: 22846148 + timestamp: 1772185000775 +- conda: https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda + sha256: 776363493bad83308ba30bcb88c2552632581b143e8ee25b1982c8c743e73abc + md5: 353823361b1d27eb3960efb076dfcaf6 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: LicenseRef-Qhull + purls: [] + size: 552937 + timestamp: 1720813982144 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/qhull-2020.2-h420ef59_5.conda + sha256: 873ac689484262a51fd79bc6103c1a1bedbf524924d7f0088fb80703042805e4 + md5: 6483b1f59526e05d7d894e466b5b6924 + depends: + - __osx >=11.0 + - libcxx >=16 + license: LicenseRef-Qhull + purls: [] + size: 516376 + timestamp: 1720814307311 +- conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.10.2-h17e89b9_5.conda + sha256: 94242e6623d10c08ebdaaf183056a15fc3b27b092a4cd826399ddabef7d51c2e + md5: 6c4f73c9a7e9b51f3a8e321c3e867bb6 + depends: + - __glibc >=2.17,<3.0.a0 + - alsa-lib >=1.2.15.3,<1.3.0a0 + - dbus >=1.16.2,<2.0a0 + - double-conversion >=3.4.0,<3.5.0a0 + - fontconfig >=2.17.1,<3.0a0 + - fonts-conda-ecosystem + - harfbuzz >=12.3.2 + - icu >=78.2,<79.0a0 + - krb5 >=1.22.2,<1.23.0a0 + - libbrotlicommon >=1.2.0,<1.3.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libclang-cpp22.1 >=22.1.0,<22.2.0a0 + - libclang13 >=22.1.0 + - libcups >=2.3.3,<2.4.0a0 + - libdrm >=2.4.125,<2.5.0a0 + - libegl >=1.7.0,<2.0a0 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - libgcc >=14 + - libgl >=1.7.0,<2.0a0 + - libglib >=2.86.4,<3.0a0 + - libjpeg-turbo >=3.1.2,<4.0a0 + - libllvm22 >=22.1.0,<22.2.0a0 + - libpng >=1.6.55,<1.7.0a0 + - libpq >=18.2,<19.0a0 + - libsqlite >=3.51.2,<4.0a0 + - libstdcxx >=14 + - libtiff >=4.7.1,<4.8.0a0 + - libvulkan-loader >=1.4.341.0,<2.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - libxcb >=1.17.0,<2.0a0 + - libxkbcommon >=1.13.1,<2.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.5,<4.0a0 + - pcre2 >=10.47,<10.48.0a0 + - wayland >=1.24.0,<2.0a0 + - xcb-util >=0.4.1,<0.5.0a0 + - xcb-util-cursor >=0.1.6,<0.2.0a0 + - xcb-util-image >=0.4.0,<0.5.0a0 + - xcb-util-keysyms >=0.4.1,<0.5.0a0 + - xcb-util-renderutil >=0.3.10,<0.4.0a0 + - xcb-util-wm >=0.4.2,<0.5.0a0 + - xorg-libice >=1.1.2,<2.0a0 + - xorg-libsm >=1.2.6,<2.0a0 + - xorg-libx11 >=1.8.13,<2.0a0 + - xorg-libxcomposite >=0.4.7,<1.0a0 + - xorg-libxcursor >=1.2.3,<2.0a0 + - xorg-libxdamage >=1.1.6,<2.0a0 + - xorg-libxext >=1.3.7,<2.0a0 + - xorg-libxrandr >=1.5.5,<2.0a0 + - xorg-libxtst >=1.2.5,<2.0a0 + - xorg-libxxf86vm >=1.1.7,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - qt 6.10.2 + license: LGPL-3.0-only + license_family: LGPL + purls: [] + size: 56550801 + timestamp: 1772121854618 +- conda: https://conda.anaconda.org/conda-forge/linux-64/rav1e-0.8.1-h1fbca29_0.conda + sha256: cf550bbc8e5ebedb6dba9ccaead3e07bd1cb86b183644a4c853e06e4b3ad5ac7 + md5: d83958768626b3c8471ce032e28afcd3 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - __glibc >=2.17 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 5595970 + timestamp: 1772540833621 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/rav1e-0.8.1-h8246384_0.conda + sha256: 925e35b71fe513e0380ecd2fe137e3f4f248bf7ce4bad96946c7c704b7a50d26 + md5: 4706a8a71474c692482c3f86c2175454 + depends: + - __osx >=11.0 + constrains: + - __osx >=11.0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 886953 + timestamp: 1772541394570 +- conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda + sha256: 12ffde5a6f958e285aa22c191ca01bbd3d6e710aa852e00618fa6ddc59149002 + md5: d7d95fc8287ea7bf33e0e7116d2b95ec + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - ncurses >=6.5,<7.0a0 + license: GPL-3.0-only + license_family: GPL + purls: [] + size: 345073 + timestamp: 1765813471974 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda + sha256: a77010528efb4b548ac2a4484eaf7e1c3907f2aec86123ed9c5212ae44502477 + md5: f8381319127120ce51e081dce4865cf4 + depends: + - __osx >=11.0 + - ncurses >=6.5,<7.0a0 + license: GPL-3.0-only + license_family: GPL + purls: [] + size: 313930 + timestamp: 1765813902568 +- conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.8.0-np2py312h3226591_1.conda + sha256: 23c643c37fafa14ba3f2b7a407126ea5e732a3655ea8157cf9f977098f863448 + md5: 38decbeae260892040709cafc0514162 + depends: + - python + - numpy >=1.24.1 + - scipy >=1.10.0 + - joblib >=1.3.0 + - threadpoolctl >=3.2.0 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - _openmp_mutex >=4.5 + - numpy >=1.23,<3 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/scikit-learn?source=hash-mapping + size: 9726193 + timestamp: 1765801245538 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.8.0-np2py312he5ca3e3_1.conda + sha256: 5f640a06e001666f9d4dca7cca992f1753e722e9f6e50899d7d250c02ddf7398 + md5: ed7887c51edfa304c69a424279cec675 + depends: + - python + - numpy >=1.24.1 + - scipy >=1.10.0 + - joblib >=1.3.0 + - threadpoolctl >=3.2.0 + - libcxx >=19 + - python 3.12.* *_cpython + - __osx >=11.0 + - llvm-openmp >=19.1.7 + - numpy >=1.23,<3 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/scikit-learn?source=hash-mapping + size: 9124177 + timestamp: 1766550900752 +- conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.17.1-py312h54fa4ab_0.conda + sha256: e3ad577361d67f6c078a6a7a3898bf0617b937d44dc4ccd57aa3336f2b5778dd + md5: 3e38daeb1fb05a95656ff5af089d2e4c + depends: + - __glibc >=2.17,<3.0.a0 + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libgcc >=14 + - libgfortran + - libgfortran5 >=14.3.0 + - liblapack >=3.9.0,<4.0a0 + - libstdcxx >=14 + - numpy <2.7 + - numpy >=1.23,<3 + - numpy >=1.25.2 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/scipy?source=compressed-mapping + size: 17109648 + timestamp: 1771880675810 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.17.1-py312h0f234b1_0.conda + sha256: 7082a8c87ae32b6090681a1376e3335cf23c95608c68a3f96f3581c847f8b840 + md5: fd035cd01bb171090a990ae4f4143090 + depends: + - __osx >=11.0 + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libcxx >=19 + - libgfortran + - libgfortran5 >=14.3.0 + - liblapack >=3.9.0,<4.0a0 + - numpy <2.7 + - numpy >=1.23,<3 + - numpy >=1.25.2 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/scipy?source=compressed-mapping + size: 13966986 + timestamp: 1771881089893 +- conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.0-pyh332efcf_0.conda + sha256: fd7201e38e38bf7f25818d624ca8da97b8998957ca9ae3fb7fdc9c17e6b25fcd + md5: 1d00d46c634177fc8ede8b99d6089239 + depends: + - python >=3.10 + license: MIT + license_family: MIT + purls: + - pkg:pypi/setuptools?source=compressed-mapping + size: 637506 + timestamp: 1770634745653 +- conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda + sha256: 458227f759d5e3fcec5d9b7acce54e10c9e1f4f4b7ec978f3bfd54ce4ee9853d + md5: 3339e3b65d58accf4ca4fb8748ab16b3 + depends: + - python >=3.9 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/six?source=hash-mapping + size: 18455 + timestamp: 1753199211006 +- conda: https://conda.anaconda.org/conda-forge/linux-64/sleef-3.9.0-ha0421bc_0.conda + sha256: 57afc2ab5bdb24cf979964018dddbc5dfaee130b415e6863765e45aed2175ee4 + md5: e8a0b4f5e82ecacffaa5e805020473cb + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex >=4.5 + - libgcc >=14 + - libstdcxx >=14 + license: BSL-1.0 + purls: [] + size: 1951720 + timestamp: 1756274576844 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/sleef-3.9.0-hb028509_0.conda + sha256: 799d0578369e67b6d0d6ecdacada411c259629fc4a500b99703c5e85d0a68686 + md5: 68f833178f171cfffdd18854c0e9b7f9 + depends: + - __osx >=11.0 + - libcxx >=19 + - llvm-openmp >=19.1.7 + license: BSL-1.0 + purls: [] + size: 587027 + timestamp: 1756274982526 +- pypi: ./ + name: spectralnet + version: 0.1.2 + sha256: 86d1f2e00688eb4866e70815ff2dc41f671fd9c9317d5b103b30837e00d56b0b + requires_dist: + - setuptools + - wheel + - torch>=2.0.0 + - torchvision>=0.15.1 + - h5py>=3.8.0 + - numpy>=1.24 + - annoy>=1.17.1 + - scipy>=1.10.1 + - munkres + - matplotlib + - scikit-learn>=1.2.2 + - pytest>=7.0 ; extra == 'dev' + - pytest-cov ; extra == 'dev' + requires_python: '>=3.11' +- conda: https://conda.anaconda.org/conda-forge/linux-64/svt-av1-4.0.1-hecca717_0.conda + sha256: 4a1d2005153b9454fc21c9bad1b539df189905be49e851ec62a6212c2e045381 + md5: 2a2170a3e5c9a354d09e4be718c43235 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 2619743 + timestamp: 1769664536467 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/svt-av1-4.0.1-h0cb729a_0.conda + sha256: bdef3c1c4d2a396ad4f7dc64c5e9a02d4c5a21ff93ed07a33e49574de5d2d18d + md5: 8badc3bf16b62272aa2458f138223821 + depends: + - __osx >=11.0 + - libcxx >=19 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 1456245 + timestamp: 1769664727051 +- conda: https://conda.anaconda.org/conda-forge/noarch/sympy-1.14.0-pyh2585a3b_106.conda + sha256: 1c8057e6875eba958aa8b3c1a072dc9a75d013f209c26fd8125a5ebd3abbec0c + md5: 32d866e43b25275f61566b9391ccb7b5 + depends: + - __unix + - cpython + - gmpy2 >=2.0.8 + - mpmath >=1.1.0,<1.5 + - python >=3.10 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/sympy?source=compressed-mapping + size: 4661767 + timestamp: 1771952371059 +- conda: https://conda.anaconda.org/conda-forge/linux-64/tbb-2022.3.0-hb700be7_2.conda + sha256: 975710e4b7f1b13c3c30b7fbf21e22f50abe0463b6b47a231582fdedcc45c961 + md5: 8f7278ca5f7456a974992a8b34284737 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libhwloc >=2.12.2,<2.12.3.0a0 + - libstdcxx >=14 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 181329 + timestamp: 1767886632911 +- conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda + sha256: 6016672e0e72c4cf23c0cf7b1986283bd86a9c17e8d319212d78d8e9ae42fdfd + md5: 9d64911b31d57ca443e9f1e36b04385f + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/threadpoolctl?source=hash-mapping + size: 23869 + timestamp: 1741878358548 +- conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda + sha256: cafeec44494f842ffeca27e9c8b0c27ed714f93ac77ddadc6aaf726b5554ebac + md5: cffd3bdd58090148f4cfcd831f4b26ab + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libzlib >=1.3.1,<2.0a0 + constrains: + - xorg-libx11 >=1.8.12,<2.0a0 + license: TCL + license_family: BSD + purls: [] + size: 3301196 + timestamp: 1769460227866 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda + sha256: 799cab4b6cde62f91f750149995d149bc9db525ec12595e8a1d91b9317f038b3 + md5: a9d86bc62f39b94c4661716624eb21b0 + depends: + - __osx >=11.0 + - libzlib >=1.3.1,<2.0a0 + license: TCL + license_family: BSD + purls: [] + size: 3127137 + timestamp: 1769460817696 +- conda: https://conda.anaconda.org/conda-forge/linux-64/torchvision-0.25.0-cpu_py312_h843e820_3.conda + sha256: 5ac4cf646c78824e985e04b1c0bcc13a4d2a251b157c957dea167ce55489ca82 + md5: e3a44da4c3af6b53019b8dde8157273b + depends: + - python + - pytorch * cpu* + - pillow >=5.3.0,!=8.3.0,!=8.3.1 + - torchvision-extra-decoders + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - giflib >=5.2.2,<5.3.0a0 + - pytorch >=2.10.0,<2.11.0a0 + - libtorch >=2.10.0,<2.11.0a0 + - libpng >=1.6.54,<1.7.0a0 + - numpy >=1.23,<3 + - python_abi 3.12.* *_cp312 + - libjpeg-turbo >=3.1.2,<4.0a0 + - libwebp-base >=1.6.0,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/torchvision?source=hash-mapping + size: 1487564 + timestamp: 1769898340161 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/torchvision-0.25.0-cpu_py312_h215c760_3.conda + sha256: 2f08f4dac3904ea59eddf71656ad701a603922fa40aaffb6b2997f67bfc83a84 + md5: 843ba3bcf311239ffef67fdbe8b6f755 + depends: + - python + - pytorch * cpu* + - pillow >=5.3.0,!=8.3.0,!=8.3.1 + - torchvision-extra-decoders + - python 3.12.* *_cpython + - libcxx >=19 + - __osx >=11.0 + - numpy >=1.23,<3 + - libpng >=1.6.54,<1.7.0a0 + - libtorch >=2.10.0,<2.11.0a0 + - pytorch >=2.10.0,<2.11.0a0 + - libjpeg-turbo >=3.1.2,<4.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - python_abi 3.12.* *_cp312 + - giflib >=5.2.2,<5.3.0a0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/torchvision?source=hash-mapping + size: 1404602 + timestamp: 1769898298663 +- conda: https://conda.anaconda.org/conda-forge/linux-64/torchvision-extra-decoders-0.0.2-py312h1b2fc9e_6.conda + sha256: 64ac4c6bde9c52f1b5ec192bba56c583fd4f8f2e5f9015f0796e89b4a15459ed + md5: f9028b12a9ba51e1ce0f03efd1d38cb3 + depends: + - python + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - pytorch >=2.10.0,<2.11.0a0 + - libtorch >=2.10.0,<2.11.0a0 + - python_abi 3.12.* *_cp312 + - libheif >=1.19.7,<1.20.0a0 + - libavif16 >=1.3.0,<2.0a0 + license: LGPL-2.1-only + purls: + - pkg:pypi/torchvision-extra-decoders?source=hash-mapping + size: 67797 + timestamp: 1769464622010 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/torchvision-extra-decoders-0.0.2-py312hf15907b_6.conda + sha256: 603dba371ca407f656dd1d2b38b681afb4cc31fdc449dbbd51f9dc29b8b6bc25 + md5: feb0cecd71a441e67e3be8fe3fb10171 + depends: + - python + - libcxx >=19 + - python 3.12.* *_cpython + - __osx >=11.0 + - libtorch >=2.10.0,<2.11.0a0 + - pytorch >=2.10.0,<2.11.0a0 + - libavif16 >=1.3.0,<2.0a0 + - libheif >=1.19.7,<1.20.0a0 + - python_abi 3.12.* *_cp312 + license: LGPL-2.1-only + purls: + - pkg:pypi/torchvision-extra-decoders?source=hash-mapping + size: 71133 + timestamp: 1769464763999 +- conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.3-py312h4c3975b_0.conda + sha256: bed440cad040f0fe76266f9a527feecbaf00385b68a96532aa69614fe5153f8e + md5: e03a4bf52d2170d64c816b2a52972097 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/tornado?source=compressed-mapping + size: 850918 + timestamp: 1765458857375 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.4-py312h4409184_0.conda + sha256: 114bfa1b859a64c589c428fce0ff8e358d8f0aaa7b98d353b94a95c7bceae640 + md5: fde4548a1e99c14eea9752f270ab68aa + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/tornado?source=hash-mapping + size: 854598 + timestamp: 1765836762571 +- pypi: https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl + name: tqdm + version: 4.67.3 + sha256: ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf + requires_dist: + - colorama ; sys_platform == 'win32' + - importlib-metadata ; python_full_version < '3.8' + - pytest>=6 ; extra == 'dev' + - pytest-cov ; extra == 'dev' + - pytest-timeout ; extra == 'dev' + - pytest-asyncio>=0.24 ; extra == 'dev' + - nbval ; extra == 'dev' + - requests ; extra == 'discord' + - slack-sdk ; extra == 'slack' + - requests ; extra == 'telegram' + - ipywidgets>=6 ; extra == 'notebook' + requires_python: '>=3.7' +- conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda + sha256: 7c2df5721c742c2a47b2c8f960e718c930031663ac1174da67c1ed5999f7938c + md5: edd329d7d3a4ab45dcf905899a7a6115 + depends: + - typing_extensions ==4.15.0 pyhcf101f3_0 + license: PSF-2.0 + license_family: PSF + purls: [] + size: 91383 + timestamp: 1756220668932 +- conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + sha256: 032271135bca55aeb156cee361c81350c6f3fb203f57d024d7e5a1fc9ef18731 + md5: 0caa1af407ecff61170c9437a808404d + depends: + - python >=3.10 + - python + license: PSF-2.0 + license_family: PSF + purls: + - pkg:pypi/typing-extensions?source=hash-mapping + size: 51692 + timestamp: 1756220668932 +- conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda + sha256: 1d30098909076af33a35017eed6f2953af1c769e273a0626a04722ac4acaba3c + md5: ad659d0a2b3e47e38d829aa8cad2d610 + license: LicenseRef-Public-Domain + purls: [] + size: 119135 + timestamp: 1767016325805 +- conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.1-py312h4c3975b_0.conda + sha256: 895bbfe9ee25c98c922799de901387d842d7c01cae45c346879865c6a907f229 + md5: 0b6c506ec1f272b685240e70a29261b8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/unicodedata2?source=compressed-mapping + size: 410641 + timestamp: 1770909099497 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py312h2bbb03f_0.conda + sha256: e935d0c11581e31e89ce4899a28b16f924d1a3c1af89f18f8a2c5f5728b3107f + md5: 45b836f333fd3e282c16fff7dc82994e + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/unicodedata2?source=compressed-mapping + size: 415828 + timestamp: 1770909782683 +- conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.24.0-hd6090a7_1.conda + sha256: 3aa04ae8e9521d9b56b562376d944c3e52b69f9d2a0667f77b8953464822e125 + md5: 035da2e4f5770f036ff704fa17aace24 + depends: + - __glibc >=2.17,<3.0.a0 + - libexpat >=2.7.1,<3.0a0 + - libffi >=3.5.2,<3.6.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + purls: [] + size: 329779 + timestamp: 1761174273487 +- conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.46.3-pyhd8ed1ab_0.conda + sha256: d6cf2f0ebd5e09120c28ecba450556ce553752652d91795442f0e70f837126ae + md5: bdbd7385b4a67025ac2dba4ef8cb6a8f + depends: + - packaging >=24.0 + - python >=3.10 + license: MIT + license_family: MIT + purls: + - pkg:pypi/wheel?source=hash-mapping + size: 31858 + timestamp: 1769139207397 +- conda: https://conda.anaconda.org/conda-forge/linux-64/x265-3.5-h924138e_3.tar.bz2 + sha256: 76c7405bcf2af639971150f342550484efac18219c0203c5ee2e38b8956fe2a0 + md5: e7f6ed84d4623d52ee581325c1587a6b + depends: + - libgcc-ng >=10.3.0 + - libstdcxx-ng >=10.3.0 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 3357188 + timestamp: 1646609687141 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/x265-3.5-hbc6ce65_3.tar.bz2 + sha256: 2fed6987dba7dee07bd9adc1a6f8e6c699efb851431bcb6ebad7de196e87841d + md5: b1f7f2780feffe310b068c021e8ff9b2 + depends: + - libcxx >=12.0.1 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 1832744 + timestamp: 1646609481185 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda + sha256: ad8cab7e07e2af268449c2ce855cbb51f43f4664936eff679b1f3862e6e4b01d + md5: fdc27cb255a7a2cc73b7919a968b48f0 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libxcb >=1.17.0,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 20772 + timestamp: 1750436796633 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.6-hb03c661_0.conda + sha256: c2be9cae786fdb2df7c2387d2db31b285cf90ab3bfabda8fa75a596c3d20fc67 + md5: 4d1fc190b99912ed557a8236e958c559 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libxcb >=1.13 + - libxcb >=1.17.0,<2.0a0 + - xcb-util-image >=0.4.0,<0.5.0a0 + - xcb-util-renderutil >=0.3.10,<0.4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 20829 + timestamp: 1763366954390 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda + sha256: 94b12ff8b30260d9de4fd7a28cca12e028e572cbc504fd42aa2646ec4a5bded7 + md5: a0901183f08b6c7107aab109733a3c91 + depends: + - libgcc-ng >=12 + - libxcb >=1.16,<2.0.0a0 + - xcb-util >=0.4.1,<0.5.0a0 + license: MIT + license_family: MIT + purls: [] + size: 24551 + timestamp: 1718880534789 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.1-hb711507_0.conda + sha256: 546e3ee01e95a4c884b6401284bb22da449a2f4daf508d038fdfa0712fe4cc69 + md5: ad748ccca349aec3e91743e08b5e2b50 + depends: + - libgcc-ng >=12 + - libxcb >=1.16,<2.0.0a0 + license: MIT + license_family: MIT + purls: [] + size: 14314 + timestamp: 1718846569232 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.10-hb711507_0.conda + sha256: 2d401dadc43855971ce008344a4b5bd804aca9487d8ebd83328592217daca3df + md5: 0e0cbe0564d03a99afd5fd7b362feecd + depends: + - libgcc-ng >=12 + - libxcb >=1.16,<2.0.0a0 + license: MIT + license_family: MIT + purls: [] + size: 16978 + timestamp: 1718848865819 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.conda + sha256: 31d44f297ad87a1e6510895740325a635dd204556aa7e079194a0034cdd7e66a + md5: 608e0ef8256b81d04456e8d211eee3e8 + depends: + - libgcc-ng >=12 + - libxcb >=1.16,<2.0.0a0 + license: MIT + license_family: MIT + purls: [] + size: 51689 + timestamp: 1718844051451 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.47-hb03c661_0.conda + sha256: 19c2bb14bec84b0e995b56b752369775c75f1589314b43733948bb5f471a6915 + md5: b56e0c8432b56decafae7e78c5f29ba5 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - xorg-libx11 >=1.8.13,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 399291 + timestamp: 1772021302485 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda + sha256: c12396aabb21244c212e488bbdc4abcdef0b7404b15761d9329f5a4a39113c4b + md5: fb901ff28063514abb6046c9ec2c4a45 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + purls: [] + size: 58628 + timestamp: 1734227592886 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.conda + sha256: 277841c43a39f738927145930ff963c5ce4c4dacf66637a3d95d802a64173250 + md5: 1c74ff8c35dcadf952a16f752ca5aa49 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libuuid >=2.38.1,<3.0a0 + - xorg-libice >=1.1.2,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 27590 + timestamp: 1741896361728 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_0.conda + sha256: 516d4060139dbb4de49a4dcdc6317a9353fb39ebd47789c14e6fe52de0deee42 + md5: 861fb6ccbc677bb9a9fb2468430b9c6a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libxcb >=1.17.0,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 839652 + timestamp: 1770819209719 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_1.conda + sha256: 6bc6ab7a90a5d8ac94c7e300cc10beb0500eeba4b99822768ca2f2ef356f731b + md5: b2895afaf55bf96a8c8282a2e47a5de0 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 15321 + timestamp: 1762976464266 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-hc919400_1.conda + sha256: adae11db0f66f86156569415ed79cda75b2dbf4bea48d1577831db701438164f + md5: 78b548eed8227a689f93775d5d23ae09 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 14105 + timestamp: 1762976976084 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.7-hb03c661_0.conda + sha256: 048c103000af9541c919deef03ae7c5e9c570ffb4024b42ecb58dbde402e373a + md5: f2ba4192d38b6cef2bb2c25029071d90 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - xorg-libx11 >=1.8.12,<2.0a0 + - xorg-libxfixes >=6.0.2,<7.0a0 + license: MIT + license_family: MIT + purls: [] + size: 14415 + timestamp: 1770044404696 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda + sha256: 832f538ade441b1eee863c8c91af9e69b356cd3e9e1350fff4fe36cc573fc91a + md5: 2ccd714aa2242315acaf0a67faea780b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxfixes >=6.0.1,<7.0a0 + - xorg-libxrender >=0.9.11,<0.10.0a0 + license: MIT + license_family: MIT + purls: [] + size: 32533 + timestamp: 1730908305254 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda + sha256: 43b9772fd6582bf401846642c4635c47a9b0e36ca08116b3ec3df36ab96e0ec0 + md5: b5fcc7172d22516e1f965490e65e33a4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxfixes >=6.0.1,<7.0a0 + license: MIT + license_family: MIT + purls: [] + size: 13217 + timestamp: 1727891438799 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_1.conda + sha256: 25d255fb2eef929d21ff660a0c687d38a6d2ccfbcbf0cc6aa738b12af6e9d142 + md5: 1dafce8548e38671bea82e3f5c6ce22f + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 20591 + timestamp: 1762976546182 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hc919400_1.conda + sha256: f7fa0de519d8da589995a1fe78ef74556bb8bc4172079ae3a8d20c3c81354906 + md5: 9d1299ace1924aa8f4e0bc8e71dd0cf7 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 19156 + timestamp: 1762977035194 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.7-hb03c661_0.conda + sha256: 79c60fc6acfd3d713d6340d3b4e296836a0f8c51602327b32794625826bd052f + md5: 34e54f03dfea3e7a2dcf1453a85f1085 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - xorg-libx11 >=1.8.12,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 50326 + timestamp: 1769445253162 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-hb03c661_0.conda + sha256: 83c4c99d60b8784a611351220452a0a85b080668188dce5dfa394b723d7b64f4 + md5: ba231da7fccf9ea1e768caf5c7099b84 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - xorg-libx11 >=1.8.12,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 20071 + timestamp: 1759282564045 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.2-hb9d3cd8_0.conda + sha256: 1a724b47d98d7880f26da40e45f01728e7638e6ec69f35a3e11f92acd05f9e7a + md5: 17dcc85db3c7886650b8908b183d6876 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxfixes >=6.0.1,<7.0a0 + license: MIT + license_family: MIT + purls: [] + size: 47179 + timestamp: 1727799254088 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.5-hb03c661_0.conda + sha256: 80ed047a5cb30632c3dc5804c7716131d767089f65877813d4ae855ee5c9d343 + md5: e192019153591938acf7322b6459d36e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - xorg-libx11 >=1.8.12,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxrender >=0.9.12,<0.10.0a0 + license: MIT + license_family: MIT + purls: [] + size: 30456 + timestamp: 1769445263457 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda + sha256: 044c7b3153c224c6cedd4484dd91b389d2d7fd9c776ad0f4a34f099b3389f4a1 + md5: 96d57aba173e878a2089d5638016dc5e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 33005 + timestamp: 1734229037766 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda + sha256: 752fdaac5d58ed863bbf685bb6f98092fe1a488ea8ebb7ed7b606ccfce08637a + md5: 7bbe9a0cc0df0ac5f5a8ad6d6a11af2f + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxi >=1.7.10,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 32808 + timestamp: 1727964811275 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.7-hb03c661_0.conda + sha256: 64db17baaf36fa03ed8fae105e2e671a7383e22df4077486646f7dbf12842c9f + md5: 665d152b9c6e78da404086088077c844 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - xorg-libx11 >=1.8.12,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 18701 + timestamp: 1769434732453 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hceb46e0_1.conda + sha256: ea4e50c465d70236408cb0bfe0115609fd14db1adcd8bd30d8918e0291f8a75f + md5: 2aadb0d17215603a82a2a6b0afd9a4cb + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: Zlib + license_family: Other + purls: [] + size: 122618 + timestamp: 1770167931827 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda + sha256: a339606a6b224bb230ff3d711e801934f3b3844271df9720165e0353716580d4 + md5: d99c2a23a31b0172e90f456f580b695e + depends: + - __osx >=11.0 + - libcxx >=19 + license: Zlib + license_family: Other + purls: [] + size: 94375 + timestamp: 1770168363685 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda + sha256: 68f0206ca6e98fea941e5717cec780ed2873ffabc0e1ed34428c061e2c6268c7 + md5: 4a13eeac0b5c8e5b8ab496e6c4ddd829 + depends: + - __glibc >=2.17,<3.0.a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 601375 + timestamp: 1764777111296 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda + sha256: 9485ba49e8f47d2b597dd399e88f4802e100851b27c21d7525625b0b4025a5d9 + md5: ab136e4c34e97f34fb621d2592a393d8 + depends: + - __osx >=11.0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 433413 + timestamp: 1764777166076 diff --git a/pixi.toml b/pixi.toml new file mode 100644 index 0000000..8287f65 --- /dev/null +++ b/pixi.toml @@ -0,0 +1,29 @@ +[workspace] +name = "spectralnet" +version = "0.1.2" +description = "Spectral Clustering Using Deep Neural Networks" +channels = ["conda-forge", "pytorch"] +platforms = ["osx-arm64", "linux-64"] + +[dependencies] +python = ">=3.11,<3.13" +pytorch = ">=2.0.0" +torchvision = ">=0.15.1" +numpy = ">=1.24" +scipy = ">=1.10.1" +scikit-learn = ">=1.2.2" +matplotlib = ">=3.7" +h5py = ">=3.8.0" +pip = "*" + +[pypi-dependencies] +spectralnet = { path = ".", editable = true } +annoy = ">=1.17.1" +munkres = "*" +pytest = ">=7.0" +pytest-cov = "*" +tqdm = "*" + +[tasks] +test = "pytest src/tests -v --tb=short" +test-fast = "pytest src/tests/test_models.py src/tests/test_losses.py src/tests/test_utils.py src/tests/test_metrics.py -v --tb=short" diff --git a/pyproject.toml b/pyproject.toml index b6c64e6..40c33d9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,16 +1,30 @@ [build-system] -# Minimum requirements for the build system to execute. requires = [ - "setuptools", + "setuptools>=61.0", "wheel", - "torch>=2.0.0", - "torchvision>=0.15.1", - "h5py>=3.8.0", - "numpy>=1.24", - "annoy>=1.17.1", - "scipy>=1.10.1", - "munkres", - "matplotlib", - "scikit-learn>=1.2.2" ] -build-backend = "setuptools.build_meta" \ No newline at end of file +build-backend = "setuptools.build_meta" + +# --------------------------------------------------------------------------- +# python-semantic-release configuration +# Commit-type → version bump mapping (Conventional Commits): +# fix: patch (0.1.2 → 0.1.3) +# feat: minor (0.1.2 → 0.2.0) +# BREAKING CHANGE major (0.1.2 → 1.0.0) +# (also triggered by feat!: / fix!: etc.) +# --------------------------------------------------------------------------- +[tool.semantic_release] +version_toml = [] +version_variables = ["setup.cfg:version"] +branch = "main" +changelog_file = "CHANGELOG.md" +build_command = "pip install build && python -m build" +dist_path = "dist/" +upload_to_pypi = false +upload_to_release = true +commit_parser = "angular" + +[tool.semantic_release.commit_parser_options] +allowed_tags = ["feat", "fix", "perf", "refactor", "docs", "style", "test", "chore", "ci"] +minor_tags = ["feat"] +patch_tags = ["fix", "perf"] \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index 5f20ae9..c2d7687 100644 --- a/setup.cfg +++ b/setup.cfg @@ -32,4 +32,13 @@ install_requires = scikit-learn>=1.2.2 [options.packages.find] -where = src \ No newline at end of file +where = src + +[options.extras_require] +dev = + pytest>=7.0 + pytest-cov + +[tool:pytest] +testpaths = src/tests +addopts = -v --tb=short From ebffe722c526a87ace186e1035fad22f44801961 Mon Sep 17 00:00:00 2001 From: Amitai Date: Fri, 6 Mar 2026 17:16:51 +0200 Subject: [PATCH 05/10] refactor: clean up imports, remove dead code, fix docstring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - _spectralnet_model.py: remove unused numpy import; replace np.sqrt(m) with plain m**0.5; fix docstring "Cholesky" → "QR decomposition" - _spectralnet_trainer.py: replace absolute wildcard 'from spectralnet._utils import *' with an explicit relative import of the four symbols actually used; remove redundant nested 'with torch.no_grad()' inside validate() (already guarded by the outer context manager) - _reduction.py: replace 'from ._utils import *' with explicit import of the three symbols used (get_affinity_matrix, get_laplacian, plot_laplacian_eigenvectors) - _utils.py: delete dead create_weights_dir() function (trainers now use __file__-relative paths with os.makedirs) --- src/spectralnet/_models/_spectralnet_model.py | 5 ++--- src/spectralnet/_reduction.py | 2 +- src/spectralnet/_trainers/_spectralnet_trainer.py | 13 ++++++++----- src/spectralnet/_utils.py | 8 -------- 4 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/spectralnet/_models/_spectralnet_model.py b/src/spectralnet/_models/_spectralnet_model.py index d645de6..e8e7da0 100644 --- a/src/spectralnet/_models/_spectralnet_model.py +++ b/src/spectralnet/_models/_spectralnet_model.py @@ -1,5 +1,4 @@ import torch -import numpy as np import torch.nn as nn @@ -26,7 +25,7 @@ def __init__(self, architecture: dict, input_dim: int): def _make_orthonorm_weights(self, Y: torch.Tensor) -> torch.Tensor: """ - Orthonormalize the output of the network using the Cholesky decomposition. + Orthonormalize the output of the network using QR decomposition. Parameters ---------- @@ -46,7 +45,7 @@ def _make_orthonorm_weights(self, Y: torch.Tensor) -> torch.Tensor: m = Y.shape[0] _, R = torch.linalg.qr(Y) - orthonorm_weights = np.sqrt(m) * torch.inverse(R) + orthonorm_weights = m**0.5 * torch.inverse(R) return orthonorm_weights def forward( diff --git a/src/spectralnet/_reduction.py b/src/spectralnet/_reduction.py index 1e1958e..2f9d761 100644 --- a/src/spectralnet/_reduction.py +++ b/src/spectralnet/_reduction.py @@ -2,7 +2,7 @@ import numpy as np import matplotlib.pyplot as plt -from ._utils import * +from ._utils import get_affinity_matrix, get_laplacian, plot_laplacian_eigenvectors from ._cluster import SpectralNet from sklearn.cluster import KMeans from ._metrics import Metrics diff --git a/src/spectralnet/_trainers/_spectralnet_trainer.py b/src/spectralnet/_trainers/_spectralnet_trainer.py index 877846a..0f06b16 100644 --- a/src/spectralnet/_trainers/_spectralnet_trainer.py +++ b/src/spectralnet/_trainers/_spectralnet_trainer.py @@ -2,10 +2,14 @@ import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader, random_split, TensorDataset -from sklearn.neighbors import kneighbors_graph from tqdm import trange -from spectralnet._utils import * from ._trainer import Trainer +from .._utils import ( + make_batch_for_sparse_grapsh, + get_nearest_neighbors, + compute_scale, + get_gaussian_kernel, +) from .._losses import SpectralNetLoss from .._models import SpectralNetModel @@ -157,9 +161,8 @@ def validate(self, valid_loader: DataLoader) -> float: X = make_batch_for_sparse_grapsh(X) Y = self.spectral_net(X, should_update_orth_weights=False) - with torch.no_grad(): - if self.siamese_net is not None: - X = self.siamese_net.forward_once(X) + if self.siamese_net is not None: + X = self.siamese_net.forward_once(X) W = self._get_affinity_matrix(X) diff --git a/src/spectralnet/_utils.py b/src/spectralnet/_utils.py index fcf8de6..9449699 100644 --- a/src/spectralnet/_utils.py +++ b/src/spectralnet/_utils.py @@ -535,11 +535,3 @@ def write_assignments_to_file(assignments: np.ndarray): np.savetxt( "cluster_assignments.csv", assignments.astype(int), fmt="%i", delimiter="," ) - - -def create_weights_dir(): - """ - Creates a directory for the weights of the Autoencoder and the Siamese network - """ - if not os.path.exists("weights"): - os.makedirs("weights") From 01cde74b4aae7abe645e5f378b0935a470745757 Mon Sep 17 00:00:00 2001 From: Amitai Date: Sat, 7 Mar 2026 18:52:43 +0200 Subject: [PATCH 06/10] docs: add pixi development setup instructions to README Add a "From source (with pixi)" section under Installation explaining how to install pixi, clone the repo, install the environment with 'pixi install', and run the test suite with 'pixi run test'. --- README.md | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5c440a4..bb54bda 100644 --- a/README.md +++ b/README.md @@ -8,12 +8,39 @@ This package is based on the following paper - [SpectralNet](https://openreview. ## Installation -You can install the latest package version via +### From PyPI ```bash pip install spectralnet ``` +### From source (with pixi) + +[pixi](https://pixi.sh) is the recommended way to set up a fully reproducible +development environment after cloning the repo. + +```bash +# 1. Install pixi (once, system-wide) +curl -fsSL https://pixi.sh/install.sh | sh + +# 2. Clone and enter the repo +git clone https://github.com/shaham-lab/SpectralNet.git +cd SpectralNet + +# 3. Install all dependencies (conda + PyPI) into an isolated environment +pixi install + +# 4. Run the test suite to verify everything works +pixi run test +``` + +After `pixi install` you can prefix any command with `pixi run` to execute it +inside the managed environment, or activate the environment with: + +```bash +pixi shell +``` + ## Usage ### Clustering From 081ec0401a254fa403f84e7888c116938eceb062 Mon Sep 17 00:00:00 2001 From: Amitai Date: Sat, 7 Mar 2026 19:08:21 +0200 Subject: [PATCH 07/10] perf: chunked inference in predict/embed and fix get_random_batch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three scalability fixes for large datasets: 1. get_random_batch() (_cluster.py): previously encoded the *entire* dataset through the AE and Siamese network just to return a small random batch — O(N) wasted work. Now selects indices first and encodes only the sampled batch, wrapped in torch.no_grad(). 2. predict() (_cluster.py): previously moved the full X to GPU and ran AE + SpectralNet in a single forward pass, causing OOM on large inputs. Now iterates in chunks of spectral_batch_size, accumulating CPU tensors, then concatenates for k-means. 3. AETrainer.embed() (_ae_trainer.py): previously called ae_net.encode(X.to(device)) on the whole dataset at once. Now iterates in chunks of batch_size, keeps intermediate results on CPU, and returns a concatenated CPU tensor that DataLoaders in downstream trainers can page to the device batch-by-batch. --- src/spectralnet/_cluster.py | 35 ++++++++++++------------ src/spectralnet/_trainers/_ae_trainer.py | 11 +++++--- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/spectralnet/_cluster.py b/src/spectralnet/_cluster.py index b8ca5ad..b74d31e 100644 --- a/src/spectralnet/_cluster.py +++ b/src/spectralnet/_cluster.py @@ -243,15 +243,17 @@ def predict(self, X: torch.Tensor) -> np.ndarray: np.ndarray The cluster assignments for the given data. """ - X = X.view(X.size(0), -1) - X = X.to(self.device) - + X_flat = X.view(X.size(0), -1) + chunks = [] with torch.no_grad(): - if self.should_use_ae: - X = self.ae_net.encode(X) - self.embeddings_ = self.spec_net(X, should_update_orth_weights=False) - self.embeddings_ = self.embeddings_.detach().cpu().numpy() - + for start in range(0, len(X_flat), self.spectral_batch_size): + chunk = X_flat[start : start + self.spectral_batch_size].to(self.device) + if self.should_use_ae: + chunk = self.ae_net.encode(chunk) + chunks.append( + self.spec_net(chunk, should_update_orth_weights=False).cpu() + ) + self.embeddings_ = torch.cat(chunks).numpy() cluster_assignments = self._get_clusters_by_kmeans(self.embeddings_) return cluster_assignments @@ -272,18 +274,17 @@ def get_random_batch(self, batch_size: int = 1024) -> tuple: n = len(self._X) batch_size = min(batch_size, n) permuted_indices = torch.randperm(n)[:batch_size] - X_raw = self._X.view(n, -1) - X_encoded = X_raw - if self.should_use_ae: - X_encoded = self.ae_trainer.embed(self._X) + # Select the raw batch first — avoids encoding the full dataset. + X_raw = self._X.view(n, -1)[permuted_indices] + X_encoded = X_raw.to(self.device) - if self.should_use_siamese: - X_encoded = self.siamese_net.forward_once(X_encoded) + with torch.no_grad(): + if self.should_use_ae: + X_encoded = self.ae_net.encode(X_encoded) + if self.should_use_siamese: + X_encoded = self.siamese_net.forward_once(X_encoded) - X_encoded = X_encoded[permuted_indices] - X_raw = X_raw[permuted_indices] - X_encoded = X_encoded.to(self.device) return X_raw, X_encoded def _get_clusters_by_kmeans(self, embeddings: np.ndarray) -> np.ndarray: diff --git a/src/spectralnet/_trainers/_ae_trainer.py b/src/spectralnet/_trainers/_ae_trainer.py index dc4f5af..208ca70 100644 --- a/src/spectralnet/_trainers/_ae_trainer.py +++ b/src/spectralnet/_trainers/_ae_trainer.py @@ -91,12 +91,15 @@ def validate(self, valid_loader: DataLoader) -> float: return valid_loss def embed(self, X: torch.Tensor) -> torch.Tensor: - print("Embedding data ...") + """Encode the full dataset in chunks to avoid OOM on large inputs.""" self.ae_net.eval() + X_flat = X.view(X.size(0), -1) + chunks = [] with torch.no_grad(): - X = X.view(X.size(0), -1) - encoded_data = self.ae_net.encode(X.to(self.device)) - return encoded_data + for start in range(0, len(X_flat), self.batch_size): + chunk = X_flat[start : start + self.batch_size].to(self.device) + chunks.append(self.ae_net.encode(chunk).cpu()) + return torch.cat(chunks) def _get_data_loader(self) -> tuple: trainset_len = int(len(self.X) * 0.9) From 9586b6abb15ad45e58054b0b08edc15235e6d98f Mon Sep 17 00:00:00 2001 From: Amitai Date: Sat, 7 Mar 2026 19:27:50 +0200 Subject: [PATCH 08/10] feat: Dataset-based interface for true out-of-core scalability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the Tensor-only fit/predict API with a Dataset-first design so that arbitrarily large datasets stored on disk (e.g. 10 M images) can be processed without ever loading them all into memory at once. _cluster.py — new _FeatureDataset adapter class A thin Dataset wrapper that normalises both in-memory torch.Tensors and any user-defined disk-streaming Dataset into consistent (x_flat, y) tuples. fit() and predict() accept Union[Tensor, Dataset/DataLoader]; Tensor inputs are wrapped automatically for full backward compatibility. fit(X, y=None): - X can now be torch.Tensor (small data) or Dataset (large/disk data) - Stores self._dataset; all trainers receive the Dataset and create their own DataLoaders with the right batch size and shuffling predict(X): - X can be Tensor or DataLoader; iterates in spectral_batch_size chunks get_random_batch(): - Creates a temporary shuffled DataLoader over self._dataset and takes the first batch — O(batch_size) memory, not O(N) AETrainer: - train(dataset): derives input_dim from dataset[0], uses random_split directly on the Dataset; training loop extracts x from (x, y) tuples - embed(dataset) → TensorDataset: encodes via DataLoader, returns a new (encoded_x, y) TensorDataset on CPU for downstream trainers - _get_data_loader(): now calls random_split(self._dataset, ...) SiameseTrainer: - train(dataset): iterates a DataLoader to materialise self.X for KNN; comment notes that a representative subset should be passed for very large datasets (exact KNN is inherently O(N²)) SpectralTrainer: - train(dataset, siamese_net): derives input_dim from dataset[0]; _get_data_loader() calls random_split directly on the Dataset — no TensorDataset construction needed --- src/spectralnet/_cluster.py | 123 ++++++++++++++---- src/spectralnet/_trainers/_ae_trainer.py | 49 +++---- .../_trainers/_siamesenet_trainer.py | 10 +- .../_trainers/_spectralnet_trainer.py | 34 ++--- 4 files changed, 140 insertions(+), 76 deletions(-) diff --git a/src/spectralnet/_cluster.py b/src/spectralnet/_cluster.py index b74d31e..554134b 100644 --- a/src/spectralnet/_cluster.py +++ b/src/spectralnet/_cluster.py @@ -1,11 +1,63 @@ import torch import numpy as np +from typing import Union +from torch.utils.data import DataLoader, Dataset, TensorDataset from ._utils import * from sklearn.cluster import KMeans from ._trainers import SpectralTrainer, SiameseTrainer, AETrainer +class _FeatureDataset(Dataset): + """Adapts a Tensor or any user-defined Dataset into consistent ``(x_flat, y)`` pairs. + + This single adapter is what makes the whole pipeline scale: for small + datasets pass a ``torch.Tensor``; for 10 M images on disk define a + ``Dataset`` whose ``__getitem__`` loads one sample at a time and pass + that instead. Every trainer then pulls data through its own + ``DataLoader``, so nothing large ever lives entirely in memory. + + Parameters + ---------- + source : torch.Tensor or Dataset + Tensor inputs are flattened to ``(N, -1)`` immediately and kept in + memory. Dataset inputs are accessed lazily — only one item at a + time is materialised. Dataset items must be a plain feature tensor + or a ``(x, y)`` tuple. + y : torch.Tensor, optional + Override labels. Only used when *source* is a ``torch.Tensor``. + """ + + def __init__(self, source: Union[torch.Tensor, Dataset], y: torch.Tensor = None): + if isinstance(source, torch.Tensor): + self._x = source.view(source.size(0), -1) + self._y = ( + y if y is not None else torch.zeros(len(self._x), dtype=torch.long) + ) + self._delegate = None + else: + self._delegate = source + self._x = None + self._y = None + + def __len__(self) -> int: + return len(self._delegate) if self._delegate is not None else len(self._x) + + def __getitem__(self, idx): + if self._delegate is not None: + item = self._delegate[idx] + if isinstance(item, (list, tuple)): + x = item[0].flatten() + y = item[1] if len(item) > 1 else torch.tensor(0, dtype=torch.long) + else: + x = item.flatten() + y = torch.tensor(0, dtype=torch.long) + else: + x = self._x[idx] + y = self._y[idx] + return x, y + + class SpectralNet: def __init__( self, @@ -161,18 +213,25 @@ def _validate_spectral_hiddens(self): "The number of units in the last layer of spectral_hiddens network must be equal to the number of clusters or components." ) - def fit(self, X: torch.Tensor, y: torch.Tensor = None): + def fit(self, X: Union[torch.Tensor, Dataset], y: torch.Tensor = None): """Performs the main training loop for the SpectralNet model. Parameters ---------- - X : torch.Tensor - Data to train the networks on. - + X : torch.Tensor or torch.utils.data.Dataset + Training data. For small datasets pass a ``torch.Tensor``. + For large datasets that cannot fit in RAM (e.g. 10 M images + stored on disk) pass a ``torch.utils.data.Dataset`` whose + ``__getitem__`` loads one sample at a time — nothing large + will be materialised in memory at once. Dataset items must be + either a plain feature tensor or a ``(x, y)`` tuple; when + using the Dataset form, the ``y`` argument below is ignored. y : torch.Tensor, optional - Labels in case there are any. Defaults to None. + Integer labels. Only used when ``X`` is a ``torch.Tensor``. + Defaults to None. """ - self._X = X + dataset = _FeatureDataset(X, y=y) + self._dataset = dataset ae_config = { "hiddens": self.ae_hiddens, "epochs": self.ae_epochs, @@ -208,46 +267,61 @@ def fit(self, X: torch.Tensor, y: torch.Tensor = None): "batch_size": self.spectral_batch_size, } + working_dataset = dataset if self.should_use_ae: self.ae_trainer = AETrainer(config=ae_config, device=self.device) - self.ae_net = self.ae_trainer.train(X) - X = self.ae_trainer.embed(X) + self.ae_net = self.ae_trainer.train(working_dataset) + working_dataset = self.ae_trainer.embed(working_dataset) if self.should_use_siamese: self.siamese_trainer = SiameseTrainer( config=siamese_config, device=self.device ) - self.siamese_net = self.siamese_trainer.train(X) + self.siamese_net = self.siamese_trainer.train(working_dataset) else: self.siamese_net = None is_sparse = self.is_sparse_graph if is_sparse: - build_ann(X) + # ANN index construction requires the full feature set in memory; + # iterate the dataset in batches to collect them. + loader = DataLoader(working_dataset, batch_size=self.spectral_batch_size) + X_all = torch.cat([batch[0] for batch in loader]) + build_ann(X_all) self.spectral_trainer = SpectralTrainer( config=spectral_config, device=self.device, is_sparse=is_sparse ) - self.spec_net = self.spectral_trainer.train(X, y, self.siamese_net) + self.spec_net = self.spectral_trainer.train(working_dataset, self.siamese_net) - def predict(self, X: torch.Tensor) -> np.ndarray: + def predict(self, X: Union[torch.Tensor, DataLoader]) -> np.ndarray: """Predicts the cluster assignments for the given data. Parameters ---------- - X : torch.Tensor - Data to be clustered. + X : torch.Tensor or torch.utils.data.DataLoader + Data to cluster. Pass a ``DataLoader`` for large datasets so + that samples are streamed from disk rather than loaded all at + once. Returns ------- np.ndarray The cluster assignments for the given data. """ - X_flat = X.view(X.size(0), -1) + if isinstance(X, torch.Tensor): + loader = DataLoader( + _FeatureDataset(X), + batch_size=self.spectral_batch_size, + shuffle=False, + ) + else: + loader = X chunks = [] with torch.no_grad(): - for start in range(0, len(X_flat), self.spectral_batch_size): - chunk = X_flat[start : start + self.spectral_batch_size].to(self.device) + for batch in loader: + x = batch[0] if isinstance(batch, (list, tuple)) else batch + chunk = x.to(self.device) if self.should_use_ae: chunk = self.ae_net.encode(chunk) chunks.append( @@ -271,21 +345,16 @@ def get_random_batch(self, batch_size: int = 1024) -> tuple: The raw batch and the encoded batch. """ - n = len(self._X) - batch_size = min(batch_size, n) - permuted_indices = torch.randperm(n)[:batch_size] - - # Select the raw batch first — avoids encoding the full dataset. - X_raw = self._X.view(n, -1)[permuted_indices] - X_encoded = X_raw.to(self.device) - + # Use a DataLoader so sampling never requires the full dataset in memory. + loader = DataLoader(self._dataset, batch_size=batch_size, shuffle=True) + x_raw, _ = next(iter(loader)) + X_encoded = x_raw.to(self.device) with torch.no_grad(): if self.should_use_ae: X_encoded = self.ae_net.encode(X_encoded) if self.should_use_siamese: X_encoded = self.siamese_net.forward_once(X_encoded) - - return X_raw, X_encoded + return x_raw, X_encoded def _get_clusters_by_kmeans(self, embeddings: np.ndarray) -> np.ndarray: """Performs k-means clustering on the spectral-embedding space. diff --git a/src/spectralnet/_trainers/_ae_trainer.py b/src/spectralnet/_trainers/_ae_trainer.py index 208ca70..276ee33 100644 --- a/src/spectralnet/_trainers/_ae_trainer.py +++ b/src/spectralnet/_trainers/_ae_trainer.py @@ -6,7 +6,7 @@ from tqdm import trange from ._trainer import Trainer from .._models import AEModel -from torch.utils.data import DataLoader, random_split +from torch.utils.data import DataLoader, Dataset, TensorDataset, random_split class AETrainer: @@ -25,13 +25,12 @@ def __init__(self, config: dict, device: torch.device): self.weights_path = os.path.join(self.weights_dir, "ae_weights.pth") os.makedirs(self.weights_dir, exist_ok=True) - def train(self, X: torch.Tensor) -> AEModel: - self.X = X.view(X.size(0), -1) + def train(self, dataset: Dataset) -> AEModel: + self._dataset = dataset self.criterion = nn.MSELoss() - self.ae_net = AEModel(self.architecture, input_dim=self.X.shape[1]).to( - self.device - ) + x0, _ = dataset[0] + self.ae_net = AEModel(self.architecture, input_dim=x0.numel()).to(self.device) self.optimizer = optim.Adam(self.ae_net.parameters(), lr=self.lr) @@ -49,9 +48,8 @@ def train(self, X: torch.Tensor) -> AEModel: t = trange(self.epochs, leave=True) for epoch in t: train_loss = 0.0 - for batch_x in train_loader: - batch_x = batch_x.to(self.device) - batch_x = batch_x.view(batch_x.size(0), -1) + for x, _ in train_loader: + batch_x = x.to(self.device) self.optimizer.zero_grad() output = self.ae_net(batch_x) loss = self.criterion(output, batch_x) @@ -81,30 +79,35 @@ def validate(self, valid_loader: DataLoader) -> float: self.ae_net.eval() valid_loss = 0.0 with torch.no_grad(): - for batch_x in valid_loader: - batch_x = batch_x.to(self.device) - batch_x = batch_x.view(batch_x.size(0), -1) + for x, _ in valid_loader: + batch_x = x.to(self.device) output = self.ae_net(batch_x) loss = self.criterion(output, batch_x) valid_loss += loss.item() valid_loss /= len(valid_loader) return valid_loss - def embed(self, X: torch.Tensor) -> torch.Tensor: - """Encode the full dataset in chunks to avoid OOM on large inputs.""" + def embed(self, dataset: Dataset) -> TensorDataset: + """Encode an entire dataset chunk-by-chunk to avoid OOM on large inputs. + + Returns a ``TensorDataset`` of ``(encoded_x, y)`` pairs on CPU, + ready to be passed directly to the downstream Siamese or Spectral + trainer as a new Dataset. + """ self.ae_net.eval() - X_flat = X.view(X.size(0), -1) - chunks = [] + loader = DataLoader(dataset, batch_size=self.batch_size, shuffle=False) + encoded_chunks, label_chunks = [], [] with torch.no_grad(): - for start in range(0, len(X_flat), self.batch_size): - chunk = X_flat[start : start + self.batch_size].to(self.device) - chunks.append(self.ae_net.encode(chunk).cpu()) - return torch.cat(chunks) + for x, y in loader: + encoded_chunks.append(self.ae_net.encode(x.to(self.device)).cpu()) + label_chunks.append(y) + return TensorDataset(torch.cat(encoded_chunks), torch.cat(label_chunks)) def _get_data_loader(self) -> tuple: - trainset_len = int(len(self.X) * 0.9) - validset_len = len(self.X) - trainset_len - trainset, validset = random_split(self.X, [trainset_len, validset_len]) + n = len(self._dataset) + trainset_len = int(n * 0.9) + validset_len = n - trainset_len + trainset, validset = random_split(self._dataset, [trainset_len, validset_len]) train_loader = DataLoader( trainset, batch_size=self.ae_config["batch_size"], shuffle=True ) diff --git a/src/spectralnet/_trainers/_siamesenet_trainer.py b/src/spectralnet/_trainers/_siamesenet_trainer.py index 66726a4..0684e40 100644 --- a/src/spectralnet/_trainers/_siamesenet_trainer.py +++ b/src/spectralnet/_trainers/_siamesenet_trainer.py @@ -6,7 +6,7 @@ from tqdm import trange from annoy import AnnoyIndex from sklearn.neighbors import NearestNeighbors -from torch.utils.data import DataLoader, random_split +from torch.utils.data import DataLoader, Dataset, random_split from ._trainer import Trainer from .._models import SiameseNetModel @@ -54,9 +54,11 @@ def __init__(self, config: dict, device: torch.device): os.makedirs(_weights_dir, exist_ok=True) self.weights_path = os.path.join(_weights_dir, "siamese_weights.pth") - def train(self, X: torch.Tensor) -> SiameseNetModel: - self.X = X.view(X.size(0), -1) - # self.X = X + def train(self, dataset: Dataset) -> SiameseNetModel: + # KNN pair construction requires the full feature matrix in memory. + # For very large datasets pass a representative subset as the Dataset. + loader = DataLoader(dataset, batch_size=1024, shuffle=False) + self.X = torch.cat([batch[0] for batch in loader]) self.criterion = ContrastiveLoss() self.siamese_net = SiameseNetModel( diff --git a/src/spectralnet/_trainers/_spectralnet_trainer.py b/src/spectralnet/_trainers/_spectralnet_trainer.py index 0f06b16..5dea323 100644 --- a/src/spectralnet/_trainers/_spectralnet_trainer.py +++ b/src/spectralnet/_trainers/_spectralnet_trainer.py @@ -1,7 +1,7 @@ import torch import torch.nn as nn import torch.optim as optim -from torch.utils.data import DataLoader, random_split, TensorDataset +from torch.utils.data import DataLoader, Dataset, TensorDataset, random_split from tqdm import trange from ._trainer import Trainer from .._utils import ( @@ -53,17 +53,16 @@ def __init__(self, config: dict, device: torch.device, is_sparse: bool = False): self.is_local_scale = self.spectral_config["is_local_scale"] def train( - self, X: torch.Tensor, y: torch.Tensor, siamese_net: nn.Module = None + self, dataset: Dataset, siamese_net: nn.Module = None ) -> SpectralNetModel: """ Train the SpectralNet model. Parameters ---------- - X : torch.Tensor - The dataset to train on. - y : torch.Tensor, optional - The labels of the dataset in case there are any. + dataset : torch.utils.data.Dataset + Dataset whose items are ``(x_flat, y)`` pairs. Can be an + in-memory ``TensorDataset`` or any disk-streaming Dataset. siamese_net : nn.Module, optional The siamese network to use for computing the affinity matrix. @@ -71,22 +70,15 @@ def train( ------- SpectralNetModel The trained SpectralNet model. - - Notes - ----- - This function trains the SpectralNet model using the provided dataset (`X`) and labels (`y`). - If labels are not provided (`y` is None), unsupervised training is performed. - The siamese network (`siamese_net`) is an optional parameter used for computing the affinity matrix. - The trained SpectralNet model is returned as the output. """ - self.X = X.view(X.size(0), -1) - self.y = y + self._dataset = dataset self.counter = 0 self.siamese_net = siamese_net self.criterion = SpectralNetLoss() + x0, _ = dataset[0] self.spectral_net = SpectralNetModel( - self.architecture, input_dim=self.X.shape[1] + self.architecture, input_dim=x0.numel() ).to(self.device) self.optimizer = optim.Adam(self.spectral_net.parameters(), lr=self.lr) @@ -201,12 +193,10 @@ def _get_data_loader(self) -> tuple: Returns: tuple: The data loaders """ - if self.y is None: - self.y = torch.zeros(len(self.X)) - train_size = int(0.9 * len(self.X)) - valid_size = len(self.X) - train_size - dataset = TensorDataset(self.X, self.y) - train_dataset, valid_dataset = random_split(dataset, [train_size, valid_size]) + n = len(self._dataset) + train_size = int(0.9 * n) + valid_size = n - train_size + train_dataset, valid_dataset = random_split(self._dataset, [train_size, valid_size]) train_loader = DataLoader( train_dataset, batch_size=self.batch_size, shuffle=True ) From d2a1e8b4cb60152cf834b83c4e13518dfcaa4a87 Mon Sep 17 00:00:00 2001 From: AmitaiYacobi Date: Sat, 7 Mar 2026 19:45:47 +0200 Subject: [PATCH 09/10] docs: update README with Dataset-based large-scale usage pattern - Split Usage section into "small datasets (in-memory tensor)" and "large datasets (streaming from disk)" - Add a concrete ImageFolderDataset example showing how to define a custom Dataset for 10 M+ images on disk, pass it to fit(), and stream predictions through a DataLoader - Note the Siamese-training memory caveat and the two workarounds (use_approx=True, or pass a subset) - Tighten the metrics example (f-string, y_np variable) - Clean up the Running examples section --- README.md | 88 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 66 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index bb54bda..296a5b1 100644 --- a/README.md +++ b/README.md @@ -43,44 +43,88 @@ pixi shell ## Usage -### Clustering +### Clustering — small datasets (in-memory tensor) -The basic functionality is quite intuitive and easy to use, e.g., +For datasets that fit in RAM, pass a `torch.Tensor` directly: ```python from spectralnet import SpectralNet spectralnet = SpectralNet(n_clusters=10) -spectralnet.fit(X) # X is the dataset and it should be a torch.Tensor -cluster_assignments = spectralnet.predict(X) # Get the final assignments to clusters +spectralnet.fit(X) # X: torch.Tensor of shape (N, ...) +cluster_assignments = spectralnet.predict(X) ``` -If you have labels to your dataset and you want to measure ACC and NMI you can do the following: +To measure ACC and NMI when labels are available: ```python -from spectralnet import SpectralNet -from spectralnet import Metrics - +from spectralnet import SpectralNet, Metrics spectralnet = SpectralNet(n_clusters=2) -spectralnet.fit(X, y) # X is the dataset and it should be a torch.Tensor -cluster_assignments = spectralnet.predict(X) # Get the final assignments to clusters - -y = y_train.detach().cpu().numpy() # In case your labels are of torch.Tensor type. -acc_score = Metrics.acc_score(cluster_assignments, y, n_clusters=2) -nmi_score = Metrics.nmi_score(cluster_assignments, y) -print(f"ACC: {np.round(acc_score, 3)}") -print(f"NMI: {np.round(nmi_score, 3)}") +spectralnet.fit(X, y) # y: integer label tensor +cluster_assignments = spectralnet.predict(X) + +y_np = y.detach().cpu().numpy() +acc_score = Metrics.acc_score(cluster_assignments, y_np, n_clusters=2) +nmi_score = Metrics.nmi_score(cluster_assignments, y_np) +print(f"ACC: {acc_score:.3f} NMI: {nmi_score:.3f}") ``` -You can read the code docs for more information and functionalities
+### Clustering — large datasets (streaming from disk) + +For datasets too large to hold in RAM (e.g. millions of images on disk), +define a `torch.utils.data.Dataset` that loads **one sample at a time** +and pass it to `fit()`. Nothing large ever lives in memory at once — every +trainer pulls mini-batches through its own `DataLoader` internally. -#### Running examples +```python +from torch.utils.data import Dataset, DataLoader +from spectralnet import SpectralNet +from PIL import Image +import torchvision.transforms as T +import os + +class ImageFolderDataset(Dataset): + def __init__(self, root): + self.paths = [ + os.path.join(root, f) for f in os.listdir(root) if f.endswith(".jpg") + ] + self.transform = T.Compose([T.Resize(64), T.ToTensor(), T.Normalize(0.5, 0.5)]) + + def __len__(self): + return len(self.paths) + + def __getitem__(self, idx): + return self.transform(Image.open(self.paths[idx]).convert("RGB")) + +dataset = ImageFolderDataset("/path/to/images") + +spectralnet = SpectralNet( + n_clusters=10, + should_use_ae=True, # compress images before clustering + ae_hiddens=[2048, 512, 64, 10], + spectral_hiddens=[512, 512, 10], +) +spectralnet.fit(dataset) -In order to run the model on twomoons or MNIST datasets, you should first cd to the examples folder and then run:
-`python3 cluster_twomoons.py`
-or
-`python3 cluster_mnist.py` +# predict() also accepts a DataLoader for large test sets +test_loader = DataLoader(dataset, batch_size=512, shuffle=False) +cluster_assignments = spectralnet.predict(test_loader) +``` + +> **Note on Siamese training with large datasets:** the Siamese network +> builds exact k-NN pairs, which requires loading all features into memory. +> For very large datasets either disable it (`should_use_siamese=False`), +> enable approximate neighbours (`siamese_use_approx=True`), or pass a +> representative subset as the Dataset. + +### Running examples + +```bash +cd examples +python3 cluster_twomoons.py +python3 cluster_mnist.py +```