diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 80fb0c02..53e317ec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ concurrency: jobs: test-precommit: - runs-on: ubuntu-22.04-16core + runs-on: ubuntu-24.04 steps: - name: Checkout code @@ -32,20 +32,11 @@ jobs: cache-write: ${{ github.event_name == 'push' && github.ref_name == 'main' }} - name: Check formatting and typing - run: | - set -e - # pyright seems to do something weird at initialization that causes it to error out - # We can ignore the first invocation here. - pyright esm/__init__.py || true - pre-commit install - env NODE_OPTIONS="--max-old-space-size=16384" pre-commit run --all-files --show-diff-on-failure - [ -z "$(git status --porcelain)" ] && true || (echo "❌❌❌ pre-commit hook failed! A few files changed ❌❌❌]" && git status --porcelain && false) - git reset --hard HEAD # test without the pre-commit changes - shell: pixi run bash -e {0} + run: pixi run lint-all test-esm: - runs-on: ubuntu-22.04-16core + runs-on: ubuntu-24.04 steps: - name: Checkout code @@ -59,20 +50,18 @@ jobs: cache-write: ${{ github.event_name == 'push' && github.ref_name == 'main' }} - name: Run tests - run: | - set -o pipefail - pytest -v --junitxml=pytest.xml tests/ | tee pytest-coverage.txt - shell: pixi run bash -e {0} + run: pixi run cov-test - name: Run Docker tests env: DOCKER_TAG: ${{ github.sha }} FORGE_URL: https://forge.evolutionaryscale.ai/ + ESM3_FORGE_TOKEN: ${{ secrets.ESM3_FORGE_TOKEN }} run: | set -e cd tests make build-oss-ci - make start-docker-oss URL=${{ env.FORGE_URL }} DOCKER_TAG=${{ env.DOCKER_TAG }} ESM3_FORGE_TOKEN=${{ secrets.ESM3_FORGE_TOKEN }} + make start-docker-oss URL=${{ env.FORGE_URL }} DOCKER_TAG=${{ env.DOCKER_TAG }} ESM3_FORGE_TOKEN=${{ env.ESM3_FORGE_TOKEN }} shell: pixi run bash -e {0} - name: cleanup docker containers if they're hanging diff --git a/.gitignore b/.gitignore index 9e83a30d..2c904dd0 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ esm.egg-info # pixi environments .pixi *.egg-info +*.pyc diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..269ea33b --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,33 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +exclude: (fasta|pdb|cif|mds|json)$ +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.2.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + exclude: pixi.lock + - id: check-merge-conflict +- repo: https://github.com/seddonym/import-linter + rev: v1.12.1 + hooks: + - id: import-linter +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.7.3 + hooks: + - id: ruff # linter + args: [ --fix ] + - id: ruff-format # formatter + types_or: [python, jupyter] +- repo: https://github.com/RobertCraigie/pyright-python + rev: v1.1.399 + hooks: + - id: pyright + name: pyright + entry: pyright + language: system + types: [python] + pass_filenames: true # For speed, we only check the files that are changed diff --git a/README.md b/README.md index 1d9b25c1..62cb57a9 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ - [Installation ](#installation-) +- [Available Models](#available-models-) - [ESM 3](#esm-3-) - [Quickstart for ESM3 Open](#esm3-quickstart-) - [ESM3 98B via Forge API](#esm3-forge) @@ -33,6 +34,31 @@ To get started with ESM, install the python library using pip: pip install esm ``` +## Available Models + +### ESM 3 Family + +| Model | Model Size | Release Date | Note | +|-------|------------|--------------|------| +| **Flagship Models** | | | Most users will be interested in using one of these models. | +| esm3-large-2024-03 | 98B | 2024-03 | | +| esm3-medium-2024-08 | 7B | 2024-08 | | +| esm3-small-2024-08 | 1.4B | 2024-08 | | +| **Published Models** | | | These models were used to generate all of the results in the ESM3 paper and are provided to facilitate reproducibility. | +| esm3-large-2024-03 | 98B | 2024-03 | | +| esm3-medium-2024-03 | 7B | 2024-03 | | +| esm3-small-2024-03 | 1.4B | 2024-03 | | +| **Experimental Models** | | | These models are provided for early use by researchers and are still under development. | +| esm3-medium-multimer-2024-09 | 7B | 2024-09 | | + +### ESM C Models + +| Model | Model Size | Number of Layers | Release Date | +|-------|------------|------------------|--------------| +| esmc-6b-2024-12 | 6B | 80 | 2024-12 | +| esmc-600m-2024-12 | 600M | 36 | 2024-12 | +| esmc-300m-2024-12 | 300M | 30 | 2024-12 | + ## ESM 3 [ESM3](https://www.evolutionaryscale.ai/papers/esm3-simulating-500-million-years-of-evolution-with-a-language-model) is a frontier generative model for biology, able to jointly reason across three fundamental biological properties of proteins: sequence, structure, and function. These three data modalities are represented as tracks of discrete tokens at the input and output of ESM3. You can present the model with a combination of partial inputs across the tracks, and ESM3 will provide output predictions for all the tracks. diff --git a/cookbook/local/open_generate.ipynb b/cookbook/local/open_generate.ipynb index c8cec4c1..32a72c38 100644 --- a/cookbook/local/open_generate.ipynb +++ b/cookbook/local/open_generate.ipynb @@ -38,6 +38,7 @@ "\n", "!pip install py3Dmol\n", "import py3Dmol\n", + "\n", "from esm.models.esm3 import ESM3\n", "from esm.sdk.api import ESMProtein, GenerationConfig\n", "from esm.utils.structure.protein_chain import ProteinChain" diff --git a/cookbook/local/raw_forwards.py b/cookbook/local/raw_forwards.py index c62f1734..baad28ee 100644 --- a/cookbook/local/raw_forwards.py +++ b/cookbook/local/raw_forwards.py @@ -13,9 +13,7 @@ from esm.tokenization.function_tokenizer import ( InterProQuantizedTokenizer as EsmFunctionTokenizer, ) -from esm.tokenization.sequence_tokenizer import ( - EsmSequenceTokenizer, -) +from esm.tokenization.sequence_tokenizer import EsmSequenceTokenizer from esm.utils.structure.protein_chain import ProteinChain from esm.utils.types import FunctionAnnotation diff --git a/cookbook/tutorials/1_esmprotein.ipynb b/cookbook/tutorials/1_esmprotein.ipynb index e143ff13..13017733 100644 --- a/cookbook/tutorials/1_esmprotein.ipynb +++ b/cookbook/tutorials/1_esmprotein.ipynb @@ -72,6 +72,7 @@ "outputs": [], "source": [ "from biotite.database import rcsb\n", + "\n", "from esm.sdk.api import ESMProtein\n", "from esm.utils.structure.protein_chain import ProteinChain\n", "from esm.utils.types import FunctionAnnotation\n", @@ -496,9 +497,10 @@ "# Functions for visualizing InterPro function annotations\n", "\n", "from dna_features_viewer import GraphicFeature, GraphicRecord\n", - "from esm.utils.function.interpro import InterPro, InterProEntryType\n", "from matplotlib import colormaps\n", "\n", + "from esm.utils.function.interpro import InterPro, InterProEntryType\n", + "\n", "\n", "def visualize_function_annotations(\n", " annotations: list[FunctionAnnotation],\n", diff --git a/cookbook/tutorials/3_gfp_design.ipynb b/cookbook/tutorials/3_gfp_design.ipynb index d5157e64..bde09ad5 100644 --- a/cookbook/tutorials/3_gfp_design.ipynb +++ b/cookbook/tutorials/3_gfp_design.ipynb @@ -64,6 +64,7 @@ "import matplotlib.pyplot as pl\n", "import py3Dmol\n", "import torch\n", + "\n", "from esm.sdk import client\n", "from esm.sdk.api import ESMProtein, GenerationConfig\n", "from esm.utils.structure.protein_chain import ProteinChain" diff --git a/cookbook/tutorials/4_forge_generate.ipynb b/cookbook/tutorials/4_forge_generate.ipynb index 1e61f629..7570fda9 100644 --- a/cookbook/tutorials/4_forge_generate.ipynb +++ b/cookbook/tutorials/4_forge_generate.ipynb @@ -36,6 +36,7 @@ "\n", "!pip install py3Dmol\n", "import py3Dmol\n", + "\n", "from esm.sdk import client\n", "from esm.sdk.api import ESMProtein, GenerationConfig\n", "from esm.utils.structure.protein_chain import ProteinChain" diff --git a/cookbook/tutorials/5_guided_generation.ipynb b/cookbook/tutorials/5_guided_generation.ipynb index 36fa6fbb..f04d6be3 100644 --- a/cookbook/tutorials/5_guided_generation.ipynb +++ b/cookbook/tutorials/5_guided_generation.ipynb @@ -14,13 +14,13 @@ "3. Minimize a biophysical energy function\n", "4. Use experimental screening data to guide designs with a regression model\n", "\n", - "As long as your scoring function takes a protein as input and outputs a single score, you can use it to guide designs. To accomplish this, we use an implementation of derivative-free guidance inspired by Soft Value-Based Decoding described in [Li, et al 2024](https://arxiv.org/abs/2408.08252).\n", + "As long as your scoring function takes a protein as input and outputs a single score, you can use it to guide designs. To accomplish this, we use an implementation of derivative-free guidance inspired by Soft Value-Based Decoding described in [Li, et al 2024](https://arxiv.org/abs/2408.08252) and constrained optimization using the Modified Differential Method of Multipliers from [Platt & Barr 1987](https://proceedings.neurips.cc/paper_files/paper/1987/file/a1126573153ad7e9f44ba80e99316482-Paper.pdf)\n", "\n", "In this notebook we will walk through a few examples to illustrate how to use guided generation. \n", "\n", "1. Guide towards high pTM for improved generation quality\n", "2. Generate a protein with no cysteine (C) residues\n", - "3. Maximize protein globularity by minimizing the radius of gyration\n", + "3. Maximize protein globularity by minimizing the radius of gyration, while keeping pTM high\n", "\n" ] }, @@ -49,6 +49,7 @@ "source": [ "import biotite.structure as bs\n", "import py3Dmol\n", + "\n", "from esm.sdk.api import ESMProtein, GenerationConfig\n", "from esm.sdk.experimental import ESM3GuidedDecoding, GuidedDecodingScoringFunction" ] @@ -269,6 +270,11 @@ "metadata": {}, "outputs": [], "source": [ + "# Start from a fully masked protein\n", + "PROTEIN_LENGTH = 256\n", + "starting_protein = ESMProtein(sequence=\"_\" * PROTEIN_LENGTH)\n", + "\n", + "# Call guided_generate\n", "no_cysteine_protein = no_cysteine_guided_decoding.guided_generate(\n", " protein=starting_protein,\n", " num_decoding_steps=len(starting_protein) // 8,\n", @@ -302,7 +308,20 @@ "source": [ "## Maximize Globularity\n", "\n", - "We use the radius of gyration as a proxy to maximize globularity, we also encourage generations to have high pTM" + "We use the radius of gyration as a proxy to maximize globularity, and we will also encourage generations to have high pTM by using constraints" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from esm.sdk.experimental import (\n", + " ConstraintType,\n", + " ESM3GuidedDecodingWithConstraints,\n", + " GenerationConstraint,\n", + ")" ] }, { @@ -313,12 +332,11 @@ "source": [ "class RadiousOfGyrationScoringFunction(GuidedDecodingScoringFunction):\n", " def __call__(self, protein: ESMProtein) -> float:\n", + " # Use the negative radius of gyration as the score to maximize\n", " score = -1 * self.radius_of_gyration(protein)\n", "\n", - " assert protein.ptm is not None, \"Protein must have pTM scores to be scored\"\n", - " if protein.ptm < 0.5:\n", - " # Penalize proteins with low pTM scores\n", - " score = score * 2\n", + " # Re-scale the score to be in a similar magnitude as pTM\n", + " score = score / 100.0\n", "\n", " return score\n", "\n", @@ -335,8 +353,19 @@ "metadata": {}, "outputs": [], "source": [ - "radius_guided_decoding = ESM3GuidedDecoding(\n", - " client=model, scoring_function=RadiousOfGyrationScoringFunction()\n", + "# Constrain generation to have pTM > 0.75\n", + "ptm_constraint = GenerationConstraint(\n", + " scoring_function=PTMScoringFunction(),\n", + " constraint_type=ConstraintType.GREATER_EQUAL,\n", + " value=0.75,\n", + ")\n", + "\n", + "radius_guided_decoding = ESM3GuidedDecodingWithConstraints(\n", + " client=model,\n", + " scoring_function=RadiousOfGyrationScoringFunction(),\n", + " constraints=[ptm_constraint], # Add list of constraints\n", + " damping=1.0, # Damping factor for the MMDM algorithm\n", + " learning_rate=10.0, # Learning rate for the MMDM algorithm\n", ")" ] }, @@ -346,6 +375,11 @@ "metadata": {}, "outputs": [], "source": [ + "# Start from a fully masked protein\n", + "PROTEIN_LENGTH = 256\n", + "starting_protein = ESMProtein(sequence=\"_\" * PROTEIN_LENGTH)\n", + "\n", + "# Call guided_generate\n", "radius_guided_protein = radius_guided_decoding.guided_generate(\n", " protein=starting_protein,\n", " num_decoding_steps=len(starting_protein) // 8,\n", @@ -359,11 +393,32 @@ "metadata": {}, "outputs": [], "source": [ + "# Visualize the trajectory of the constrained generation\n", + "radius_guided_decoding.visualize_latest_trajectory()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Visualize the generated protein\n", "view = py3Dmol.view(width=800, height=400)\n", "view.addModel(radius_guided_protein.to_pdb_string(), \"pdb\")\n", "view.setStyle({\"cartoon\": {\"color\": \"spectrum\"}})\n", "view.zoomTo()" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Check pTM\n", + "radius_guided_protein.ptm" + ] } ], "metadata": { diff --git a/esm/__init__.py b/esm/__init__.py index fa986f1e..1da6a555 100644 --- a/esm/__init__.py +++ b/esm/__init__.py @@ -1,2 +1 @@ __version__ = "3.2.1" - diff --git a/esm/layers/attention.py b/esm/layers/attention.py index afd88e37..564ef90c 100644 --- a/esm/layers/attention.py +++ b/esm/layers/attention.py @@ -5,15 +5,12 @@ import torch.nn.functional as F from torch import nn -from esm.layers.rotary import ( - RotaryEmbedding, - TritonRotaryEmbedding, -) +from esm.layers.rotary import RotaryEmbedding, TritonRotaryEmbedding try: - from flash_attn import flash_attn_varlen_qkvpacked_func # type:ignore -except ImportError: - flash_attn_varlen_func = None + from flash_attn import flash_attn_varlen_qkvpacked_func +except (ImportError, RuntimeError): + flash_attn_varlen_qkvpacked_func = None class MultiHeadAttention(nn.Module): @@ -117,7 +114,7 @@ def forward(self, x, seq_id): ) qkv_N3HD = self.rotary(qkv_N3HD, cu_seqlens, max_seqlen) - context_NHD = flash_attn_varlen_qkvpacked_func( + context_NHD = flash_attn_varlen_qkvpacked_func( # type: ignore qkv_N3HD, cu_seqlens, max_seqlen, softmax_scale=self.d_head**-0.5 ) context_ND = einops.rearrange(context_NHD, "n h d -> n (h d)") diff --git a/esm/layers/blocks.py b/esm/layers/blocks.py index 593b277f..76ebbe06 100644 --- a/esm/layers/blocks.py +++ b/esm/layers/blocks.py @@ -2,13 +2,8 @@ import torch.nn as nn import torch.nn.functional as F -from esm.layers.attention import ( - FlashMultiHeadAttention, - MultiHeadAttention, -) -from esm.layers.geom_attention import ( - GeometricReasoningOriginalImpl, -) +from esm.layers.attention import FlashMultiHeadAttention, MultiHeadAttention +from esm.layers.geom_attention import GeometricReasoningOriginalImpl from esm.utils.structure.affine3d import Affine3D diff --git a/esm/layers/structure_proj.py b/esm/layers/structure_proj.py index 783ddeb4..faad0fe9 100644 --- a/esm/layers/structure_proj.py +++ b/esm/layers/structure_proj.py @@ -2,10 +2,7 @@ import torch.nn as nn from esm.utils.constants.physics import BB_COORDINATES -from esm.utils.structure.affine3d import ( - Affine3D, - RotationMatrix, -) +from esm.utils.structure.affine3d import Affine3D, RotationMatrix class Dim6RotStructureHead(nn.Module): diff --git a/esm/models/esm3.py b/esm/models/esm3.py index cbe02ddd..218a8e90 100644 --- a/esm/models/esm3.py +++ b/esm/models/esm3.py @@ -13,10 +13,7 @@ from esm.layers.regression_head import RegressionHead from esm.layers.transformer_stack import TransformerStack from esm.models.function_decoder import FunctionTokenDecoder -from esm.models.vqvae import ( - StructureTokenDecoder, - StructureTokenEncoder, -) +from esm.models.vqvae import StructureTokenDecoder, StructureTokenEncoder from esm.sdk.api import ( ESM3InferenceClient, ESMProtein, @@ -32,10 +29,7 @@ from esm.tokenization import TokenizerCollectionProtocol from esm.utils import encoding from esm.utils.constants import esm3 as C -from esm.utils.constants.models import ( - ESM3_OPEN_SMALL, - normalize_model_name, -) +from esm.utils.constants.models import ESM3_OPEN_SMALL, normalize_model_name from esm.utils.decoding import decode_protein_tensor from esm.utils.generation import ( _batch_forward, @@ -50,9 +44,7 @@ get_default_sampling_config, validate_sampling_config, ) -from esm.utils.structure.affine3d import ( - build_affine3d_from_coordinates, -) +from esm.utils.structure.affine3d import build_affine3d_from_coordinates @dataclass diff --git a/esm/models/function_decoder.py b/esm/models/function_decoder.py index c4f32992..e5f1fb28 100644 --- a/esm/models/function_decoder.py +++ b/esm/models/function_decoder.py @@ -12,9 +12,7 @@ from esm.layers.regression_head import RegressionHead from esm.layers.transformer_stack import TransformerStack -from esm.tokenization.function_tokenizer import ( - InterProQuantizedTokenizer, -) +from esm.tokenization.function_tokenizer import InterProQuantizedTokenizer from esm.utils.constants import esm3 as C from esm.utils.misc import merge_annotations, merge_ranges from esm.utils.types import FunctionAnnotation diff --git a/esm/models/vqvae.py b/esm/models/vqvae.py index 0f5226a4..37bc3945 100644 --- a/esm/models/vqvae.py +++ b/esm/models/vqvae.py @@ -7,10 +7,7 @@ from esm.layers.transformer_stack import TransformerStack from esm.utils.constants import esm3 as C from esm.utils.misc import knn_graph -from esm.utils.structure.affine3d import ( - Affine3D, - build_affine3d_from_coordinates, -) +from esm.utils.structure.affine3d import Affine3D, build_affine3d_from_coordinates from esm.utils.structure.predicted_aligned_error import ( compute_predicted_aligned_error, compute_tm, diff --git a/esm/pretrained.py b/esm/pretrained.py index b9121511..e452e1d2 100644 --- a/esm/pretrained.py +++ b/esm/pretrained.py @@ -6,14 +6,8 @@ from esm.models.esm3 import ESM3 from esm.models.esmc import ESMC from esm.models.function_decoder import FunctionTokenDecoder -from esm.models.vqvae import ( - StructureTokenDecoder, - StructureTokenEncoder, -) -from esm.tokenization import ( - get_esm3_model_tokenizers, - get_esmc_model_tokenizers, -) +from esm.models.vqvae import StructureTokenDecoder, StructureTokenEncoder +from esm.tokenization import get_esm3_model_tokenizers, get_esmc_model_tokenizers from esm.utils.constants.esm3 import data_root from esm.utils.constants.models import ( ESM3_FUNCTION_DECODER_V0, diff --git a/esm/sdk/api.py b/esm/sdk/api.py index 7d092fd7..0212ddcd 100644 --- a/esm/sdk/api.py +++ b/esm/sdk/api.py @@ -2,27 +2,19 @@ from abc import ABC from copy import deepcopy -from typing import List, Sequence +from typing import Sequence import attr import torch from attr import asdict, define import esm.utils.constants.api as C -from esm.tokenization import ( - TokenizerCollectionProtocol, - get_esm3_model_tokenizers, -) +from esm.tokenization import TokenizerCollectionProtocol, get_esm3_model_tokenizers from esm.utils import encoding from esm.utils.constants.models import ESM3_OPEN_SMALL -from esm.utils.misc import ( - get_chainbreak_boundaries_from_sequence, -) +from esm.utils.misc import get_chainbreak_boundaries_from_sequence from esm.utils.structure.protein_chain import ProteinChain -from esm.utils.structure.protein_complex import ( - SINGLE_LETTER_CHAIN_IDS, - ProteinComplex, -) +from esm.utils.structure.protein_complex import SINGLE_LETTER_CHAIN_IDS, ProteinComplex from esm.utils.types import FunctionAnnotation, PathOrBuffer @@ -43,7 +35,6 @@ class ESMProtein(ProteinType): plddt: torch.Tensor | None = None ptm: torch.Tensor | None = None - # When calling EvolutionaryScale API, use this flag to disclose any # sequences that may potentially have concerns. # Such sequences may not go through standard safety filter for approved users. @@ -79,12 +70,9 @@ def from_pdb( def from_protein_chain( cls, protein_chain: ProteinChain, with_annotations: bool = False ) -> ESMProtein: - # By default, we don't annotate with DSSP / SASA, which are expensive. - # If mkdssp is installed, we can annotate with a flag. if with_annotations: return ESMProtein( sequence=protein_chain.sequence, - secondary_structure=protein_chain.dssp().tolist(), sasa=protein_chain.sasa().tolist(), function_annotations=None, coordinates=torch.tensor(protein_chain.atom37_positions), @@ -123,7 +111,8 @@ def to_pdb(self, pdb_path: PathOrBuffer) -> None: protein_complex.to_pdb(pdb_path) def to_pdb_string(self) -> str: - protein_chain = self.to_protein_chain() + # Note: This was modified to match .to_pdb() behavior. We can revisit this at some point + protein_chain = self.to_protein_complex().infer_oxygen() return protein_chain.to_pdb_string() def to_protein_chain(self) -> ProteinChain: @@ -172,6 +161,7 @@ def to_protein_complex( if gt_chains is not None else SINGLE_LETTER_CHAIN_IDS[i], entity_id=gt_chains[i].entity_id if gt_chains is not None else None, + confidence=self.plddt[start:end] if self.plddt is not None else None, ) pred_chains.append(pred_chain) return ProteinComplex.from_chains(pred_chains) @@ -321,8 +311,6 @@ class InverseFoldingConfig: temperature: float = 1.0 - - ## Low Level Endpoint Types @define class SamplingTrackConfig: @@ -382,22 +370,23 @@ class LogitsConfig: # Embeddings. return_embeddings: bool = False return_hidden_states: bool = False + return_mean_embedding: bool = False + return_mean_hidden_states: bool = False ith_hidden_layer: int = -1 - - - @define class LogitsOutput: logits: ForwardTrackData | None = None embeddings: torch.Tensor | None = None + mean_embedding: torch.Tensor | None = None # Residue annotations is multi-hot, so deserves special treatment # It's not a categorical distribution, but instead a bernoulli, so # softmax across the last dimension is _wrong_ residue_annotation_logits: torch.Tensor | None = None hidden_states: torch.Tensor | None = None + mean_hidden_state: torch.Tensor | None = None @define diff --git a/esm/sdk/base_forge_client.py b/esm/sdk/base_forge_client.py index aa679bca..ff05b541 100644 --- a/esm/sdk/base_forge_client.py +++ b/esm/sdk/base_forge_client.py @@ -70,11 +70,13 @@ def __exit__(self, exc_type, exc_val, exc_tb): def prepare_request( self, request: dict[str, Any], - potential_sequence_of_concern: bool = False, + potential_sequence_of_concern: bool | None = None, return_bytes: bool = False, headers: dict[str, str] = {}, ) -> tuple[dict[str, Any], dict[str, str]]: - request["potential_sequence_of_concern"] = potential_sequence_of_concern + if potential_sequence_of_concern is not None: + request["potential_sequence_of_concern"] = potential_sequence_of_concern + headers = {**self.headers, **headers} if return_bytes: headers["return-bytes"] = "true" @@ -103,42 +105,58 @@ async def _async_post( self, endpoint, request, - potential_sequence_of_concern: bool = False, + potential_sequence_of_concern: bool | None = None, params: dict[str, Any] = {}, headers: dict[str, str] = {}, return_bytes: bool = False, ): - request, headers = self.prepare_request( - request, potential_sequence_of_concern, return_bytes, headers - ) - response = await self.async_client.post( - url=urljoin(self.url, f"/api/v1/{endpoint}"), - json=request, - params=params, - headers=headers, - timeout=self.request_timeout, - ) - data = self.prepare_data(response, endpoint) - return data + try: + request, headers = self.prepare_request( + request, potential_sequence_of_concern, return_bytes, headers + ) + response = await self.async_client.post( + url=urljoin(self.url, f"/api/v1/{endpoint}"), + json=request, + params=params, + headers=headers, + timeout=self.request_timeout, + ) + data = self.prepare_data(response, endpoint) + return data + except ESMProteinError as e: + raise e + except Exception as e: + raise ESMProteinError( + error_code=500, + error_msg=f"Failed to submit request to {endpoint}. Error: {e}", + ) def _post( self, endpoint, request, - potential_sequence_of_concern: bool = False, + potential_sequence_of_concern: bool | None = None, params: dict[str, Any] = {}, headers: dict[str, str] = {}, return_bytes: bool = False, ): - request, headers = self.prepare_request( - request, potential_sequence_of_concern, return_bytes, headers - ) - response = self.client.post( - url=urljoin(self.url, f"/api/v1/{endpoint}"), - json=request, - params=params, - headers=headers, - timeout=self.request_timeout, - ) - data = self.prepare_data(response, endpoint) - return data + try: + request, headers = self.prepare_request( + request, potential_sequence_of_concern, return_bytes, headers + ) + response = self.client.post( + url=urljoin(self.url, f"/api/v1/{endpoint}"), + json=request, + params=params, + headers=headers, + timeout=self.request_timeout, + ) + data = self.prepare_data(response, endpoint) + return data + except ESMProteinError as e: + raise e + except Exception as e: + raise ESMProteinError( + error_code=500, + error_msg=f"Failed to submit request to {endpoint}. Error: {e}", + ) diff --git a/esm/sdk/experimental/__init__.py b/esm/sdk/experimental/__init__.py new file mode 100644 index 00000000..d583d825 --- /dev/null +++ b/esm/sdk/experimental/__init__.py @@ -0,0 +1,14 @@ +from .constrained_generation import ( + ConstraintType, + ESM3GuidedDecodingWithConstraints, + GenerationConstraint, +) +from .guided_generation import ESM3GuidedDecoding, GuidedDecodingScoringFunction + +__all__ = [ + "ConstraintType", + "ESM3GuidedDecodingWithConstraints", + "GenerationConstraint", + "ESM3GuidedDecoding", + "GuidedDecodingScoringFunction", +] diff --git a/esm/sdk/experimental/constrained_generation.py b/esm/sdk/experimental/constrained_generation.py new file mode 100644 index 00000000..967b7519 --- /dev/null +++ b/esm/sdk/experimental/constrained_generation.py @@ -0,0 +1,324 @@ +from __future__ import annotations + +from dataclasses import dataclass, field +from enum import Enum +from typing import List + +import matplotlib.pyplot as plt +import numpy as np +import torch +from matplotlib.collections import LineCollection +from matplotlib.colors import Normalize +from tqdm import tqdm + +from esm.sdk import batch_executor +from esm.sdk.api import ( + ESM3InferenceClient, + ESMProtein, + ESMProteinError, + ESMProteinTensor, + SamplingConfig, + SamplingTrackConfig, +) +from esm.sdk.experimental.guided_generation import ( + ESM3GuidedDecoding, + GuidedDecodingScoringFunction, +) + + +class ConstraintType(Enum): + GREATER_EQUAL = "greater_equal" # f(x) ≥ threshold + LESS_EQUAL = "less_equal" # f(x) ≤ threshold + EQUAL = "equal" # f(x) = threshold + + +@dataclass(slots=True) +class GenerationConstraint: + """ + A single inequality or equality constraint. + + Parameters + ---------- + scoring_function + Maps a protein ➜ real value (e.g. pTM, length, …). + value + Target value for inequality or equality constraint. + constraint_type + Type of constraint to apply. + - GREATER_EQUAL: f(x) ≥ value + - LESS_EQUAL: f(x) ≤ value + - EQUAL: f(x) = value (equality) + """ + + scoring_function: GuidedDecodingScoringFunction + value: float + constraint_type: ConstraintType = ConstraintType.GREATER_EQUAL + lambda_: float = field(default=0.0, init=False) # dual variable (MDMM) + + def g(self, x: float) -> float: + """ + Canonical form: + + • inequalities → g(x) ≤ 0 + • equalities → h(x) (we still return a scalar, no ≤) + """ + if self.constraint_type is ConstraintType.GREATER_EQUAL: + return self.value - x + if self.constraint_type is ConstraintType.LESS_EQUAL: + return x - self.value + # equality: h(x) = x - value (we will *not* project λ) + return x - self.value + + def update_lambda(self, g: float, eta: float, gamma: float) -> None: + """ + Update the dual variable λ according to the MDMM update rule. + """ + if self.constraint_type is ConstraintType.EQUAL: + self.lambda_ += eta * g # no projection for equality constraints + else: + self.lambda_ = max(0.0, self.lambda_ + eta * g) + + def copy(self) -> GenerationConstraint: + """ + Create a copy of this constraint. + """ + c = GenerationConstraint( + scoring_function=self.scoring_function, + value=self.value, + constraint_type=self.constraint_type, + ) + c.lambda_ = self.lambda_ # copy the dual variable + return c + + +class ESM3GuidedDecodingWithConstraints(ESM3GuidedDecoding): + """ + Derivative-free guided decoding with constraints. + + Uses the Modified Differential Method of Multipliers (MDMM) to + guarantee convergence to the constrained optimum without + hand-tuning penalty weights. + + References: + [1] Platt, John, and Alan Barr. "Constrained differential optimization." Neural Information Processing Systems. 1987. + [2] https://www.engraved.blog/how-we-can-make-machine-learning-algorithms-tunable/ + """ + + def __init__( + self, + client: ESM3InferenceClient, + scoring_function: GuidedDecodingScoringFunction, + constraints: GenerationConstraint | list[GenerationConstraint], + *, + damping: float = 10.0, + learning_rate: float = 1.0, + ): + super().__init__(client, scoring_function) + + if isinstance(constraints, GenerationConstraint): + constraints = [constraints] + + self.constraints = [c.copy() for c in constraints] + self.gamma = float(damping) + self.eta = float(learning_rate) + + self.recorder: TrajectoryRecorder | None = None + + def guided_generate( + self, + protein: ESMProtein, + num_decoding_steps: int, + num_samples_per_step: int, + denoised_prediction_temperature: float = 0.0, + track: str = "sequence", + verbose: bool = True, + ) -> ESMProtein: + # Reset the trajectory recorder + self.recorder = TrajectoryRecorder() + + protein_tensor = self.client.encode(protein) + assert not isinstance(protein_tensor, ESMProteinError) + + if track == "structure": + protein_tensor = self.maybe_add_default_structure_tokens(protein_tensor) + + n_masked = self.get_number_of_masked_positions(protein_tensor, track=track) + n_unmask = n_masked // num_decoding_steps + + best_reward = float("-inf") + + if verbose: + pbar = tqdm(range(num_decoding_steps), desc="S=-inf λ=0.00") + else: + pbar = range(num_decoding_steps) + + for step in pbar: + # Last iteration: unmask whatever is left + if step == num_decoding_steps - 1: + n_unmask = self.get_number_of_masked_positions( + protein_tensor, track=track + ) + + # ---------- propose & evaluate M samples (parallel-safe) ---- # + def _propose_and_eval(pt: ESMProteinTensor): + new_pt = self.randomly_unmask_positions(pt, n_unmask, track=track) + reward, g_val, raw_vals = self._score_and_constraints( + new_pt, denoised_prediction_temperature + ) + return new_pt, reward, g_val, raw_vals + + if self._use_batch_executor: + with batch_executor(show_progress=False) as ex: + results = ex.execute_batch( + user_func=_propose_and_eval, + pt=[protein_tensor] * num_samples_per_step, + ) + + if isinstance(results, Exception): + raise results + samples, rewards, gh_lists, val_lists = zip(*results) # type: ignore + else: + samples, rewards, gh_lists, val_lists = [], [], [], [] + for _ in range(num_samples_per_step): + s, r, g, c = _propose_and_eval(protein_tensor) + samples.append(s) + rewards.append(r) + gh_lists.append(g) + val_lists.append(c) + + # -------- compute MDMM lagrangian for each sample ----------- + lags = [self._lagrangian(r, ghs) for r, ghs in zip(rewards, gh_lists)] + + best_idx = int(torch.tensor(lags).argmin()) + protein_tensor = samples[best_idx] + best_reward = rewards[best_idx] + best_g_vals = gh_lists[best_idx] + + # -------- dual updates (MDMM) ----------------- + for g, c in zip(best_g_vals, self.constraints): + c.update_lambda(g, self.eta, self.gamma) + + self.recorder.log( + step=step, + reward=best_reward, + g_list=best_g_vals, + lambda_list=[c.lambda_ for c in self.constraints], + ) + + if verbose and isinstance(pbar, tqdm): + lam_str = ", ".join( + f"λ_{i}={c.lambda_:.2f}" for i, c in enumerate(self.constraints) + ) + pbar.set_description(f"S={best_reward:+.3f} {lam_str}") + + final = self.client.forward_and_sample( + protein_tensor, + sampling_configuration=SamplingConfig( + sequence=SamplingTrackConfig(temperature=0.0), + structure=SamplingTrackConfig(temperature=0.0), + ), + ) + assert not isinstance(final, ESMProteinError) + decoded = self.client.decode(final.protein_tensor) + assert not isinstance(decoded, ESMProteinError) + return decoded + + def visualize_latest_trajectory( + self, constraint_idx: int = 0, cmap: str = "viridis" + ) -> None: + """ + Visualise the trajectory of the latest optimisation run. + If you optimise multiple constraints, pick which one to plot via `constraint_idx`. + """ + if not self.recorder: + raise ValueError("No trajectory recorder available.") + + steps, g_vals, rewards = self.recorder.as_arrays(constraint_idx) + self.recorder.plot_line(constraint_idx=constraint_idx, cmap=cmap) + + def _score_and_constraints( + self, pt: ESMProteinTensor, temp: float + ) -> tuple[float, list[float], list[float]]: + protein = self.predict_denoised(pt, temperature=temp) + reward = self.scoring_function(protein) + vals, ghs = [], [] + for c in self.constraints: + val = c.scoring_function(protein) + vals.append(val) + ghs.append(c.g(val)) + return reward, ghs, vals + + def _lagrangian(self, reward: float, g_vals: list[float]) -> float: + """ + MDMM L(x, λ) = -reward + Σ_i (λ_i - γ g_i) * g_i + (reward is to be maximised ⇒ we minimise -reward) + """ + lag = -reward + for g, c in zip(g_vals, self.constraints): + lag += (c.lambda_ - self.gamma * g) * g + return lag + + +@dataclass +class TrajectoryRecorder: + steps: List[int] = field(default_factory=list) + rewards: List[float] = field(default_factory=list) + g_vals: List[List[float]] = field( + default_factory=list + ) # each step → list of constraints + lambdas: List[List[float]] = field(default_factory=list) # each step → list of λ s + + def log( + self, step: int, reward: float, g_list: list[float], lambda_list: list[float] + ) -> None: + """Append one optimisation step to the trajectory.""" + self.steps.append(step) + self.rewards.append(reward) + self.g_vals.append(list(g_list)) + self.lambdas.append(list(lambda_list)) + + def as_arrays(self, constraint_idx: int = 0): + """ + Return numpy arrays suitable for plotting. + If you optimise multiple constraints, pick which one to plot via `constraint_idx`. + """ + return ( + np.asarray(self.steps), + np.asarray([g[constraint_idx] for g in self.g_vals]), + np.asarray(self.rewards), + ) + + def plot_line(self, constraint_idx: int = 0, cmap: str = "viridis"): + """ + Continuous line with markers and a colour-gradient that follows the optimisation step. + """ + steps, x_vals, y_vals = self.as_arrays(constraint_idx) + + # build coloured line segments + points = np.column_stack([x_vals, y_vals]) + segments = np.concatenate([points[:-1, None, :], points[1:, None, :]], axis=1) + + norm = Normalize(vmin=steps.min(), vmax=steps.max()) + lc = LineCollection(segments, cmap=cmap, norm=norm, linewidth=2) # type: ignore + lc.set_array(steps) + + fig, ax = plt.subplots() + ax.add_collection(lc) + ax.scatter( + x_vals, + y_vals, + c=steps, + cmap=cmap, + norm=norm, + marker="o", + edgecolor="k", + zorder=3, + ) + + ax.axvline(0, linestyle="--", color="grey") + ax.set_xlabel("constraint value g(x) (≤ 0 is feasible)") + ax.set_ylabel("reward R(x)") + ax.set_title("Trajectory in constraint–reward space") + plt.colorbar(lc, label="optimisation step") + plt.tight_layout() + plt.show() diff --git a/esm/sdk/experimental.py b/esm/sdk/experimental/guided_generation.py similarity index 98% rename from esm/sdk/experimental.py rename to esm/sdk/experimental/guided_generation.py index 39ad6703..9acd32e8 100644 --- a/esm/sdk/experimental.py +++ b/esm/sdk/experimental/guided_generation.py @@ -44,7 +44,7 @@ def __init__( self._use_batch_executor = True else: raise ValueError( - "client must be an instance of ESM3 or ESM3ForgeInferenceClient" + f"client must be an instance of ESM3 or ESM3ForgeInferenceClient. Got {type(client)}" ) self.client = client diff --git a/esm/sdk/forge.py b/esm/sdk/forge.py index d9244993..e9bb2e3d 100644 --- a/esm/sdk/forge.py +++ b/esm/sdk/forge.py @@ -1,15 +1,10 @@ import asyncio import base64 -import inspect import pickle from concurrent.futures import ThreadPoolExecutor -from contextvars import ContextVar -from functools import wraps -from typing import Any, Literal, Sequence, cast +from typing import Any, Sequence import torch -from attr import asdict -from tenacity import retry, retry_if_result, stop_after_attempt, wait_exponential from esm.sdk.api import ( MSA, @@ -29,17 +24,11 @@ SamplingTrackConfig, ) from esm.sdk.base_forge_client import _BaseForgeInferenceClient +from esm.sdk.retry import retry_decorator from esm.utils.constants.api import MIMETYPE_ES_PICKLE -from esm.utils.misc import ( - deserialize_tensors, - maybe_list, - maybe_tensor, -) -from esm.utils.sampling import validate_sampling_config +from esm.utils.misc import deserialize_tensors, maybe_list, maybe_tensor from esm.utils.types import FunctionAnnotation -skip_retries_var = ContextVar("skip_retries", default=False) - def _list_to_function_annotations(l) -> list[FunctionAnnotation] | None: if l is None or len(l) <= 0: @@ -47,21 +36,17 @@ def _list_to_function_annotations(l) -> list[FunctionAnnotation] | None: return [FunctionAnnotation(*t) for t in l] -def retry_if_specific_error(exception): - """ - We only retry on specific errors. - Currently we retry for 502 (bad gateway) and 429 (rate limit) - """ - return isinstance(exception, ESMProteinError) and exception.error_code in { - 429, - 502, - 504, - } +def _maybe_logits(data: dict[str, Any], track: str, return_bytes: bool = False): + ret = data.get("logits", {}).get(track, None) + # TODO(s22chan): just return this when removing return_bytes + return ret if ret is None or not return_bytes else maybe_tensor(ret) -def log_retry_attempt(retry_state): - print( - f"Retrying... Attempt {retry_state.attempt_number} after {retry_state.next_action.sleep}s due to: {retry_state.outcome.result()}" +def _maybe_b64_decode(obj, return_bytes: bool): + return ( + deserialize_tensors(base64.b64decode(obj)) + if return_bytes and isinstance(obj, str) + else obj ) @@ -77,45 +62,6 @@ def _validate_protein_tensor_input(input): ) -def retry_decorator(func): - """ - A static method that returns a retry decorator. This decorator uses the - instance's retry settings. - """ - - @wraps(func) - async def async_wrapper(instance, *args, **kwargs): - if skip_retries_var.get(): - return await func(instance, *args, **kwargs) - retry_decorator = retry( - retry=retry_if_result(retry_if_specific_error), - wait=wait_exponential( - multiplier=1, min=instance.min_retry_wait, max=instance.max_retry_wait - ), - stop=stop_after_attempt(instance.max_retry_attempts), - before_sleep=log_retry_attempt, - ) - # Apply the retry decorator to the function - return await retry_decorator(func)(instance, *args, **kwargs) - - @wraps(func) - def wrapper(instance, *args, **kwargs): - if skip_retries_var.get(): - return func(instance, *args, **kwargs) - retry_decorator = retry( - retry=retry_if_result(retry_if_specific_error), - wait=wait_exponential( - multiplier=1, min=instance.min_retry_wait, max=instance.max_retry_wait - ), - stop=stop_after_attempt(instance.max_retry_attempts), - before_sleep=log_retry_attempt, - ) - # Apply the retry decorator to the function - return retry_decorator(func)(instance, *args, **kwargs) - - return async_wrapper if inspect.iscoroutinefunction(func) else wrapper - - class SequenceStructureForgeInferenceClient(_BaseForgeInferenceClient): def __init__( self, @@ -147,13 +93,9 @@ def __init__( ) @staticmethod - def _process_fold_request( - sequence: str, - model_name: str | None, - ): + def _process_fold_request(sequence: str, model_name: str | None): request: dict[str, Any] = {"sequence": sequence} - request["model"] = model_name return request @@ -164,12 +106,15 @@ def _process_fold_response(data: dict[str, Any], sequence: str) -> ESMProtein: sequence=sequence, coordinates=maybe_tensor(data["coordinates"], convert_none_to_nan=True), ptm=maybe_tensor(data.get("ptm", None)), - plddt=maybe_tensor(data.get("plddt", None)), + plddt=maybe_tensor(data.get("plddt", None), convert_none_to_nan=True), ) @staticmethod def process_inverse_fold_request( - coordinates: torch.Tensor, config: InverseFoldingConfig, model_name: str | None + coordinates: torch.Tensor, + sequence: str | None, + config: InverseFoldingConfig, + model_name: str | None, ): inverse_folding_config = { "invalid_ids": config.invalid_ids, @@ -178,6 +123,7 @@ def process_inverse_fold_request( request = { "coordinates": maybe_list(coordinates, convert_nan_to_none=True), "inverse_folding_config": inverse_folding_config, + "sequence": sequence, } if model_name is not None: request["model"] = model_name @@ -222,13 +168,13 @@ async def async_fold( del potential_sequence_of_concern request = self._process_fold_request( - sequence, - model_name if model_name is not None else self.model, + sequence, model_name if model_name is not None else self.model ) - # Intentionally not catching errors, so our higher level logic such as automatic - # batch runner gets a chance to handle different errors properly. - data = await self._async_post("fold", request) + try: + data = await self._async_post("fold", request) + except ESMProteinError as e: + return e return self._process_fold_response(data, sequence) @@ -249,16 +195,17 @@ def fold( potential_sequence_of_concern: this parameter is largely deprecated and ignored by the folding endpoint. """ + del potential_sequence_of_concern request = self._process_fold_request( - sequence, - model_name if model_name is not None else self.model, + sequence, model_name if model_name is not None else self.model ) - # Intentionally not catching errors, so our higher level logic such as automatic - # batch runner gets a chance to handle different errors properly. - data = self._post("fold", request) + try: + data = self._post("fold", request) + except ESMProteinError as e: + return e return self._process_fold_response(data, sequence) @@ -268,6 +215,7 @@ async def async_inverse_fold( coordinates: torch.Tensor, config: InverseFoldingConfig, potential_sequence_of_concern: bool, + sequence: str | None = None, model_name: str | None = None, ) -> ESMProtein | ESMProteinError: """Generate protein sequence from its structure. @@ -282,14 +230,18 @@ async def async_inverse_fold( model_name: Override the client level model name if needed. """ request = self.process_inverse_fold_request( - coordinates, config, model_name if model_name is not None else self.model + coordinates, + sequence, + config, + model_name if model_name is not None else self.model, ) - # Intentionally not catching errors, so our higher level logic such as automatic - # batch runner gets a chance to handle different errors properly. - data = await self._async_post( - "inverse_fold", request, potential_sequence_of_concern - ) + try: + data = await self._async_post( + "inverse_fold", request, potential_sequence_of_concern + ) + except ESMProteinError as e: + return e return ESMProtein(sequence=data["sequence"]) @@ -299,6 +251,7 @@ def inverse_fold( coordinates: torch.Tensor, config: InverseFoldingConfig, potential_sequence_of_concern: bool, + sequence: str | None = None, model_name: str | None = None, ) -> ESMProtein | ESMProteinError: """Generate protein sequence from its structure. @@ -313,12 +266,16 @@ def inverse_fold( model_name: Override the client level model name if needed. """ request = self.process_inverse_fold_request( - coordinates, config, model_name if model_name is not None else self.model + coordinates, + sequence, + config, + model_name if model_name is not None else self.model, ) - # Intentionally not catching errors, so our higher level logic such as automatic - # batch runner gets a chance to handle different errors properly. - data = self._post("inverse_fold", request, potential_sequence_of_concern) + try: + data = self._post("inverse_fold", request, potential_sequence_of_concern) + except ESMProteinError as e: + return e return ESMProtein(sequence=data["sequence"]) @@ -442,6 +399,8 @@ def _field_to_tensor(field, convert_none_to_nan: bool = False): def _process_forward_and_sample_request( input: ESMProteinTensor, sampling_configuration: SamplingConfig, model_name: str ) -> dict[str, Any]: + from esm.utils.sampling import validate_sampling_config + _validate_protein_tensor_input(input) validate_sampling_config(sampling_configuration, on_invalid="raise") @@ -623,7 +582,9 @@ def _process_logits_request( "function": config.function, "residue_annotations": config.residue_annotations, "return_embeddings": config.return_embeddings, + "return_mean_embedding": config.return_mean_embedding, "return_hidden_states": config.return_hidden_states, + "return_mean_hidden_states": config.return_mean_hidden_states, "ith_hidden_layer": config.ith_hidden_layer, } @@ -634,34 +595,28 @@ def _process_logits_request( def _process_logits_response( data: dict[str, Any], return_bytes: bool ) -> LogitsOutput: - def _maybe_logits(track: str): - ret = data.get("logits", {}).get(track, None) - # TODO(s22chan): just return this when removing return_bytes - return ret if ret is None or not return_bytes else maybe_tensor(ret) - - def _maybe_b64_decode(obj): - return ( - deserialize_tensors(base64.b64decode(obj)) - if return_bytes and isinstance(obj, str) - else obj - ) - - logits = _maybe_b64_decode(data["logits"]) + logits = _maybe_b64_decode(data["logits"], return_bytes) data["logits"] = dict(logits) if logits is not None else logits - data["embeddings"] = _maybe_b64_decode(data["embeddings"]) - data["hidden_states"] = _maybe_b64_decode(data["hidden_states"]) + data["embeddings"] = _maybe_b64_decode(data["embeddings"], return_bytes) + data["hidden_states"] = _maybe_b64_decode(data["hidden_states"], return_bytes) return LogitsOutput( logits=ForwardTrackData( - sequence=_maybe_logits("sequence"), - structure=_maybe_logits("structure"), - secondary_structure=_maybe_logits("secondary_structure"), - sasa=_maybe_logits("sasa"), - function=_maybe_logits("function"), + sequence=_maybe_logits(data, "sequence", return_bytes), + structure=_maybe_logits(data, "structure", return_bytes), + secondary_structure=_maybe_logits( + data, "secondary_structure", return_bytes + ), + sasa=_maybe_logits(data, "sasa", return_bytes), + function=_maybe_logits(data, "function", return_bytes), ), embeddings=maybe_tensor(data["embeddings"]), - residue_annotation_logits=_maybe_logits("residue_annotation"), + mean_embedding=data["mean_embedding"], + residue_annotation_logits=_maybe_logits( + data, "residue_annotation", return_bytes + ), hidden_states=maybe_tensor(data["hidden_states"]), + mean_hidden_state=maybe_tensor(data["mean_hidden_state"]), ) @retry_decorator @@ -741,8 +696,8 @@ async def safe_generate(protein, config): try: res = self.async_generate(protein, config) return await res - except Exception as e: - return ESMProteinError(500, str(e)) + except ESMProteinError as e: + return e tasks = [ safe_generate(protein, config) for protein, config in zip(inputs, configs) @@ -1009,6 +964,7 @@ def _process_logits_request( logits_config = { "sequence": config.sequence, "return_embeddings": config.return_embeddings, + "return_mean_embedding": config.return_mean_embedding, "return_hidden_states": config.return_hidden_states, "ith_hidden_layer": config.ith_hidden_layer, } @@ -1019,26 +975,17 @@ def _process_logits_request( def _process_logits_response( data: dict[str, Any], return_bytes: bool ) -> LogitsOutput: - def _maybe_logits(track: str): - ret = data.get("logits", {}).get(track, None) - # TODO(s22chan): just return this when removing return_bytes - return ret if ret is None or not return_bytes else maybe_tensor(ret) - - def _maybe_b64_decode(obj): - return ( - deserialize_tensors(base64.b64decode(obj)) - if return_bytes and isinstance(obj, str) - else obj - ) - - logits = _maybe_b64_decode(data["logits"]) + logits = _maybe_b64_decode(data["logits"], return_bytes) data["logits"] = dict(logits) if logits is not None else logits - data["embeddings"] = _maybe_b64_decode(data["embeddings"]) - data["hidden_states"] = _maybe_b64_decode(data["hidden_states"]) + data["embeddings"] = _maybe_b64_decode(data["embeddings"], return_bytes) + data["hidden_states"] = _maybe_b64_decode(data["hidden_states"], return_bytes) output = LogitsOutput( - logits=ForwardTrackData(sequence=_maybe_logits("sequence")), + logits=ForwardTrackData( + sequence=_maybe_logits(data, "sequence", return_bytes) + ), embeddings=maybe_tensor(data["embeddings"]), + mean_embedding=data["mean_embedding"], hidden_states=maybe_tensor(data["hidden_states"]), ) return output diff --git a/esm/sdk/retry.py b/esm/sdk/retry.py new file mode 100644 index 00000000..16c354b6 --- /dev/null +++ b/esm/sdk/retry.py @@ -0,0 +1,76 @@ +import inspect +from contextvars import ContextVar +from functools import wraps + +import httpx +from tenacity import ( + retry, + retry_if_exception_type, + retry_if_result, + stop_after_attempt, + wait_incrementing, +) + +from esm.sdk.api import ESMProteinError + +skip_retries_var = ContextVar("skip_retries", default=False) + + +def retry_if_specific_error(exception): + """ + We only retry on specific errors. + """ + return isinstance(exception, ESMProteinError) and exception.error_code in { + 429, + 500, + 502, + 504, + 500, + } + + +def log_retry_attempt(retry_state): + print( + f"Retrying... Attempt {retry_state.attempt_number} after {retry_state.next_action.sleep}s due to: {retry_state.outcome.result()}" + ) + + +def retry_decorator(func): + """ + A static method that returns a retry decorator. This decorator uses the + instance's retry settings. + """ + + @wraps(func) + async def async_wrapper(instance, *args, **kwargs): + if skip_retries_var.get(): + return await func(instance, *args, **kwargs) + retry_decorator = retry( + retry=retry_if_result(retry_if_specific_error) + | retry_if_exception_type(httpx.ConnectTimeout), # ADDED + wait=wait_incrementing( + increment=1, start=instance.min_retry_wait, max=instance.max_retry_wait + ), + stop=stop_after_attempt(instance.max_retry_attempts), + before_sleep=log_retry_attempt, + ) + # Apply the retry decorator to the function + return await retry_decorator(func)(instance, *args, **kwargs) + + @wraps(func) + def wrapper(instance, *args, **kwargs): + if skip_retries_var.get(): + return func(instance, *args, **kwargs) + retry_decorator = retry( + retry=retry_if_result(retry_if_specific_error) + | retry_if_exception_type(httpx.ConnectTimeout), # ADDED + wait=wait_incrementing( + increment=1, start=instance.min_retry_wait, max=instance.max_retry_wait + ), + stop=stop_after_attempt(instance.max_retry_attempts), + before_sleep=log_retry_attempt, + ) + # Apply the retry decorator to the function + return retry_decorator(func)(instance, *args, **kwargs) + + return async_wrapper if inspect.iscoroutinefunction(func) else wrapper diff --git a/esm/sdk/sagemaker.py b/esm/sdk/sagemaker.py index 219a4442..79232054 100644 --- a/esm/sdk/sagemaker.py +++ b/esm/sdk/sagemaker.py @@ -22,7 +22,7 @@ def __init__(self, endpoint_name: str, model: str | None = None): self._boto3_client = boto3.client(service_name="sagemaker-runtime") - def _post(self, endpoint, request, potential_sequence_of_concern): + def _post(self, endpoint, request, potential_sequence_of_concern: bool = False): request["potential_sequence_of_concern"] = potential_sequence_of_concern request["model"] = request.get("model", None) invocations_request = { diff --git a/esm/tokenization/__init__.py b/esm/tokenization/__init__.py index ea609225..6db76554 100644 --- a/esm/tokenization/__init__.py +++ b/esm/tokenization/__init__.py @@ -1,10 +1,7 @@ from dataclasses import dataclass from typing import Protocol -from esm.utils.constants.models import ( - ESM3_OPEN_SMALL, - normalize_model_name, -) +from esm.utils.constants.models import ESM3_OPEN_SMALL, normalize_model_name from .function_tokenizer import InterProQuantizedTokenizer from .residue_tokenizer import ResidueAnnotationsTokenizer diff --git a/esm/utils/decoding.py b/esm/utils/decoding.py index 18dff27d..1fe256b6 100644 --- a/esm/utils/decoding.py +++ b/esm/utils/decoding.py @@ -10,24 +10,12 @@ from esm.models.vqvae import StructureTokenDecoder from esm.sdk.api import ESMProtein, ESMProteinTensor from esm.tokenization import TokenizerCollectionProtocol -from esm.tokenization.function_tokenizer import ( - InterProQuantizedTokenizer, -) -from esm.tokenization.residue_tokenizer import ( - ResidueAnnotationsTokenizer, -) -from esm.tokenization.sasa_tokenizer import ( - SASADiscretizingTokenizer, -) -from esm.tokenization.sequence_tokenizer import ( - EsmSequenceTokenizer, -) -from esm.tokenization.ss_tokenizer import ( - SecondaryStructureTokenizer, -) -from esm.tokenization.structure_tokenizer import ( - StructureTokenizer, -) +from esm.tokenization.function_tokenizer import InterProQuantizedTokenizer +from esm.tokenization.residue_tokenizer import ResidueAnnotationsTokenizer +from esm.tokenization.sasa_tokenizer import SASADiscretizingTokenizer +from esm.tokenization.sequence_tokenizer import EsmSequenceTokenizer +from esm.tokenization.ss_tokenizer import SecondaryStructureTokenizer +from esm.tokenization.structure_tokenizer import StructureTokenizer from esm.tokenization.tokenizer_base import EsmTokenizerBase from esm.utils.constants import api as api_constants from esm.utils.constants import esm3 as C @@ -251,6 +239,7 @@ def assemble_message(headers: Mapping[str, str], response: Response) -> dict[str content_type = headers.get("Content-Type", "application/json") if content_type == api_constants.MIMETYPE_ES_PICKLE: return pickle.loads(response.content) - elif content_type == "application/json": + elif "application/json" in content_type: + # Can handle something like "application/json; charset=utf-8" return response.json() raise ValueError(f"Unknown Content-Type: {content_type}") diff --git a/esm/utils/encoding.py b/esm/utils/encoding.py index 83c9d033..8461709d 100644 --- a/esm/utils/encoding.py +++ b/esm/utils/encoding.py @@ -7,26 +7,13 @@ from esm.tokenization.function_tokenizer import ( InterProQuantizedTokenizer as EsmFunctionTokenizer, ) - -from esm.tokenization.residue_tokenizer import ( - ResidueAnnotationsTokenizer, -) -from esm.tokenization.sasa_tokenizer import ( - SASADiscretizingTokenizer, -) -from esm.tokenization.sequence_tokenizer import ( - EsmSequenceTokenizer, -) -from esm.tokenization.ss_tokenizer import ( - SecondaryStructureTokenizer, -) -from esm.tokenization.structure_tokenizer import ( - StructureTokenizer, -) +from esm.tokenization.residue_tokenizer import ResidueAnnotationsTokenizer +from esm.tokenization.sasa_tokenizer import SASADiscretizingTokenizer +from esm.tokenization.sequence_tokenizer import EsmSequenceTokenizer +from esm.tokenization.ss_tokenizer import SecondaryStructureTokenizer +from esm.tokenization.structure_tokenizer import StructureTokenizer from esm.utils.constants import esm3 as C -from esm.utils.function.encode_decode import ( - encode_function_annotations, -) +from esm.utils.function.encode_decode import encode_function_annotations from esm.utils.structure.protein_chain import ProteinChain from esm.utils.types import FunctionAnnotation @@ -165,8 +152,6 @@ def tokenize_function_annotations( return function_tokens, residue_annotation_tokens - - # Tokenized Defaults def get_default_sequence_tokens( sequence_length: int, sequence_tokenizer: EsmSequenceTokenizer @@ -242,5 +227,3 @@ def get_default_residue_annotation_tokens( residue_annotation_tokens[0] = residue_annotation_tokenizer.bos_token_id residue_annotation_tokens[-1] = residue_annotation_tokenizer.eos_token_id return residue_annotation_tokens - - diff --git a/esm/utils/forge_context_manager.py b/esm/utils/forge_context_manager.py index bf898cd2..b1c2bdf3 100644 --- a/esm/utils/forge_context_manager.py +++ b/esm/utils/forge_context_manager.py @@ -7,10 +7,7 @@ from tqdm import tqdm from esm.sdk.api import ESMProteinError -from esm.sdk.forge import ( - retry_if_specific_error, - skip_retries_var, -) +from esm.sdk.retry import retry_if_specific_error, skip_retries_var TQDM_BAR_FORMAT = ( "{desc:<12}{percentage:3.0f}%|{bar:24}| {n_fmt}/{total_fmt} " @@ -25,7 +22,7 @@ def __init__( self, initial_concurrency: int = 32, min_concurrency: int = 1, - max_concurrency: int = 512, + max_concurrency: int = 64, step_up: int = 1, ): self.concurrency = initial_concurrency @@ -56,8 +53,10 @@ class ForgeBatchExecutor: """ def __init__( - self, max_attempts: int = 10, max_workers: int = 512, show_progress: bool = True + self, max_attempts: int = 10, max_workers: int = 64, show_progress: bool = True ): + if max_workers > 64: + raise ValueError("max_workers must be less than 64") self.rate_limiter = AIMDRateLimiter(max_concurrency=max_workers) self.max_attempts = max_attempts self.show_progress = show_progress diff --git a/esm/utils/function/encode_decode.py b/esm/utils/function/encode_decode.py index 29534e34..a4029858 100644 --- a/esm/utils/function/encode_decode.py +++ b/esm/utils/function/encode_decode.py @@ -3,16 +3,9 @@ import torch -from esm.models.function_decoder import ( - FunctionTokenDecoder, - merge_annotations, -) -from esm.tokenization.function_tokenizer import ( - InterProQuantizedTokenizer, -) -from esm.tokenization.residue_tokenizer import ( - ResidueAnnotationsTokenizer, -) +from esm.models.function_decoder import FunctionTokenDecoder, merge_annotations +from esm.tokenization.function_tokenizer import InterProQuantizedTokenizer +from esm.tokenization.residue_tokenizer import ResidueAnnotationsTokenizer from esm.utils.constants import esm3 as C from esm.utils.types import FunctionAnnotation diff --git a/esm/utils/function/lsh.py b/esm/utils/function/lsh.py index 25e8534c..87f4c674 100644 --- a/esm/utils/function/lsh.py +++ b/esm/utils/function/lsh.py @@ -65,7 +65,7 @@ def write_hyperplanes(self, filepath: PathLike): hyperplanes: dict[str, np.ndarray] = { # type: ignore str(i): table.hyperplanes for i, table in enumerate(self.tables) } - np.savez(filepath, **hyperplanes) + np.savez(filepath, **hyperplanes) # type: ignore def __call__(self, array): tokens = np.stack([table(array) for table in self.tables], 1) diff --git a/esm/utils/generation.py b/esm/utils/generation.py index 47c371b0..b2d4ede4 100644 --- a/esm/utils/generation.py +++ b/esm/utils/generation.py @@ -19,13 +19,8 @@ SamplingConfig, SamplingTrackConfig, ) -from esm.tokenization import ( - EsmTokenizerBase, - TokenizerCollectionProtocol, -) -from esm.tokenization.function_tokenizer import ( - InterProQuantizedTokenizer, -) +from esm.tokenization import EsmTokenizerBase, TokenizerCollectionProtocol +from esm.tokenization.function_tokenizer import InterProQuantizedTokenizer from esm.utils.constants import esm3 as C from esm.utils.misc import stack_variable_length_tensors from esm.utils.noise_schedules import NOISE_SCHEDULE_REGISTRY diff --git a/esm/utils/generation_test.py b/esm/utils/generation_test.py deleted file mode 100644 index f8f740bc..00000000 --- a/esm/utils/generation_test.py +++ /dev/null @@ -1,143 +0,0 @@ -import pytest -import torch - -from evolutionaryscale.models.esm3v2 import Esm3v2 -from esm.sdk.api import ( - ESMProtein, - ESMProteinTensor, - GenerationConfig, -) -from evolutionaryscale.utils.env import ModelName -from evolutionaryscale.utils.remote_inference.api_v1 import ( - ESM3RemoteModelInferenceClient, -) -from projects.forge.inference.utils.model import _load_esm_model - - -@pytest.fixture() -def esm3_remote_inference_client(): - model = _load_esm_model( - ModelName.ESM3_TINY_DEV, distributed_model=False, load_function_decoder=False - ) - assert isinstance(model, Esm3v2) - client = ESM3RemoteModelInferenceClient( - model, - tokenizers=model.tokenizers, - device=torch.device("cuda"), - enable_batched_runner=False, - ) - return client - - -@pytest.mark.gpu -def test_chain_break_tokens(esm3_remote_inference_client): - tokenizer = esm3_remote_inference_client.tokenizers.sequence - # 3 separate chains with 2 chainbreak tokens. - sequence_with_chain_breaks = torch.tensor( - [ - tokenizer.bos_token_id, - 20, - 20, - 20, - 20, - tokenizer.chain_break_token_id, - 21, - 21, - 21, - tokenizer.chain_break_token_id, - 22, - 22, - 22, - tokenizer.eos_token_id, - ] - ) - protein = esm3_remote_inference_client.generate( - ESMProteinTensor(sequence=sequence_with_chain_breaks), - # There are 10 tokens that actually need to be sampled. - GenerationConfig(track="structure", num_steps=10), - ) - - assert isinstance(protein, ESMProteinTensor) - assert protein.structure is not None - - -@pytest.mark.gpu -def test_num_decoding_steps_more_than_mask_tokens(esm3_remote_inference_client): - protein = esm3_remote_inference_client.generate( - esm3_remote_inference_client.encode( - ESMProtein(sequence="CDEFG") - ), # sequence of 5. - GenerationConfig(track="structure", num_steps=10), # use 10 decoding steps. - ) - # Client should handle over-specification of decoding steps. - # TODO: This should be a warning. - assert isinstance(protein, ESMProteinTensor) - assert protein.structure is not None - - -@pytest.mark.gpu -def test_num_decoding_steps_more_than_mask_tokens_batched(esm3_remote_inference_client): - protein_list = esm3_remote_inference_client.batch_generate( - inputs=[ - esm3_remote_inference_client.encode(ESMProtein(sequence="CDEFG")), - esm3_remote_inference_client.encode(ESMProtein(sequence="ABCDEFG")), - esm3_remote_inference_client.encode(ESMProtein(sequence="AB__EFG")), - ], - configs=[ - GenerationConfig(track="structure", num_steps=10), - GenerationConfig(track="structure", num_steps=3), - GenerationConfig(track="sequence", num_steps=20), - ], - ) - # Client should handle over-specification of decoding steps. - # TODO: This should be a warning. - assert isinstance(protein_list[0], ESMProteinTensor) - assert protein_list[0].structure is not None - assert isinstance(protein_list[1], ESMProteinTensor) - assert protein_list[1].structure is not None - assert isinstance(protein_list[2], ESMProteinTensor) - assert protein_list[2].sequence is not None - - -@pytest.mark.gpu -def test_encode_chainbreak_token(esm3_remote_inference_client): - protein = esm3_remote_inference_client.encode(ESMProtein(sequence="MSTNP|KPQKK")) - assert isinstance(protein, ESMProteinTensor) - assert protein.sequence is not None - assert ( - protein.sequence[6] - == esm3_remote_inference_client.tokenizers.sequence.chain_break_token_id - ) - - -@pytest.mark.gpu -def test_generation_with_chainbreak_token(esm3_remote_inference_client): - chainbreak_sequence = torch.tensor( - [ - esm3_remote_inference_client.tokenizers.sequence.bos_token_id, - 20, - 8, - 11, - 17, - 14, - esm3_remote_inference_client.tokenizers.sequence.chain_break_token_id, - 15, - 14, - 16, - 15, - 15, - esm3_remote_inference_client.tokenizers.sequence.eos_token_id, - ] - ) - - protein = esm3_remote_inference_client.generate( - ESMProteinTensor(sequence=chainbreak_sequence), - GenerationConfig(track="structure", num_steps=1), - ) - # Can't specify more decoding steps than masks available. - assert isinstance(protein, ESMProteinTensor) - assert protein.structure is not None - assert ( - protein.structure[6] - == esm3_remote_inference_client.tokenizers.structure.chain_break_token_id - ) diff --git a/esm/utils/misc.py b/esm/utils/misc.py index 0afb9a1f..f0d7a602 100644 --- a/esm/utils/misc.py +++ b/esm/utils/misc.py @@ -33,15 +33,15 @@ def slice_python_object_as_numpy( >>> slice_python_object_as_numpy(obj, np.arange(5) < 3) [1, 2, 3] """ - if isinstance(idx, int): - idx = [idx] + if np.isscalar(idx): + idx = [int(idx)] # type: ignore if isinstance(idx, np.ndarray) and idx.dtype == bool: sliced_obj = [obj[i] for i in np.where(idx)[0]] elif isinstance(idx, slice): sliced_obj = obj[idx] else: - sliced_obj = [obj[i] for i in idx] + sliced_obj = [obj[i] for i in idx] # type: ignore match obj, sliced_obj: case str(), list(): @@ -156,6 +156,37 @@ def stack_variable_length_tensors( return array +def binpack( + tensor: torch.Tensor, sequence_id: torch.Tensor | None, pad_value: int | float +): + """ + Args: + tensor (Tensor): [B, L, ...] + + Returns: + Tensor: [B_binpacked, L_binpacked, ...] + """ + if sequence_id is None: + return tensor + + num_sequences = sequence_id.max(dim=-1).values + 1 + + dims = sequence_id.shape + tensor.shape[2:] + output_tensor = torch.full( + dims, fill_value=pad_value, dtype=tensor.dtype, device=tensor.device + ) + + idx = 0 + for batch_idx, (batch_seqid, batch_num_sequences) in enumerate( + zip(sequence_id, num_sequences) + ): + for seqid in range(batch_num_sequences): + mask = batch_seqid == seqid + output_tensor[batch_idx, mask] = tensor[idx, : mask.sum()] + idx += 1 + return output_tensor + + def unbinpack( tensor: torch.Tensor, sequence_id: torch.Tensor | None, pad_value: int | float ): @@ -280,9 +311,18 @@ def maybe_list(x, convert_nan_to_none: bool = False) -> list | None: return None if not convert_nan_to_none: return x.tolist() - nan_mask = torch.isnan(x) - np_arr = x.cpu().numpy().astype(object) - np_arr[nan_mask.cpu().numpy()] = None + + # Handle both torch.tensor and np.ndarray input. + if isinstance(x, torch.Tensor): + nan_mask = torch.isnan(x).cpu().numpy() + np_arr = x.cpu().numpy().astype(object) + elif isinstance(x, np.ndarray): + nan_mask = np.isnan(x) + np_arr = x.astype(object) + else: + raise TypeError("maybe_list can only work with torch.tensor or np.ndarray.") + + np_arr[nan_mask] = None return np_arr.tolist() @@ -313,7 +353,6 @@ def get_chainbreak_boundaries_from_sequence(sequence: Sequence[str]) -> np.ndarr return chain_boundaries -# TODO(return_bytes): remove when retiring return_bytes on SageMaker def deserialize_tensors(b: bytes) -> Any: buf = BytesIO(zstd.ZSTD_uncompress(b)) d = torch.load(buf, map_location="cpu", weights_only=False) diff --git a/esm/utils/residue_constants.py b/esm/utils/residue_constants.py index b1af5390..81b379e8 100644 --- a/esm/utils/residue_constants.py +++ b/esm/utils/residue_constants.py @@ -1,3 +1,4 @@ +# Copyright 2025 EvolutionaryScale # Copyright 2021 AlQuraishi Laboratory # Copyright 2021 DeepMind Technologies Limited # @@ -13,6 +14,527 @@ # See the License for the specific language governing permissions and # limitations under the License. +"""Constants used in AlphaFold.""" + +import collections +import functools +from pathlib import Path +from typing import List, Mapping, Tuple + +import numpy as np + +# import tree + +# Internal import (35fd). + + +# Distance from one CA to next CA [trans configuration: omega = 180]. +ca_ca = 3.80209737096 + +# Format: The list for each AA type contains chi1, chi2, chi3, chi4 in +# this order (or a relevant subset from chi1 onwards). ALA and GLY don't have +# chi angles so their chi angle lists are empty. +chi_angles_atoms = { + "ALA": [], + # Chi5 in arginine is always 0 +- 5 degrees, so ignore it. + "ARG": [ + ["N", "CA", "CB", "CG"], + ["CA", "CB", "CG", "CD"], + ["CB", "CG", "CD", "NE"], + ["CG", "CD", "NE", "CZ"], + ], + "ASN": [["N", "CA", "CB", "CG"], ["CA", "CB", "CG", "OD1"]], + "ASP": [["N", "CA", "CB", "CG"], ["CA", "CB", "CG", "OD1"]], + "CYS": [["N", "CA", "CB", "SG"]], + "GLN": [ + ["N", "CA", "CB", "CG"], + ["CA", "CB", "CG", "CD"], + ["CB", "CG", "CD", "OE1"], + ], + "GLU": [ + ["N", "CA", "CB", "CG"], + ["CA", "CB", "CG", "CD"], + ["CB", "CG", "CD", "OE1"], + ], + "GLY": [], + "HIS": [["N", "CA", "CB", "CG"], ["CA", "CB", "CG", "ND1"]], + "ILE": [["N", "CA", "CB", "CG1"], ["CA", "CB", "CG1", "CD1"]], + "LEU": [["N", "CA", "CB", "CG"], ["CA", "CB", "CG", "CD1"]], + "LYS": [ + ["N", "CA", "CB", "CG"], + ["CA", "CB", "CG", "CD"], + ["CB", "CG", "CD", "CE"], + ["CG", "CD", "CE", "NZ"], + ], + "MET": [ + ["N", "CA", "CB", "CG"], + ["CA", "CB", "CG", "SD"], + ["CB", "CG", "SD", "CE"], + ], + "PHE": [["N", "CA", "CB", "CG"], ["CA", "CB", "CG", "CD1"]], + "PRO": [["N", "CA", "CB", "CG"], ["CA", "CB", "CG", "CD"]], + "SER": [["N", "CA", "CB", "OG"]], + "THR": [["N", "CA", "CB", "OG1"]], + "TRP": [["N", "CA", "CB", "CG"], ["CA", "CB", "CG", "CD1"]], + "TYR": [["N", "CA", "CB", "CG"], ["CA", "CB", "CG", "CD1"]], + "VAL": [["N", "CA", "CB", "CG1"]], + "UNK": [], +} + +# If chi angles given in fixed-length array, this matrix determines how to mask +# them for each AA type. The order is as per restype_order (see below). +chi_angles_mask = [ + [0.0, 0.0, 0.0, 0.0], # ALA + [1.0, 1.0, 1.0, 1.0], # ARG + [1.0, 1.0, 0.0, 0.0], # ASN + [1.0, 1.0, 0.0, 0.0], # ASP + [1.0, 0.0, 0.0, 0.0], # CYS + [1.0, 1.0, 1.0, 0.0], # GLN + [1.0, 1.0, 1.0, 0.0], # GLU + [0.0, 0.0, 0.0, 0.0], # GLY + [1.0, 1.0, 0.0, 0.0], # HIS + [1.0, 1.0, 0.0, 0.0], # ILE + [1.0, 1.0, 0.0, 0.0], # LEU + [1.0, 1.0, 1.0, 1.0], # LYS + [1.0, 1.0, 1.0, 0.0], # MET + [1.0, 1.0, 0.0, 0.0], # PHE + [1.0, 1.0, 0.0, 0.0], # PRO + [1.0, 0.0, 0.0, 0.0], # SER + [1.0, 0.0, 0.0, 0.0], # THR + [1.0, 1.0, 0.0, 0.0], # TRP + [1.0, 1.0, 0.0, 0.0], # TYR + [1.0, 0.0, 0.0, 0.0], # VAL + [0.0, 0.0, 0.0, 0.0], # UNK +] + +# The following chi angles are pi periodic: they can be rotated by a multiple +# of pi without affecting the structure. +chi_pi_periodic = [ + [0.0, 0.0, 0.0, 0.0], # ALA + [0.0, 0.0, 0.0, 0.0], # ARG + [0.0, 0.0, 0.0, 0.0], # ASN + [0.0, 1.0, 0.0, 0.0], # ASP + [0.0, 0.0, 0.0, 0.0], # CYS + [0.0, 0.0, 0.0, 0.0], # GLN + [0.0, 0.0, 1.0, 0.0], # GLU + [0.0, 0.0, 0.0, 0.0], # GLY + [0.0, 0.0, 0.0, 0.0], # HIS + [0.0, 0.0, 0.0, 0.0], # ILE + [0.0, 0.0, 0.0, 0.0], # LEU + [0.0, 0.0, 0.0, 0.0], # LYS + [0.0, 0.0, 0.0, 0.0], # MET + [0.0, 1.0, 0.0, 0.0], # PHE + [0.0, 0.0, 0.0, 0.0], # PRO + [0.0, 0.0, 0.0, 0.0], # SER + [0.0, 0.0, 0.0, 0.0], # THR + [0.0, 0.0, 0.0, 0.0], # TRP + [0.0, 1.0, 0.0, 0.0], # TYR + [0.0, 0.0, 0.0, 0.0], # VAL + [0.0, 0.0, 0.0, 0.0], # UNK +] + +# Atoms positions relative to the 8 rigid groups, defined by the pre-omega, phi, +# psi and chi angles: +# 0: 'backbone group', +# 1: 'pre-omega-group', (empty) +# 2: 'phi-group', (currently empty, because it defines only hydrogens) +# 3: 'psi-group', +# 4,5,6,7: 'chi1,2,3,4-group' +# The atom positions are relative to the axis-end-atom of the corresponding +# rotation axis. The x-axis is in direction of the rotation axis, and the y-axis +# is defined such that the dihedral-angle-definiting atom (the last entry in +# chi_angles_atoms above) is in the xy-plane (with a positive y-coordinate). +# format: [atomname, group_idx, rel_position] +rigid_group_atom_positions = { + "ALA": [ + ["N", 0, (-0.525, 1.363, 0.000)], + ["CA", 0, (0.000, 0.000, 0.000)], + ["C", 0, (1.526, -0.000, -0.000)], + ["CB", 0, (-0.529, -0.774, -1.205)], + ["O", 3, (0.627, 1.062, 0.000)], + ], + "ARG": [ + ["N", 0, (-0.524, 1.362, -0.000)], + ["CA", 0, (0.000, 0.000, 0.000)], + ["C", 0, (1.525, -0.000, -0.000)], + ["CB", 0, (-0.524, -0.778, -1.209)], + ["O", 3, (0.626, 1.062, 0.000)], + ["CG", 4, (0.616, 1.390, -0.000)], + ["CD", 5, (0.564, 1.414, 0.000)], + ["NE", 6, (0.539, 1.357, -0.000)], + ["NH1", 7, (0.206, 2.301, 0.000)], + ["NH2", 7, (2.078, 0.978, -0.000)], + ["CZ", 7, (0.758, 1.093, -0.000)], + ], + "ASN": [ + ["N", 0, (-0.536, 1.357, 0.000)], + ["CA", 0, (0.000, 0.000, 0.000)], + ["C", 0, (1.526, -0.000, -0.000)], + ["CB", 0, (-0.531, -0.787, -1.200)], + ["O", 3, (0.625, 1.062, 0.000)], + ["CG", 4, (0.584, 1.399, 0.000)], + ["ND2", 5, (0.593, -1.188, 0.001)], + ["OD1", 5, (0.633, 1.059, 0.000)], + ], + "ASP": [ + ["N", 0, (-0.525, 1.362, -0.000)], + ["CA", 0, (0.000, 0.000, 0.000)], + ["C", 0, (1.527, 0.000, -0.000)], + ["CB", 0, (-0.526, -0.778, -1.208)], + ["O", 3, (0.626, 1.062, -0.000)], + ["CG", 4, (0.593, 1.398, -0.000)], + ["OD1", 5, (0.610, 1.091, 0.000)], + ["OD2", 5, (0.592, -1.101, -0.003)], + ], + "CYS": [ + ["N", 0, (-0.522, 1.362, -0.000)], + ["CA", 0, (0.000, 0.000, 0.000)], + ["C", 0, (1.524, 0.000, 0.000)], + ["CB", 0, (-0.519, -0.773, -1.212)], + ["O", 3, (0.625, 1.062, -0.000)], + ["SG", 4, (0.728, 1.653, 0.000)], + ], + "GLN": [ + ["N", 0, (-0.526, 1.361, -0.000)], + ["CA", 0, (0.000, 0.000, 0.000)], + ["C", 0, (1.526, 0.000, 0.000)], + ["CB", 0, (-0.525, -0.779, -1.207)], + ["O", 3, (0.626, 1.062, -0.000)], + ["CG", 4, (0.615, 1.393, 0.000)], + ["CD", 5, (0.587, 1.399, -0.000)], + ["NE2", 6, (0.593, -1.189, -0.001)], + ["OE1", 6, (0.634, 1.060, 0.000)], + ], + "GLU": [ + ["N", 0, (-0.528, 1.361, 0.000)], + ["CA", 0, (0.000, 0.000, 0.000)], + ["C", 0, (1.526, -0.000, -0.000)], + ["CB", 0, (-0.526, -0.781, -1.207)], + ["O", 3, (0.626, 1.062, 0.000)], + ["CG", 4, (0.615, 1.392, 0.000)], + ["CD", 5, (0.600, 1.397, 0.000)], + ["OE1", 6, (0.607, 1.095, -0.000)], + ["OE2", 6, (0.589, -1.104, -0.001)], + ], + "GLY": [ + ["N", 0, (-0.572, 1.337, 0.000)], + ["CA", 0, (0.000, 0.000, 0.000)], + ["C", 0, (1.517, -0.000, -0.000)], + ["O", 3, (0.626, 1.062, -0.000)], + ], + "HIS": [ + ["N", 0, (-0.527, 1.360, 0.000)], + ["CA", 0, (0.000, 0.000, 0.000)], + ["C", 0, (1.525, 0.000, 0.000)], + ["CB", 0, (-0.525, -0.778, -1.208)], + ["O", 3, (0.625, 1.063, 0.000)], + ["CG", 4, (0.600, 1.370, -0.000)], + ["CD2", 5, (0.889, -1.021, 0.003)], + ["ND1", 5, (0.744, 1.160, -0.000)], + ["CE1", 5, (2.030, 0.851, 0.002)], + ["NE2", 5, (2.145, -0.466, 0.004)], + ], + "ILE": [ + ["N", 0, (-0.493, 1.373, -0.000)], + ["CA", 0, (0.000, 0.000, 0.000)], + ["C", 0, (1.527, -0.000, -0.000)], + ["CB", 0, (-0.536, -0.793, -1.213)], + ["O", 3, (0.627, 1.062, -0.000)], + ["CG1", 4, (0.534, 1.437, -0.000)], + ["CG2", 4, (0.540, -0.785, -1.199)], + ["CD1", 5, (0.619, 1.391, 0.000)], + ], + "LEU": [ + ["N", 0, (-0.520, 1.363, 0.000)], + ["CA", 0, (0.000, 0.000, 0.000)], + ["C", 0, (1.525, -0.000, -0.000)], + ["CB", 0, (-0.522, -0.773, -1.214)], + ["O", 3, (0.625, 1.063, -0.000)], + ["CG", 4, (0.678, 1.371, 0.000)], + ["CD1", 5, (0.530, 1.430, -0.000)], + ["CD2", 5, (0.535, -0.774, 1.200)], + ], + "LYS": [ + ["N", 0, (-0.526, 1.362, -0.000)], + ["CA", 0, (0.000, 0.000, 0.000)], + ["C", 0, (1.526, 0.000, 0.000)], + ["CB", 0, (-0.524, -0.778, -1.208)], + ["O", 3, (0.626, 1.062, -0.000)], + ["CG", 4, (0.619, 1.390, 0.000)], + ["CD", 5, (0.559, 1.417, 0.000)], + ["CE", 6, (0.560, 1.416, 0.000)], + ["NZ", 7, (0.554, 1.387, 0.000)], + ], + "MET": [ + ["N", 0, (-0.521, 1.364, -0.000)], + ["CA", 0, (0.000, 0.000, 0.000)], + ["C", 0, (1.525, 0.000, 0.000)], + ["CB", 0, (-0.523, -0.776, -1.210)], + ["O", 3, (0.625, 1.062, -0.000)], + ["CG", 4, (0.613, 1.391, -0.000)], + ["SD", 5, (0.703, 1.695, 0.000)], + ["CE", 6, (0.320, 1.786, -0.000)], + ], + "PHE": [ + ["N", 0, (-0.518, 1.363, 0.000)], + ["CA", 0, (0.000, 0.000, 0.000)], + ["C", 0, (1.524, 0.000, -0.000)], + ["CB", 0, (-0.525, -0.776, -1.212)], + ["O", 3, (0.626, 1.062, -0.000)], + ["CG", 4, (0.607, 1.377, 0.000)], + ["CD1", 5, (0.709, 1.195, -0.000)], + ["CD2", 5, (0.706, -1.196, 0.000)], + ["CE1", 5, (2.102, 1.198, -0.000)], + ["CE2", 5, (2.098, -1.201, -0.000)], + ["CZ", 5, (2.794, -0.003, -0.001)], + ], + "PRO": [ + ["N", 0, (-0.566, 1.351, -0.000)], + ["CA", 0, (0.000, 0.000, 0.000)], + ["C", 0, (1.527, -0.000, 0.000)], + ["CB", 0, (-0.546, -0.611, -1.293)], + ["O", 3, (0.621, 1.066, 0.000)], + ["CG", 4, (0.382, 1.445, 0.0)], + # ['CD', 5, (0.427, 1.440, 0.0)], + ["CD", 5, (0.477, 1.424, 0.0)], # manually made angle 2 degrees larger + ], + "SER": [ + ["N", 0, (-0.529, 1.360, -0.000)], + ["CA", 0, (0.000, 0.000, 0.000)], + ["C", 0, (1.525, -0.000, -0.000)], + ["CB", 0, (-0.518, -0.777, -1.211)], + ["O", 3, (0.626, 1.062, -0.000)], + ["OG", 4, (0.503, 1.325, 0.000)], + ], + "THR": [ + ["N", 0, (-0.517, 1.364, 0.000)], + ["CA", 0, (0.000, 0.000, 0.000)], + ["C", 0, (1.526, 0.000, -0.000)], + ["CB", 0, (-0.516, -0.793, -1.215)], + ["O", 3, (0.626, 1.062, 0.000)], + ["CG2", 4, (0.550, -0.718, -1.228)], + ["OG1", 4, (0.472, 1.353, 0.000)], + ], + "TRP": [ + ["N", 0, (-0.521, 1.363, 0.000)], + ["CA", 0, (0.000, 0.000, 0.000)], + ["C", 0, (1.525, -0.000, 0.000)], + ["CB", 0, (-0.523, -0.776, -1.212)], + ["O", 3, (0.627, 1.062, 0.000)], + ["CG", 4, (0.609, 1.370, -0.000)], + ["CD1", 5, (0.824, 1.091, 0.000)], + ["CD2", 5, (0.854, -1.148, -0.005)], + ["CE2", 5, (2.186, -0.678, -0.007)], + ["CE3", 5, (0.622, -2.530, -0.007)], + ["NE1", 5, (2.140, 0.690, -0.004)], + ["CH2", 5, (3.028, -2.890, -0.013)], + ["CZ2", 5, (3.283, -1.543, -0.011)], + ["CZ3", 5, (1.715, -3.389, -0.011)], + ], + "TYR": [ + ["N", 0, (-0.522, 1.362, 0.000)], + ["CA", 0, (0.000, 0.000, 0.000)], + ["C", 0, (1.524, -0.000, -0.000)], + ["CB", 0, (-0.522, -0.776, -1.213)], + ["O", 3, (0.627, 1.062, -0.000)], + ["CG", 4, (0.607, 1.382, -0.000)], + ["CD1", 5, (0.716, 1.195, -0.000)], + ["CD2", 5, (0.713, -1.194, -0.001)], + ["CE1", 5, (2.107, 1.200, -0.002)], + ["CE2", 5, (2.104, -1.201, -0.003)], + ["OH", 5, (4.168, -0.002, -0.005)], + ["CZ", 5, (2.791, -0.001, -0.003)], + ], + "VAL": [ + ["N", 0, (-0.494, 1.373, -0.000)], + ["CA", 0, (0.000, 0.000, 0.000)], + ["C", 0, (1.527, -0.000, -0.000)], + ["CB", 0, (-0.533, -0.795, -1.213)], + ["O", 3, (0.627, 1.062, -0.000)], + ["CG1", 4, (0.540, 1.429, -0.000)], + ["CG2", 4, (0.533, -0.776, 1.203)], + ], + # Assume alanine positions for unknown AA + "UNK": [ + ["N", 0, (-0.525, 1.363, 0.000)], + ["CA", 0, (0.000, 0.000, 0.000)], + ["C", 0, (1.526, -0.000, -0.000)], + ], +} + +# A list of atoms (excluding hydrogen) for each AA type. PDB naming convention. +residue_atoms = { + "ALA": ["C", "CA", "CB", "N", "O"], + "ARG": ["C", "CA", "CB", "CG", "CD", "CZ", "N", "NE", "O", "NH1", "NH2"], + "ASP": ["C", "CA", "CB", "CG", "N", "O", "OD1", "OD2"], + "ASN": ["C", "CA", "CB", "CG", "N", "ND2", "O", "OD1"], + "CYS": ["C", "CA", "CB", "N", "O", "SG"], + "GLU": ["C", "CA", "CB", "CG", "CD", "N", "O", "OE1", "OE2"], + "GLN": ["C", "CA", "CB", "CG", "CD", "N", "NE2", "O", "OE1"], + "GLY": ["C", "CA", "N", "O"], + "HIS": ["C", "CA", "CB", "CG", "CD2", "CE1", "N", "ND1", "NE2", "O"], + "ILE": ["C", "CA", "CB", "CG1", "CG2", "CD1", "N", "O"], + "LEU": ["C", "CA", "CB", "CG", "CD1", "CD2", "N", "O"], + "LYS": ["C", "CA", "CB", "CG", "CD", "CE", "N", "NZ", "O"], + "MET": ["C", "CA", "CB", "CG", "CE", "N", "O", "SD"], + "PHE": ["C", "CA", "CB", "CG", "CD1", "CD2", "CE1", "CE2", "CZ", "N", "O"], + "PRO": ["C", "CA", "CB", "CG", "CD", "N", "O"], + "SER": ["C", "CA", "CB", "N", "O", "OG"], + "THR": ["C", "CA", "CB", "CG2", "N", "O", "OG1"], + "TRP": [ + "C", + "CA", + "CB", + "CG", + "CD1", + "CD2", + "CE2", + "CE3", + "CZ2", + "CZ3", + "CH2", + "N", + "NE1", + "O", + ], + "TYR": ["C", "CA", "CB", "CG", "CD1", "CD2", "CE1", "CE2", "CZ", "N", "O", "OH"], + "VAL": ["C", "CA", "CB", "CG1", "CG2", "N", "O"], + "UNK": ["C", "CA", "N"], +} + +# Naming swaps for ambiguous atom names. +# Due to symmetries in the amino acids the naming of atoms is ambiguous in +# 4 of the 20 amino acids. +# (The LDDT paper lists 7 amino acids as ambiguous, but the naming ambiguities +# in LEU, VAL and ARG can be resolved by using the 3d constellations of +# the 'ambiguous' atoms and their neighbours) +# TODO: ^ interpret this +residue_atom_renaming_swaps = { + "ASP": {"OD1": "OD2"}, + "GLU": {"OE1": "OE2"}, + "PHE": {"CD1": "CD2", "CE1": "CE2"}, + "TYR": {"CD1": "CD2", "CE1": "CE2"}, +} + +# Van der Waals radii [Angstroem] of the atoms (from Wikipedia) +van_der_waals_radius = {"C": 1.7, "N": 1.55, "O": 1.52, "S": 1.8} + +Bond = collections.namedtuple("Bond", ["atom1_name", "atom2_name", "length", "stddev"]) +BondAngle = collections.namedtuple( + "BondAngle", ["atom1_name", "atom2_name", "atom3name", "angle_rad", "stddev"] +) + + +@functools.lru_cache(maxsize=None) +def load_stereo_chemical_props() -> ( + Tuple[ + Mapping[str, List[Bond]], + Mapping[str, List[Bond]], + Mapping[str, List[BondAngle]], + ] +): + """Load stereo_chemical_props.txt into a nice structure. + + Load literature values for bond lengths and bond angles and translate + bond angles into the length of the opposite edge of the triangle + ("residue_virtual_bonds"). + + Returns: + residue_bonds: dict that maps resname --> list of Bond tuples + residue_virtual_bonds: dict that maps resname --> list of Bond tuples + residue_bond_angles: dict that maps resname --> list of BondAngle tuples + """ + stereo_chemical_props = Path( + "evolutionaryscale/structure/stereo_chemical_props.txt" + ).read_text() + + lines_iter = iter(stereo_chemical_props.splitlines()) + # Load bond lengths. + residue_bonds = {} + next(lines_iter) # Skip header line. + for line in lines_iter: + if line.strip() == "-": + break + bond, resname, length, stddev = line.split() + atom1, atom2 = bond.split("-") + if resname not in residue_bonds: + residue_bonds[resname] = [] + residue_bonds[resname].append(Bond(atom1, atom2, float(length), float(stddev))) + residue_bonds["UNK"] = [] + + # Load bond angles. + residue_bond_angles = {} + next(lines_iter) # Skip empty line. + next(lines_iter) # Skip header line. + for line in lines_iter: + if line.strip() == "-": + break + bond, resname, angle_degree, stddev_degree = line.split() + atom1, atom2, atom3 = bond.split("-") + if resname not in residue_bond_angles: + residue_bond_angles[resname] = [] + residue_bond_angles[resname].append( + BondAngle( + atom1, + atom2, + atom3, + float(angle_degree) / 180.0 * np.pi, + float(stddev_degree) / 180.0 * np.pi, + ) + ) + residue_bond_angles["UNK"] = [] + + def make_bond_key(atom1_name, atom2_name): + """Unique key to lookup bonds.""" + return "-".join(sorted([atom1_name, atom2_name])) + + # Translate bond angles into distances ("virtual bonds"). + residue_virtual_bonds = {} + for resname, bond_angles in residue_bond_angles.items(): + # Create a fast lookup dict for bond lengths. + bond_cache = {} + for b in residue_bonds[resname]: + bond_cache[make_bond_key(b.atom1_name, b.atom2_name)] = b + residue_virtual_bonds[resname] = [] + for ba in bond_angles: + bond1 = bond_cache[make_bond_key(ba.atom1_name, ba.atom2_name)] + bond2 = bond_cache[make_bond_key(ba.atom2_name, ba.atom3name)] + + # Compute distance between atom1 and atom3 using the law of cosines + # c^2 = a^2 + b^2 - 2ab*cos(gamma). + gamma = ba.angle_rad + length = np.sqrt( + bond1.length**2 + + bond2.length**2 + - 2 * bond1.length * bond2.length * np.cos(gamma) + ) + + # Propagation of uncertainty assuming uncorrelated errors. + dl_outer = 0.5 / length + dl_dgamma = (2 * bond1.length * bond2.length * np.sin(gamma)) * dl_outer + dl_db1 = (2 * bond1.length - 2 * bond2.length * np.cos(gamma)) * dl_outer + dl_db2 = (2 * bond2.length - 2 * bond1.length * np.cos(gamma)) * dl_outer + stddev = np.sqrt( + (dl_dgamma * ba.stddev) ** 2 + + (dl_db1 * bond1.stddev) ** 2 + + (dl_db2 * bond2.stddev) ** 2 + ) + residue_virtual_bonds[resname].append( + Bond(ba.atom1_name, ba.atom3name, length, stddev) + ) + + return (residue_bonds, residue_virtual_bonds, residue_bond_angles) + + +# Between-residue bond lengths for general bonds (first element) and for Proline +# (second element). +between_res_bond_length_c_n = [1.329, 1.341] +between_res_bond_length_stddev_c_n = [0.014, 0.016] + +# Between-residue cos_angles. +between_res_cos_angles_c_n_ca = [-0.5203, 0.0353] # degrees: 121.352 +- 2.315 +between_res_cos_angles_ca_c_n = [-0.4473, 0.0311] # degrees: 116.568 +- 1.995 + # This mapping is used when we need to store atom data in a format that requires # fixed atom data size for every residue (e.g. a numpy array). atom_types = [ @@ -57,27 +579,190 @@ atom_order = {atom_type: i for i, atom_type in enumerate(atom_types)} atom_type_num = len(atom_types) # := 37. -restype_1to3 = { - "A": "ALA", - "R": "ARG", - "N": "ASN", - "D": "ASP", - "C": "CYS", - "Q": "GLN", - "E": "GLU", - "G": "GLY", - "H": "HIS", - "I": "ILE", - "L": "LEU", - "K": "LYS", - "M": "MET", - "F": "PHE", - "P": "PRO", - "S": "SER", - "T": "THR", - "W": "TRP", - "Y": "TYR", - "V": "VAL", +# A compact atom encoding with 14 columns +# pylint: disable=line-too-long +# pylint: disable=bad-whitespace +restype_name_to_atom14_names = { + "ALA": ["N", "CA", "C", "O", "CB", "", "", "", "", "", "", "", "", ""], + "ARG": [ + "N", + "CA", + "C", + "O", + "CB", + "CG", + "CD", + "NE", + "CZ", + "NH1", + "NH2", + "", + "", + "", + ], + "ASN": ["N", "CA", "C", "O", "CB", "CG", "OD1", "ND2", "", "", "", "", "", ""], + "ASP": ["N", "CA", "C", "O", "CB", "CG", "OD1", "OD2", "", "", "", "", "", ""], + "CYS": ["N", "CA", "C", "O", "CB", "SG", "", "", "", "", "", "", "", ""], + "GLN": ["N", "CA", "C", "O", "CB", "CG", "CD", "OE1", "NE2", "", "", "", "", ""], + "GLU": ["N", "CA", "C", "O", "CB", "CG", "CD", "OE1", "OE2", "", "", "", "", ""], + "GLY": ["N", "CA", "C", "O", "", "", "", "", "", "", "", "", "", ""], + "HIS": [ + "N", + "CA", + "C", + "O", + "CB", + "CG", + "ND1", + "CD2", + "CE1", + "NE2", + "", + "", + "", + "", + ], + "ILE": ["N", "CA", "C", "O", "CB", "CG1", "CG2", "CD1", "", "", "", "", "", ""], + "LEU": ["N", "CA", "C", "O", "CB", "CG", "CD1", "CD2", "", "", "", "", "", ""], + "LYS": ["N", "CA", "C", "O", "CB", "CG", "CD", "CE", "NZ", "", "", "", "", ""], + "MET": ["N", "CA", "C", "O", "CB", "CG", "SD", "CE", "", "", "", "", "", ""], + "PHE": [ + "N", + "CA", + "C", + "O", + "CB", + "CG", + "CD1", + "CD2", + "CE1", + "CE2", + "CZ", + "", + "", + "", + ], + "PRO": ["N", "CA", "C", "O", "CB", "CG", "CD", "", "", "", "", "", "", ""], + "SER": ["N", "CA", "C", "O", "CB", "OG", "", "", "", "", "", "", "", ""], + "THR": ["N", "CA", "C", "O", "CB", "OG1", "CG2", "", "", "", "", "", "", ""], + "TRP": [ + "N", + "CA", + "C", + "O", + "CB", + "CG", + "CD1", + "CD2", + "NE1", + "CE2", + "CE3", + "CZ2", + "CZ3", + "CH2", + ], + "TYR": [ + "N", + "CA", + "C", + "O", + "CB", + "CG", + "CD1", + "CD2", + "CE1", + "CE2", + "CZ", + "OH", + "", + "", + ], + "VAL": ["N", "CA", "C", "O", "CB", "CG1", "CG2", "", "", "", "", "", "", ""], + "UNK": ["N", "CA", "C", "", "", "", "", "", "", "", "", "", "", ""], +} +# pylint: enable=line-too-long +# pylint: enable=bad-whitespace + + +# This is the standard residue order when coding AA type as a number. +# Reproduce it by taking 3-letter AA codes and sorting them alphabetically. +restypes = [ + "A", + "R", + "N", + "D", + "C", + "Q", + "E", + "G", + "H", + "I", + "L", + "K", + "M", + "F", + "P", + "S", + "T", + "W", + "Y", + "V", +] +restype_order = {restype: i for i, restype in enumerate(restypes)} +restype_num = len(restypes) # := 20. +unk_restype_index = restype_num # Catch-all index for unknown restypes. + +restypes_with_x = restypes + ["X"] +restype_order_with_x = {restype: i for i, restype in enumerate(restypes_with_x)} + +bb_atoms = ["N", "CA", "C", "O"] + +# Hydrophobicity by residue (positive values are hydrophobic). Derived from Black & Mould (1991), normalized by subtracting 0.5. +hydrophobicity = { + "ALA": 0.116, + "ARG": -0.5, + "ASN": -0.264, + "ASP": -0.472, + "CYS": 0.18, + "GLN": -0.249, + "GLU": -0.457, + "GLY": 0.001, + "HIS": -0.335, + "ILE": 0.443, + "LEU": 0.443, + "LYS": -0.217, + "MET": 0.238, + "PHE": 0.5, + "PRO": 0.211, + "SER": -0.141, + "THR": -0.05, + "TRP": 0.378, + "TYR": 0.38, + "VAL": 0.325, +} + +# Side chain max accessible surface area in Ala-X-Ala tripeptide (from Chennamsetty et al. 2010). +side_chain_asa = { + "ALA": 64.7809, + "ARG": 210.02, + "ASN": 113.187, + "ASP": 110.209, + "CYS": 95.2439, + "GLN": 147.855, + "GLU": 143.924, + "GLY": 23.1338, + "HIS": 146.449, + "ILE": 151.242, + "LEU": 139.524, + "LYS": 177.366, + "MET": 164.674, + "PHE": 186.7, + "PRO": 111.533, + "SER": 81.2159, + "THR": 111.597, + "TRP": 229.619, + "TYR": 200.306, + "VAL": 124.237, } # Approximate Volumes of amino acids in cubic angstroms. @@ -105,3 +790,434 @@ "V": 140.0, # Valine "X": 88.6, # Unknown, use Alanine as approximation } + + +def sequence_to_onehot( + sequence: str, mapping: Mapping[str, int], map_unknown_to_x: bool = False +) -> np.ndarray: + """Maps the given sequence into a one-hot encoded matrix. + + Args: + sequence: An amino acid sequence. + mapping: A dictionary mapping amino acids to integers. + map_unknown_to_x: If True, any amino acid that is not in the mapping will be + mapped to the unknown amino acid 'X'. If the mapping doesn't contain + amino acid 'X', an error will be thrown. If False, any amino acid not in + the mapping will throw an error. + + Returns: + A numpy array of shape (seq_len, num_unique_aas) with one-hot encoding of + the sequence. + + Raises: + ValueError: If the mapping doesn't contain values from 0 to + num_unique_aas - 1 without any gaps. + """ + num_entries = max(mapping.values()) + 1 + + if sorted(set(mapping.values())) != list(range(num_entries)): + raise ValueError( + "The mapping must have values from 0 to num_unique_aas-1 " + "without any gaps. Got: %s" % sorted(mapping.values()) + ) + + one_hot_arr = np.zeros((len(sequence), num_entries), dtype=np.int32) + + for aa_index, aa_type in enumerate(sequence): + if map_unknown_to_x: + if aa_type.isalpha() and aa_type.isupper(): + aa_id = mapping.get(aa_type, mapping["X"]) + else: + raise ValueError(f"Invalid character in the sequence: {aa_type}") + else: + aa_id = mapping[aa_type] + one_hot_arr[aa_index, aa_id] = 1 + + return one_hot_arr + + +restype_1to3 = { + "A": "ALA", + "R": "ARG", + "N": "ASN", + "D": "ASP", + "C": "CYS", + "Q": "GLN", + "E": "GLU", + "G": "GLY", + "H": "HIS", + "I": "ILE", + "L": "LEU", + "K": "LYS", + "M": "MET", + "F": "PHE", + "P": "PRO", + "S": "SER", + "T": "THR", + "W": "TRP", + "Y": "TYR", + "V": "VAL", + "X": "UNK", +} + + +# NB: restype_3to1 differs from Bio.PDB.protein_letters_3to1 by being a simple +# 1-to-1 mapping of 3 letter names to one letter names. The latter contains +# many more, and less common, three letter names as keys and maps many of these +# to the same one letter name (including 'X' and 'U' which we don't use here). +restype_3to1 = {v: k for k, v in restype_1to3.items()} + +# Define a restype name for all unknown residues. +unk_restype = "UNK" + +resnames = [restype_1to3[r] for r in restypes] + [unk_restype] +resname_to_idx = {resname: i for i, resname in enumerate(resnames)} + +hydrophobic_resnames = {"VAL", "ILE", "LEU", "PHE", "MET", "TRP"} + +# The mapping here uses hhblits convention, so that B is mapped to D, J and O +# are mapped to X, U is mapped to C, and Z is mapped to E. Other than that the +# remaining 20 amino acids are kept in alphabetical order. +# There are 2 non-amino acid codes, X (representing any amino acid) and +# "-" representing a missing amino acid in an alignment. The id for these +# codes is put at the end (20 and 21) so that they can easily be ignored if +# desired. +HHBLITS_AA_TO_ID = { + "A": 0, + "B": 2, + "C": 1, + "D": 2, + "E": 3, + "F": 4, + "G": 5, + "H": 6, + "I": 7, + "J": 20, + "K": 8, + "L": 9, + "M": 10, + "N": 11, + "O": 20, + "P": 12, + "Q": 13, + "R": 14, + "S": 15, + "T": 16, + "U": 1, + "V": 17, + "W": 18, + "X": 20, + "Y": 19, + "Z": 3, + "-": 21, +} + +# Partial inversion of HHBLITS_AA_TO_ID. +ID_TO_HHBLITS_AA = { + 0: "A", + 1: "C", # Also U. + 2: "D", # Also B. + 3: "E", # Also Z. + 4: "F", + 5: "G", + 6: "H", + 7: "I", + 8: "K", + 9: "L", + 10: "M", + 11: "N", + 12: "P", + 13: "Q", + 14: "R", + 15: "S", + 16: "T", + 17: "V", + 18: "W", + 19: "Y", + 20: "X", # Includes J and O. + 21: "-", +} + +restypes_with_x_and_gap = restypes + ["X", "-"] +MAP_HHBLITS_AATYPE_TO_OUR_AATYPE = tuple( + restypes_with_x_and_gap.index(ID_TO_HHBLITS_AA[i]) + for i in range(len(restypes_with_x_and_gap)) +) + + +def _make_standard_atom_mask() -> np.ndarray: + """Returns [num_res_types, num_atom_types] mask array.""" + # +1 to account for unknown (all 0s). + mask = np.zeros([restype_num + 1, atom_type_num], dtype=np.int32) + for restype, restype_letter in enumerate(restypes): + restype_name = restype_1to3[restype_letter] + atom_names = residue_atoms[restype_name] + for atom_name in atom_names: + atom_type = atom_order[atom_name] + mask[restype, atom_type] = 1 + return mask + + +STANDARD_ATOM_MASK = _make_standard_atom_mask() + + +# A one hot representation for the first and second atoms defining the axis +# of rotation for each chi-angle in each residue. +def chi_angle_atom(atom_index: int) -> np.ndarray: + """Define chi-angle rigid groups via one-hot representations.""" + chi_angles_index = {} + one_hots = [] + + for k, v in chi_angles_atoms.items(): + indices = [atom_types.index(s[atom_index]) for s in v] + indices.extend([-1] * (4 - len(indices))) + chi_angles_index[k] = indices + + for r in restypes: + res3 = restype_1to3[r] + one_hot = np.eye(atom_type_num)[chi_angles_index[res3]] + one_hots.append(one_hot) + + one_hots.append(np.zeros([4, atom_type_num])) # Add zeros for residue `X`. + one_hot = np.stack(one_hots, axis=0) + one_hot = np.transpose(one_hot, [0, 2, 1]) + + return one_hot + + +chi_atom_1_one_hot = chi_angle_atom(1) +chi_atom_2_one_hot = chi_angle_atom(2) + +# An array like chi_angles_atoms but using indices rather than names. +chi_angles_atom_indices = [chi_angles_atoms[restype_1to3[r]] for r in restypes] +# chi_angles_atom_indices = tree.map_structure( +# lambda atom_name: atom_order[atom_name], chi_angles_atom_indices +# ) +chi_angles_atom_indices = np.array( + [ + chi_atoms + ([[0, 0, 0, 0]] * (4 - len(chi_atoms))) + for chi_atoms in chi_angles_atom_indices + ] +) + +# Mapping from (res_name, atom_name) pairs to the atom's chi group index +# and atom index within that group. +chi_groups_for_atom = collections.defaultdict(list) +for res_name, chi_angle_atoms_for_res in chi_angles_atoms.items(): + for chi_group_i, chi_group in enumerate(chi_angle_atoms_for_res): + for atom_i, atom in enumerate(chi_group): + chi_groups_for_atom[(res_name, atom)].append((chi_group_i, atom_i)) +chi_groups_for_atom = dict(chi_groups_for_atom) + + +def _make_rigid_transformation_4x4(ex, ey, translation): + """Create a rigid 4x4 transformation matrix from two axes and transl.""" + # Normalize ex. + ex_normalized = ex / np.linalg.norm(ex) + + # make ey perpendicular to ex + ey_normalized = ey - np.dot(ey, ex_normalized) * ex_normalized + ey_normalized /= np.linalg.norm(ey_normalized) + + # compute ez as cross product + eznorm = np.cross(ex_normalized, ey_normalized) + m = np.stack([ex_normalized, ey_normalized, eznorm, translation]).transpose() + m = np.concatenate([m, [[0.0, 0.0, 0.0, 1.0]]], axis=0) + return m + + +# create an array with (restype, atomtype) --> rigid_group_idx +# and an array with (restype, atomtype, coord) for the atom positions +# and compute affine transformation matrices (4,4) from one rigid group to the +# previous group +restype_atom37_to_rigid_group = np.zeros([21, 37], dtype=int) +restype_atom37_mask = np.zeros([21, 37], dtype=np.float32) +restype_atom37_rigid_group_positions = np.zeros([21, 37, 3], dtype=np.float32) +restype_atom14_to_rigid_group = np.zeros([21, 14], dtype=int) +restype_atom14_mask = np.zeros([21, 14], dtype=np.float32) +restype_atom14_rigid_group_positions = np.zeros([21, 14, 3], dtype=np.float32) +restype_rigid_group_default_frame = np.zeros([21, 8, 4, 4], dtype=np.float32) + + +def _make_rigid_group_constants(): + """Fill the arrays above.""" + for restype, restype_letter in enumerate(restypes_with_x): + resname = restype_1to3[restype_letter] + for atomname, group_idx, atom_position in rigid_group_atom_positions[resname]: + atomtype = atom_order[atomname] + restype_atom37_to_rigid_group[restype, atomtype] = group_idx + restype_atom37_mask[restype, atomtype] = 1 + restype_atom37_rigid_group_positions[restype, atomtype, :] = atom_position + + atom14idx = restype_name_to_atom14_names[resname].index(atomname) + restype_atom14_to_rigid_group[restype, atom14idx] = group_idx + restype_atom14_mask[restype, atom14idx] = 1 + restype_atom14_rigid_group_positions[restype, atom14idx, :] = atom_position + + for restype, restype_letter in enumerate(restypes_with_x): + resname = restype_1to3[restype_letter] + atom_positions = { + name: np.array(pos) for name, _, pos in rigid_group_atom_positions[resname] + } + + # backbone to backbone is the identity transform + restype_rigid_group_default_frame[restype, 0, :, :] = np.eye(4) + + # pre-omega-frame to backbone (currently dummy identity matrix) + restype_rigid_group_default_frame[restype, 1, :, :] = np.eye(4) + + # phi-frame to backbone + mat = _make_rigid_transformation_4x4( + ex=atom_positions["N"] - atom_positions["CA"], + ey=np.array([1.0, 0.0, 0.0]), + translation=atom_positions["N"], + ) + restype_rigid_group_default_frame[restype, 2, :, :] = mat + + # psi-frame to backbone + mat = _make_rigid_transformation_4x4( + ex=atom_positions["C"] - atom_positions["CA"], + ey=atom_positions["CA"] - atom_positions["N"], + translation=atom_positions["C"], + ) + restype_rigid_group_default_frame[restype, 3, :, :] = mat + + # chi1-frame to backbone + if chi_angles_mask[restype][0]: + base_atom_names = chi_angles_atoms[resname][0] + base_atom_positions = [atom_positions[name] for name in base_atom_names] + mat = _make_rigid_transformation_4x4( + ex=base_atom_positions[2] - base_atom_positions[1], + ey=base_atom_positions[0] - base_atom_positions[1], + translation=base_atom_positions[2], + ) + restype_rigid_group_default_frame[restype, 4, :, :] = mat + + # chi2-frame to chi1-frame + # chi3-frame to chi2-frame + # chi4-frame to chi3-frame + # luckily all rotation axes for the next frame start at (0,0,0) of the + # previous frame + for chi_idx in range(1, 4): + if chi_angles_mask[restype][chi_idx]: + axis_end_atom_name = chi_angles_atoms[resname][chi_idx][2] + axis_end_atom_position = atom_positions[axis_end_atom_name] + mat = _make_rigid_transformation_4x4( + ex=axis_end_atom_position, + ey=np.array([-1.0, 0.0, 0.0]), + translation=axis_end_atom_position, + ) + restype_rigid_group_default_frame[restype, 4 + chi_idx, :, :] = mat + + +_make_rigid_group_constants() + + +def make_atom14_dists_bounds(overlap_tolerance=1.5, bond_length_tolerance_factor=15.0): + """compute upper and lower bounds for bonds to assess violations.""" + restype_atom14_bond_lower_bound = np.zeros([21, 14, 14], np.float32) + restype_atom14_bond_upper_bound = np.zeros([21, 14, 14], np.float32) + restype_atom14_bond_stddev = np.zeros([21, 14, 14], np.float32) + residue_bonds, residue_virtual_bonds, _ = load_stereo_chemical_props() + for restype, restype_letter in enumerate(restypes): + resname = restype_1to3[restype_letter] + atom_list = restype_name_to_atom14_names[resname] + + # create lower and upper bounds for clashes + for atom1_idx, atom1_name in enumerate(atom_list): + if not atom1_name: + continue + atom1_radius = van_der_waals_radius[atom1_name[0]] + for atom2_idx, atom2_name in enumerate(atom_list): + if (not atom2_name) or atom1_idx == atom2_idx: + continue + atom2_radius = van_der_waals_radius[atom2_name[0]] + lower = atom1_radius + atom2_radius - overlap_tolerance + upper = 1e10 + restype_atom14_bond_lower_bound[restype, atom1_idx, atom2_idx] = lower + restype_atom14_bond_lower_bound[restype, atom2_idx, atom1_idx] = lower + restype_atom14_bond_upper_bound[restype, atom1_idx, atom2_idx] = upper + restype_atom14_bond_upper_bound[restype, atom2_idx, atom1_idx] = upper + + # overwrite lower and upper bounds for bonds and angles + for b in residue_bonds[resname] + residue_virtual_bonds[resname]: + atom1_idx = atom_list.index(b.atom1_name) + atom2_idx = atom_list.index(b.atom2_name) + lower = b.length - bond_length_tolerance_factor * b.stddev + upper = b.length + bond_length_tolerance_factor * b.stddev + restype_atom14_bond_lower_bound[restype, atom1_idx, atom2_idx] = lower + restype_atom14_bond_lower_bound[restype, atom2_idx, atom1_idx] = lower + restype_atom14_bond_upper_bound[restype, atom1_idx, atom2_idx] = upper + restype_atom14_bond_upper_bound[restype, atom2_idx, atom1_idx] = upper + restype_atom14_bond_stddev[restype, atom1_idx, atom2_idx] = b.stddev + restype_atom14_bond_stddev[restype, atom2_idx, atom1_idx] = b.stddev + return { + "lower_bound": restype_atom14_bond_lower_bound, # shape (21,14,14) + "upper_bound": restype_atom14_bond_upper_bound, # shape (21,14,14) + "stddev": restype_atom14_bond_stddev, # shape (21,14,14) + } + + +restype_atom14_ambiguous_atoms = np.zeros((21, 14), dtype=np.float32) +restype_atom14_ambiguous_atoms_swap_idx = np.tile(np.arange(14, dtype=int), (21, 1)) + + +def _make_atom14_ambiguity_feats(): + for res, pairs in residue_atom_renaming_swaps.items(): + res_idx = restype_order[restype_3to1[res]] + for atom1, atom2 in pairs.items(): + atom1_idx = restype_name_to_atom14_names[res].index(atom1) + atom2_idx = restype_name_to_atom14_names[res].index(atom2) + restype_atom14_ambiguous_atoms[res_idx, atom1_idx] = 1 + restype_atom14_ambiguous_atoms[res_idx, atom2_idx] = 1 + restype_atom14_ambiguous_atoms_swap_idx[res_idx, atom1_idx] = atom2_idx + restype_atom14_ambiguous_atoms_swap_idx[res_idx, atom2_idx] = atom1_idx + + +_make_atom14_ambiguity_feats() + + +def aatype_to_str_sequence(aatype): + return "".join([restypes_with_x[aatype[i]] for i in range(len(aatype))]) + + +# NOTE(thayes): These are computed based on the average CA->C and CA->N norm from rigid_group_atom_positions +CA_TO_N_NORM = 1.4591 +CA_TO_C_NORM = 1.5252 + + +def _make_restype_atom37_to_atom14(): + """Map from atom37 to atom14 per residue type.""" + restype_atom37_to_atom14 = [] # mapping (restype, atom37) --> atom14 + for rt in restypes: + atom_names = restype_name_to_atom14_names[restype_1to3[rt]] + atom_name_to_idx14 = {name: i for i, name in enumerate(atom_names)} + restype_atom37_to_atom14.append( + [ + (atom_name_to_idx14[name] if name in atom_name_to_idx14 else 0) + for name in atom_types + ] + ) + + restype_atom37_to_atom14.append([0] * 37) + restype_atom37_to_atom14 = np.array(restype_atom37_to_atom14, dtype=np.int32) + return restype_atom37_to_atom14 + + +def _make_restype_atom14_to_atom37(): + """Map from atom14 to atom37 per residue type.""" + restype_atom14_to_atom37 = [] # mapping (restype, atom14) --> atom37 + for rt in restypes: + atom_names = restype_name_to_atom14_names[restype_1to3[rt]] + restype_atom14_to_atom37.append( + [(atom_order[name] if name else 0) for name in atom_names] + ) + # Add dummy mapping for restype 'UNK' + restype_atom14_to_atom37.append([0] * 14) + restype_atom14_to_atom37 = np.array(restype_atom14_to_atom37, dtype=np.int32) + return restype_atom14_to_atom37 + + +RESTYPE_ATOM14_TO_ATOM37 = _make_restype_atom14_to_atom37() +RESTYPE_ATOM37_TO_ATOM14 = _make_restype_atom37_to_atom14() +CHAIN_BREAK_TOKEN = "|" diff --git a/esm/utils/sampling.py b/esm/utils/sampling.py index 68c5c868..fdf8658d 100644 --- a/esm/utils/sampling.py +++ b/esm/utils/sampling.py @@ -5,18 +5,9 @@ import torch import torch.nn.functional as F -from esm.sdk.api import ( - ESMProteinTensor, - SamplingConfig, - SamplingTrackConfig, -) -from esm.tokenization import ( - TokenizerCollectionProtocol, - get_invalid_tokenizer_ids, -) -from esm.tokenization.function_tokenizer import ( - InterProQuantizedTokenizer, -) +from esm.sdk.api import ESMProteinTensor, SamplingConfig, SamplingTrackConfig +from esm.tokenization import TokenizerCollectionProtocol, get_invalid_tokenizer_ids +from esm.tokenization.function_tokenizer import InterProQuantizedTokenizer from esm.utils.constants.esm3 import ( MAX_RESIDUE_ANNOTATIONS, SASA_DISCRETIZATION_BOUNDARIES, diff --git a/esm/utils/structure/affine3d.py b/esm/utils/structure/affine3d.py index 382abcd5..e2d9b27e 100644 --- a/esm/utils/structure/affine3d.py +++ b/esm/utils/structure/affine3d.py @@ -1,16 +1,17 @@ from __future__ import annotations import typing as T +from abc import ABC from dataclasses import dataclass import torch +from torch.nn import functional as F from typing_extensions import Self from esm.utils.misc import fp32_autocast_context -@T.runtime_checkable -class Rotation(T.Protocol): +class Rotation(ABC): @classmethod def identity(cls, shape: tuple[int, ...], **tensor_kwargs) -> Self: ... @@ -34,6 +35,8 @@ def shape(self) -> torch.Size: def as_matrix(self) -> RotationMatrix: ... + def as_quat(self, normalize: bool = False) -> RotationQuat: ... + def compose(self, other: Self) -> Self: # To be safe, we force users to explicitly convert between rotation types. ... @@ -87,7 +90,8 @@ def __init__(self, rots: torch.Tensor): assert rots.shape[-1] == 3 assert rots.shape[-2] == 3 # Force full precision - self._rots = rots.to(torch.float32) + rots = rots.to(torch.float32) + self._rots = rots @classmethod def identity(cls, shape, **tensor_kwargs): @@ -98,9 +102,7 @@ def identity(cls, shape, **tensor_kwargs): @classmethod def random(cls, shape, **tensor_kwargs): - v1 = torch.randn((*shape, 3), **tensor_kwargs) - v2 = torch.randn((*shape, 3), **tensor_kwargs) - return cls(_graham_schmidt(v1, v2)) + return RotationQuat.random(shape, **tensor_kwargs).as_matrix() def __getitem__(self, idx: T.Any) -> RotationMatrix: indices = (idx,) if isinstance(idx, int) or idx is None else tuple(idx) @@ -113,6 +115,49 @@ def shape(self) -> torch.Size: def as_matrix(self) -> RotationMatrix: return self + def as_quat(self, normalize: bool = False) -> RotationQuat: + m00, m01, m02, m10, m11, m12, m20, m21, m22 = torch.unbind( + self._rots.flatten(-2), dim=-1 + ) + q_abs = _sqrt_subgradient( + torch.stack( + [ + 1.0 + m00 + m11 + m22, + 1.0 + m00 - m11 - m22, + 1.0 - m00 + m11 - m22, + 1.0 - m00 - m11 + m22, + ], + dim=-1, + ) + ) + # we produce the desired quaternion multiplied by each of r, i, j, k + quat_by_rijk = torch.stack( + [ + x + for lst in [ + [q_abs[..., 0] ** 2, m21 - m12, m02 - m20, m10 - m01], + [m21 - m12, q_abs[..., 1] ** 2, m10 + m01, m02 + m20], + [m02 - m20, m10 + m01, q_abs[..., 2] ** 2, m12 + m21], + [m10 - m01, m20 + m02, m21 + m12, q_abs[..., 3] ** 2], + ] + for x in lst + ], + dim=-1, + ).unflatten(-1, (4, 4)) + + # We floor here at 0.1 but the exact level is not important; if q_abs is small, + # the candidate won't be picked. + flr = torch.tensor(0.1).to(dtype=q_abs.dtype, device=q_abs.device) + quat_candidates = quat_by_rijk / (2.0 * q_abs[..., None].max(flr)) + + # if not for numerical problems, quat_candidates[i] should be same (up to a sign), + # forall i; we pick the best-conditioned one (with the largest denominator) + # We manually implement one_hot so torch.compile works + one_hot = torch.zeros_like(q_abs, dtype=torch.bool) + one_hot.scatter_(-1, q_abs.argmax(dim=-1, keepdim=True), True) + quat = quat_candidates[one_hot, :].reshape(q_abs.shape) + return RotationQuat(quat) + def compose(self, other: RotationMatrix) -> RotationMatrix: with fp32_autocast_context(self._rots.device.type): return RotationMatrix(self._rots @ other._rots) @@ -147,6 +192,81 @@ def from_graham_schmidt( return RotationMatrix(_graham_schmidt(x_axis, xy_plane, eps)) +class RotationQuat(Rotation): + def __init__(self, quats: torch.Tensor, normalized=False): + assert quats.shape[-1] == 4 + self._normalized = normalized + # Force float32 as well + if normalized: + self._quats = F.normalize(quats.to(torch.float32), dim=-1) + self._quats = self._quats.where(self._quats[..., :1] >= 0, -self._quats) + else: + self._quats = quats.to(torch.float32) + + @classmethod + def identity(cls, shape, **tensor_kwargs): + q = torch.ones((*shape, 4), **tensor_kwargs) + mult = torch.tensor([1, 0, 0, 0], device=q.device) + return RotationQuat(q * mult) + + @classmethod + def random(cls, shape, **tensor_kwargs): + quat = torch.randn((*shape, 4), **tensor_kwargs) + return RotationQuat(quat, normalized=True) + + def __getitem__(self, idx: T.Any) -> RotationQuat: + indices = (idx,) if isinstance(idx, int) or idx is None else tuple(idx) + return RotationQuat(self._quats[indices + (slice(None),)]) + + @property + def shape(self) -> torch.Size: + return self._quats.shape[:-1] + + def compose(self, other: RotationQuat) -> RotationQuat: + with fp32_autocast_context(self._quats.device.type): + return RotationQuat(_quat_mult(self._quats, other._quats)) + + def convert_compose(self, other: Rotation): + return self.compose(other.as_quat()) + + def as_matrix(self) -> RotationMatrix: + q = self.normalized().tensor + r, i, j, k = torch.unbind(q, -1) + two_s = 2.0 / torch.linalg.norm(q, dim=-1) + + o = torch.stack( + ( + 1 - two_s * (j * j + k * k), + two_s * (i * j - k * r), + two_s * (i * k + j * r), + two_s * (i * j + k * r), + 1 - two_s * (i * i + k * k), + two_s * (j * k - i * r), + two_s * (i * k - j * r), + two_s * (j * k + i * r), + 1 - two_s * (i * i + j * j), + ), + -1, + ) + return RotationMatrix(o.reshape(q.shape[:-1] + (3, 3))) + + def as_quat(self, normalize: bool = False) -> RotationQuat: + return self + + def apply(self, p: torch.Tensor) -> torch.Tensor: + return _quat_rotation(self.normalized()._quats, p) + + def invert(self) -> RotationQuat: + return RotationQuat(_quat_invert(self._quats)) + + @property + def tensor(self) -> torch.Tensor: + return self._quats + + def normalized(self) -> RotationQuat: + return self if self._normalized else RotationQuat(self._quats, normalized=True) + + @dataclass(frozen=True) class Affine3D: trans: torch.Tensor @@ -222,6 +342,9 @@ def tensor_apply(self, func) -> "Affine3D": def as_matrix(self): return Affine3D(trans=self.trans, rot=self.rot.as_matrix()) + def as_quat(self, normalize: bool = False): + return Affine3D(trans=self.trans, rot=self.rot.as_quat(normalize)) + def compose(self, other: "Affine3D", autoconvert: bool = False): rot = self.rot new_rot = (rot.convert_compose if autoconvert else rot.compose)(other.rot) @@ -271,6 +394,13 @@ def from_tensor(t: torch.Tensor) -> "Affine3D": # Assume tensor 4x4 for backward compat with alphafold trans = t[..., :3, 3] rot = RotationMatrix(t[..., :3, :3]) + case 6: + # Assume quaternion representation with real part = 1 + trans = t[..., -3:] + rot = RotationQuat(F.pad(t[..., :3], (1, 0), value=1)) + case 7: + trans = t[..., -3:] + rot = RotationQuat(t[..., :4]) case 12: trans = t[..., -3:] rot = RotationMatrix(t[..., :-3].unflatten(-1, (3, 3))) @@ -305,6 +435,62 @@ def cat(affines: list["Affine3D"], dim: int = 0): return Affine3D.from_tensor(torch.cat([x.tensor for x in affines], dim=dim)) +def _quat_mult(a: torch.Tensor, b: torch.Tensor) -> torch.Tensor: + """ + Multiply two quaternions. + Usual torch rules for broadcasting apply. + + Args: + a: Quaternions as tensor of shape (..., 4), real part first. + b: Quaternions as tensor of shape (..., 4), real part first. + + Returns: + The product of a and b, a tensor of quaternions shape (..., 4). + """ + aw, ax, ay, az = torch.unbind(a, -1) + bw, bx, by, bz = torch.unbind(b, -1) + ow = aw * bw - ax * bx - ay * by - az * bz + ox = aw * bx + ax * bw + ay * bz - az * by + oy = aw * by - ax * bz + ay * bw + az * bx + oz = aw * bz + ax * by - ay * bx + az * bw + return torch.stack((ow, ox, oy, oz), -1) + + +def _quat_rotation(q: torch.Tensor, p: torch.Tensor) -> torch.Tensor: + """ + Rotates p by quaternion q. Usual torch rules for broadcasting apply. + + Args: + q: Quaternions as tensor of shape (..., 4), real part first. + p: Points as tensor of shape (..., 3) + + Returns: + The rotated version of p, of shape (..., 3) + """ + aw, ax, ay, az = torch.unbind(q, -1) + bx, by, bz = torch.unbind(p, -1) + # fmt: off + ow = - ax * bx - ay * by - az * bz + ox = aw * bx + ay * bz - az * by + oy = aw * by - ax * bz + az * bx + oz = aw * bz + ax * by - ay * bx + # fmt: on + q_mul_pts = torch.stack((ow, ox, oy, oz), -1) + return _quat_mult(q_mul_pts, _quat_invert(q))[..., 1:] + + +def _quat_invert(q: torch.Tensor): + return q * torch.tensor([1, -1, -1, -1], device=q.device) + + +def _sqrt_subgradient(x: torch.Tensor) -> torch.Tensor: + # Returns torch.sqrt(torch.max(0, x)) but with a zero subgradient where x is 0. + ret = torch.zeros_like(x) + positive_mask = x > 0 + ret[positive_mask] = torch.sqrt(x[positive_mask]) + return ret + + def _graham_schmidt(x_axis: torch.Tensor, xy_plane: torch.Tensor, eps: float = 1e-12): # A low eps here is necessary for good stability! with fp32_autocast_context(x_axis.device.type): diff --git a/esm/utils/structure/aligner.py b/esm/utils/structure/aligner.py index ec0a3ef1..f25d9987 100644 --- a/esm/utils/structure/aligner.py +++ b/esm/utils/structure/aligner.py @@ -6,17 +6,21 @@ import numpy as np import torch -from esm.utils.structure.protein_structure import ( - compute_affine_and_rmsd, -) +from esm.utils.structure.protein_structure import compute_affine_and_rmsd class Alignable(Protocol): - atom37_positions: np.ndarray - atom37_mask: np.ndarray # Trick to detect whether an object is a dataclass __dataclass_fields__: ClassVar[dict[str, Field[Any]]] + @property + def atom37_positions(self) -> np.ndarray: # type: ignore + pass + + @property + def atom37_mask(self) -> np.ndarray: # type: ignore + pass + def __len__(self) -> int: ... diff --git a/esm/utils/structure/atom_indexer.py b/esm/utils/structure/atom_indexer.py new file mode 100644 index 00000000..d62f05c9 --- /dev/null +++ b/esm/utils/structure/atom_indexer.py @@ -0,0 +1,15 @@ +import numpy as np + +from esm.utils.structure.protein_structure import index_by_atom_name + + +class AtomIndexer: + def __init__(self, structure, property: str, dim: int): + self.structure = structure + self.property = property + self.dim = dim + + def __getitem__(self, atom_names: str | list[str]) -> np.ndarray: + return index_by_atom_name( + getattr(self.structure, self.property), atom_names, self.dim + ) diff --git a/esm/utils/structure/metrics.py b/esm/utils/structure/metrics.py index dfe26ebc..d76ed766 100644 --- a/esm/utils/structure/metrics.py +++ b/esm/utils/structure/metrics.py @@ -1,19 +1,148 @@ +import numpy as np import torch +import torch.nn.functional as F from einops import rearrange +from torch import Tensor +from torch.amp import autocast # type: ignore from esm.utils import residue_constants -from esm.utils.misc import unbinpack +from esm.utils.misc import binpack, unbinpack from esm.utils.structure.protein_structure import ( compute_alignment_tensors, compute_gdt_ts_no_alignment, + compute_rmsd_no_alignment, ) +def contact_precision( + predictions: Tensor, + targets: Tensor, + src_lengths: Tensor | None = None, + minsep: int = 6, + maxsep: int | None = None, + override_length: int | None = None, # for casp +): + """Computes contact precisions. + + For protein contact prediction, precision is measured for the top (L/K) highest confidence predictions, + with L being the length of the protein sequence and K generally being equal to 1 or 5. + + K = 5 measures the predictions of the very highest confidence contacts, while K = 1 is a more general measure + over all relatively high confidence predictions. + + Since there are roughly ~L true contacts in a protein, this is a reasonable cutoff. + + + Args: + predictions (Tensor): Tensor of probabilities of size (B, L, L) + targets (Tensor): Tensor of true contacts of size (B, L, L) + src_lengths (Tensor, optional): Lengths of each sample in the batch, if using variable lengths. + If not provided, inferred from the size of the predictions. + minsep (int): Minimum separation distance to consider. We often want to measure contacts at a + certain range. Typical ranges are short [6, 12), medium [12, 24), and long [24, inf). + maxsep (int, optional): Used in conjunction with minsep to specify a contact range. If not provided uses + assumes no maximum range + override_length (int, optional): Used for casp evaluation where sometimes the "true" length is not + the same as the length of the input. Kept for posterity, we probably don't need this argument. + """ + if predictions.dim() == 2: + predictions = predictions.unsqueeze(0) + if targets.dim() == 2: + targets = targets.unsqueeze(0) + + # Check sizes + if predictions.size() != targets.size(): + raise ValueError( + f"Size mismatch. Received predictions of size {predictions.size()}, " + f"targets of size {targets.size()}" + ) + device = predictions.device + + batch_size, seqlen, _ = predictions.size() + + # Step 1) Construct a mask of size [B, L, L] to mask invalid contacts + seqlen_range = torch.arange(seqlen, device=device) + sep = seqlen_range.unsqueeze(0) - seqlen_range.unsqueeze(1) + sep = sep.unsqueeze(0) + # Mask contacts that are closer than minsep + valid_mask = sep >= minsep + # Mask contacts where target is negative (padding or unknown) + valid_mask = valid_mask & (targets >= 0) # negative targets are invalid + + # Mask contacts that are farther than maxsep, if provided + if maxsep is not None: + valid_mask &= sep < maxsep + + if src_lengths is not None: + # If the lengths of the individual sequences are provided, mask positions + # that are farther than the end of the sequence. + valid = seqlen_range.unsqueeze(0) < src_lengths.unsqueeze(1) + valid_mask &= valid.unsqueeze(1) & valid.unsqueeze(2) + else: + src_lengths = torch.full([batch_size], seqlen, device=device, dtype=torch.long) + + # Fill in the logit tensor with -inf for all invalid positions + predictions = predictions.masked_fill(~valid_mask, float("-inf")) + + # Step 2) Select the top half of the prediction (should be symmetric) + x_ind, y_ind = np.triu_indices(seqlen, minsep) + predictions_upper = predictions[:, x_ind, y_ind] + targets_upper = targets[:, x_ind, y_ind] + + # Step 3) Select the topk values in each batch where k = L (length of sequence) + topk = seqlen if override_length is None else max(seqlen, override_length) + # Indices are the indices into the predictions corresponding to the most confident predictions + indices = predictions_upper.argsort(dim=-1, descending=True)[:, :topk] + # topk_targets are the target values corresponding to the above indices + topk_targets = targets_upper[torch.arange(batch_size).unsqueeze(1), indices] + if topk_targets.size(1) < topk: + # If there aren't enough targets, pad to the output. + topk_targets = F.pad(topk_targets, [0, topk - topk_targets.size(1)]) + + # Step 4) Sum the accuracy at of the top-i predictions for i in 1, L + # topk_targets => 1/0 true vs. false contact, sorted by confidence of prediction + # cmumulative sum => Number of correct answers for the top-i predictions. + cumulative_dist = topk_targets.type_as(predictions).cumsum(-1) + + # Step 5) Find the gather indices. This should be P@(L / K) for varous values of K + # The values will differ for each batch. + gather_lengths = src_lengths.unsqueeze(1) + if override_length is not None: + gather_lengths = override_length * torch.ones_like( + gather_lengths, device=device + ) + + # This gets you (0.1 * L, 0.2 * L, 0.3 * L, etc.) + gather_indices = ( + (torch.arange(0.1, 1.1, 0.1, device=device).unsqueeze(0) * gather_lengths).type( + torch.long + ) + - 1 + ).clamp_min(0) + + # Step 6) Gather the results and divide by the number of guesses to get the precision. + binned_cumulative_dist = cumulative_dist.gather(1, gather_indices) + binned_precisions = binned_cumulative_dist / (gather_indices + 1).type_as( + binned_cumulative_dist + ) + + # Select specific P@L/k. pl5 is index 1 b/c that corresponds to L * 0.2 in + # gather_indices above + pl5 = binned_precisions[:, 1] + # pl2 = binned_precisions[:, 4] + pl = binned_precisions[:, 9] + # AUC is the integral wrt K of P@L/K for K in range(1, L) + auc = binned_precisions.mean(-1) + + return {"AUC": auc, "P@L": pl, "P@L5": pl5} + + def compute_lddt( all_atom_pred_pos: torch.Tensor, all_atom_positions: torch.Tensor, all_atom_mask: torch.Tensor, - cutoff: float = 15.0, + pairwise_all_atom_mask: torch.Tensor | None = None, + cutoff: float | torch.Tensor = 15.0, eps: float = 1e-10, per_residue: bool = True, sequence_id: torch.Tensor | None = None, @@ -29,7 +158,8 @@ def compute_lddt( all_atom_pred_pos (Tensor[float], [(Nstates x) B x (L * Natoms x) 3]): Tensor of predicted positions all_atom_positions (Tensor[float], [B x (L * Natoms x) 3]): Tensor of true positions all_atom_mask (Tensor[float], [B x (L * Natoms)]): Tensor of masks, indicating whether an atom exists. - cutoff (float): Max distance to score lddt over. + pairwise_all_atom_mask (Tensor[float], [B x (L * Natoms x L * Natoms)], optional): Tensor of masks, indicating whether a pair of atoms should be considered in the LDDT calculation. + cutoff (float): Max distance to score lddt over. This can either be a float, or a tensor of shape [B, L, L] to allow for per-residue cutoffs, e.g. if you want to use a different cutoff for nucleic acids. per_residue (bool): Whether to return per-residue or full-protein lddt. sequence_id (Tensor, optional): Sequence id tensor for binpacking. NOTE: only supported for lddt_ca calculations, not when Natoms is passed! @@ -40,7 +170,7 @@ def compute_lddt( else: Tensor[float], [(Nstates x) B] """ - n = all_atom_mask.shape[-2] + all_atom_mask = all_atom_mask[..., None] # add a dimension for broadcasting dmat_true = torch.sqrt( eps + torch.sum( @@ -57,22 +187,56 @@ def compute_lddt( dim=-1, ) ) - dists_to_score = ( - (dmat_true < cutoff) - * all_atom_mask - * rearrange(all_atom_mask, "... a b -> ... b a") - * (1.0 - torch.eye(n, device=all_atom_mask.device)) - ) + mask = all_atom_mask * rearrange(all_atom_mask, "... a b -> ... b a") + if pairwise_all_atom_mask is not None: + mask = mask * pairwise_all_atom_mask if sequence_id is not None: - # TODO(roshan): This will work for lddt_ca, but not for regular lddt + # TODO: This will work for lddt_ca, but not for regular lddt # Problem is that regular lddt has natoms * nres scores, so would need to repeat this mask by natoms # Leaving for now because it won't fail silently so should be ook. seqid_mask = sequence_id[..., None] == sequence_id[..., None, :] - dists_to_score = dists_to_score * seqid_mask.type_as(dists_to_score) + mask = mask * seqid_mask.type_as(mask) + + return compute_lddt_from_dmat( + dmat_pred, dmat_true, mask, cutoff=cutoff, eps=eps, per_residue=per_residue + ) - dist_l1 = torch.abs(dmat_true - dmat_pred) +def compute_lddt_from_dmat( + dmat_pred: torch.Tensor, + dmat_true: torch.Tensor, + pairwise_mask: torch.Tensor, + cutoff: float | torch.Tensor = 15.0, + eps: float = 1e-10, + per_residue: bool = True, +): + """ + Compute LDDT from pre-computed distance matrices. + This is useful when you want to compute LDDT with multiple different masks or cutoffs, e.g. for different molecule types (protein, nucleic acid, etc.). + + Args: + dmat_pred (Tensor[float], [B x L x L]): Predicted distance matrix + dmat_true (Tensor[float], [B x L x L]): True distance matrix + pairwise_mask (Tensor[float], [B x L x L]): Pairwise mask indicating which pairs of atoms to consider + cutoff (float): Max distance to score lddt over. This can either be a float, or a tensor of shape [B, L, L] to allow for per-residue cutoffs, e.g. if you want to use a different cutoff for nucleic acids. + per_residue (bool): Whether to return per-residue or full-protein lddt. + + Returns: + LDDT Tensor: + if per_residue: + Tensor[float], [B x L] + else: + Tensor[float], [B] + """ + n = dmat_true.size(-1) + dists_to_score = ( + (dmat_true < cutoff) + * pairwise_mask + * (1.0 - torch.eye(n, device=dmat_true.device)) + ) + + dist_l1 = torch.abs(dmat_true - dmat_pred) score = ( (dist_l1 < 0.5).type(dist_l1.dtype) + (dist_l1 < 1.0).type(dist_l1.dtype) @@ -84,7 +248,6 @@ def compute_lddt( dims = (-1,) if per_residue else (-2, -1) norm = 1.0 / (eps + torch.sum(dists_to_score, dim=dims)) score = norm * (eps + torch.sum(dists_to_score * score, dim=dims)) - return score @@ -114,6 +277,56 @@ def compute_lddt_ca( ) +# NOTE(roshan): no_grad required for stack_variable_length_tensors apparently... let's revisit if we want to backprop +@torch.no_grad() +@autocast("cuda", enabled=False) +def compute_rmsd( + mobile: torch.Tensor, + target: torch.Tensor, + atom_exists_mask: torch.Tensor | None = None, + sequence_id: torch.Tensor | None = None, + reduction: str = "batch", +): + """ + Compute RMSD between two batches of structures with support for masking invalid atoms using PyTorch. + + Args: + - mobile (torch.Tensor): Batch of coordinates of structure to be superimposed in shape (B, N, 3) + - target (torch.Tensor): Batch of coordinates of structure that is fixed in shape (B, N, 3) + - atom_exists_mask (torch.Tensor, optional): Mask for Whether an atom exists of shape (B, N) + - sequence_id (torch.Tensor, optional): Sequence id tensor for binpacking. + - reduction (str): One of "batch", "per_sample", "per_residue". + + Returns: + If reduction == "batch": + (torch.Tensor): 0-dim, Average Root Mean Square Deviation between the structures for each batch + If reduction == "per_sample": + (torch.Tensor): (B,)-dim, Root Mean Square Deviation between the structures for each batch + If reduction == "per_residue": + (torch.Tensor): (B, N)-dim, Root Mean Square Deviation between the structures for residue in the batch + """ + + (centered_mobile, _, centered_target, _, rotation_matrix, num_valid_atoms) = ( + compute_alignment_tensors( + mobile=mobile, + target=target, + atom_exists_mask=atom_exists_mask, + sequence_id=sequence_id, + ) + ) + + # Apply transformation to centered structure + rotated_mobile = torch.matmul(centered_mobile, rotation_matrix) + + # Compute rmsd for centered structures + rmsd = compute_rmsd_no_alignment( + rotated_mobile, centered_target, num_valid_atoms, reduction=reduction + ) + if reduction == "per_residue" and sequence_id is not None: + rmsd = binpack(rmsd, sequence_id, pad_value=0) + return rmsd + + def compute_gdt_ts( mobile: torch.Tensor, target: torch.Tensor, diff --git a/esm/utils/structure/mmcif_parsing.py b/esm/utils/structure/mmcif_parsing.py new file mode 100644 index 00000000..3509ffb1 --- /dev/null +++ b/esm/utils/structure/mmcif_parsing.py @@ -0,0 +1,469 @@ +from __future__ import annotations + +import functools +import io +import os +from dataclasses import dataclass +from datetime import datetime +from typing import Union + +import biotite.structure as bs +import biotite.structure.io.pdbx as pdbx + +from esm.utils import residue_constants + +# Define PathOrBuffer for the opensource version +PathOrBuffer = Union[str, os.PathLike, io.StringIO] + + +class NoProteinError(Exception): + pass + + +@dataclass +class Residue: + residue_number: int | None = None + insertion_code: str = "" + hetflag: bool = False + + +@dataclass +class MmcifHeader: + release_date: datetime | None = None + resolution: float | None = None + structure_method: str = "UNKNOWN" + + +class MmcifWrapper: + def __init__(self, id: str | None = None): + self.id: str = id or "" + self.raw: pdbx.CIFFile | None = None + self.structure: bs.AtomArray + self.header: MmcifHeader = MmcifHeader() + self.entities: dict[int, list[str]] = {} + self.chain_to_seqres: dict[str, str] = {} + self.seqres_to_structure: dict[str, dict[int, Residue]] = {} + + @classmethod + def read(cls, path: PathOrBuffer, id: str | None = None) -> MmcifWrapper: + obj = cls(id=id) + obj._load(path) + return obj + + def _load(self, path: PathOrBuffer, fileid: str | None = None): + """Load mmCIF data from file.""" + self.raw = pdbx.CIFFile.read(path) + + self._parse_structure() + self._parse_header() + self._parse_entities() + self._parse_sequences() + + def _parse_structure(self): + """Parse the atomic structure from mmCIF.""" + try: + structure = pdbx.get_structure(self.raw, model=1) + if structure is None or not isinstance(structure, bs.AtomArray): + raise NoProteinError("No structure found in mmCIF file") + if len(structure) == 0: + raise NoProteinError("Empty structure in mmCIF file") + self.structure = structure + except Exception as e: + raise ValueError(f"Failed to parse structure: {e}") + + def _parse_header(self): + """Parse header information from mmCIF.""" + if not self.raw: + return + + try: + # Get the first (and usually only) block + block = self.raw.block + + # Parse release date + if "pdbx_database_status" in block: + status_cat = block["pdbx_database_status"] + if "recvd_initial_deposition_date" in status_cat: + date_str = status_cat["recvd_initial_deposition_date"].as_item() + if date_str and date_str != "?": + try: + self.header.release_date = datetime.strptime( + date_str, "%Y-%m-%d" + ) + except ValueError: + pass + + # Parse resolution + if "refine" in block: + refine_cat = block["refine"] + if "ls_d_res_high" in refine_cat: + res_str = refine_cat["ls_d_res_high"].as_item() + if res_str and res_str != "?": + try: + self.header.resolution = float(res_str) + except ValueError: + pass + + # Parse structure method + if "exptl" in block: + exptl_cat = block["exptl"] + if "method" in exptl_cat: + method = exptl_cat["method"].as_item() + if method and method != "?": + self.header.structure_method = method.upper() + + except Exception: + # If parsing fails, keep default values + pass + + def _parse_entities(self): + """Parse entity information and map to chains.""" + if not self.raw: + return + + try: + block = self.raw.block + + # Parse entity information + if "entity" in block: + entity_cat = block["entity"] + entity_ids = entity_cat["id"].as_array(str) + entity_types = entity_cat["type"].as_array(str) + + # Initialize entities dict with all entities (not just polymers) + for i, (entity_id, entity_type) in enumerate( + zip(entity_ids, entity_types) + ): + self.entities[int(entity_id)] = [] + + # Map polymer chains to entities using entity_poly + if "entity_poly" in block: + poly_cat = block["entity_poly"] + entity_ids = poly_cat["entity_id"].as_array(str) + chain_lists = poly_cat["pdbx_strand_id"].as_array(str) + + for entity_id, chain_list in zip(entity_ids, chain_lists): + entity_id = int(entity_id) + # Chain list is comma-separated + chains = [c.strip() for c in chain_list.split(",") if c.strip()] + if entity_id in self.entities: + self.entities[entity_id] = chains + + # Map non-polymer chains using struct_asym for entities not covered by entity_poly + if "struct_asym" in block: + asym_cat = block["struct_asym"] + asym_ids = asym_cat["id"].as_array(str) + entity_ids = asym_cat["entity_id"].as_array(str) + + for asym_id, entity_id in zip(asym_ids, entity_ids): + entity_id = int(entity_id) + # Only add if entity exists but has no chains yet (non-polymer entities) + if entity_id in self.entities and not self.entities[entity_id]: + self.entities[entity_id].append(asym_id) + + except Exception: + # If parsing fails, try to infer from structure + if ( + self.structure + and hasattr(self.structure, "chain_id") + and self.structure.chain_id is not None + and hasattr(self.structure.chain_id, "__iter__") + ): + chain_ids = list(set(self.structure.chain_id)) + self.entities = {1: chain_ids} + + def _parse_sequences(self): + """Parse sequence information from mmCIF.""" + if not self.raw: + return + + block = self.raw.block + + # Parse polymer sequences + if "entity_poly" in block: + poly_cat = block["entity_poly"] + entity_ids = poly_cat["entity_id"].as_array(str) + sequences = poly_cat["pdbx_seq_one_letter_code_can"].as_array(str) + chain_lists = poly_cat["pdbx_strand_id"].as_array(str) + + for entity_id, sequence, chain_list in zip( + entity_ids, sequences, chain_lists + ): + # Clean up sequence (remove whitespace and newlines) + clean_seq = "".join(sequence.split()) + chains = [c.strip() for c in chain_list.split(",") if c.strip()] + + for chain_id in chains: + self.chain_to_seqres[chain_id] = clean_seq + + # Parse sequence to structure mapping + if "pdbx_poly_seq_scheme" in block: + seq_cat = block["pdbx_poly_seq_scheme"] + asym_ids = seq_cat["asym_id"].as_array(str) # Internal chain IDs + seq_positions = seq_cat["seq_id"].as_array(str) + auth_seq_nums = seq_cat["auth_seq_num"].as_array(str) + ins_codes = ( + seq_cat["pdb_ins_code"].as_array(str) + if "pdb_ins_code" in seq_cat + else [""] * len(asym_ids) + ) + hetflags = ( + seq_cat["hetflag"].as_array(str) + if "hetflag" in seq_cat + else ["N"] * len(asym_ids) + ) + + # Get author chain IDs if available + auth_chain_ids = ( + seq_cat["pdb_strand_id"].as_array(str) + if "pdb_strand_id" in seq_cat + else asym_ids # Fallback to internal IDs + ) + + # Build mapping from internal chain ID to author chain ID + asym_to_auth_mapping = {} + for asym_id, auth_id in zip(asym_ids, auth_chain_ids): + asym_to_auth_mapping[asym_id] = auth_id + + # Group by internal chain ID first, then map to author chain ID + chain_data = {} + for asym_id, seq_pos, auth_seq, ins_code, hetflag in zip( + asym_ids, seq_positions, auth_seq_nums, ins_codes, hetflags + ): + if asym_id not in chain_data: + chain_data[asym_id] = {} + + try: + seq_index = int(seq_pos) - 1 # Convert to 0-based indexing + res_num = int(auth_seq) if auth_seq != "?" else None + except ValueError: + continue + + if res_num is not None: + # Convert mmCIF "." and "?" to empty string + clean_ins_code = "" if ins_code in [".", "?"] else ins_code + else: + clean_ins_code = "" + res_num = None + + is_het = hetflag.upper() == "Y" # type: ignore + chain_data[asym_id][seq_index] = Residue( + residue_number=res_num, + insertion_code=clean_ins_code, # type: ignore + hetflag=is_het, + ) + + # Handle cases where multiple residues have the same auth_seq_num + # by adjusting residue numbers to be unique within each chain + for asym_id, residue_data in chain_data.items(): + # Check if there are duplicate residue numbers in this chain + positions_with_same_num = {} + for seq_idx, res_at_pos in residue_data.items(): + if res_at_pos.residue_number is not None: + res_num = res_at_pos.residue_number + if res_num not in positions_with_same_num: + positions_with_same_num[res_num] = [] + positions_with_same_num[res_num].append(seq_idx) + + # Fix duplicate residue numbers by making them sequential + for res_num, seq_indices in positions_with_same_num.items(): + if len(seq_indices) > 1: + # Multiple residues have the same residue number + # Make them sequential starting from the original number + seq_indices.sort() # Ensure consistent ordering + for i, seq_idx in enumerate(seq_indices): + original_pos = residue_data[seq_idx] + new_pos = Residue( + residue_number=res_num + i, + insertion_code=original_pos.insertion_code, + hetflag=original_pos.hetflag, + ) + residue_data[seq_idx] = new_pos + + # Create ordered mappings using author chain IDs + for asym_id in chain_data: + auth_chain_id = asym_to_auth_mapping.get(asym_id, asym_id) + if auth_chain_id in self.chain_to_seqres: + seq_len = len(self.chain_to_seqres[auth_chain_id]) + ordered_mapping = {} + + for i in range(seq_len): + if i in chain_data[asym_id]: + ordered_mapping[i] = chain_data[asym_id][i] + else: + # Missing residue - no structure coordinates + ordered_mapping[i] = Residue( + residue_number=None, insertion_code="", hetflag=False + ) + + self.seqres_to_structure[auth_chain_id] = ordered_mapping + else: + # Handle case where auth_chain_id is not in chain_to_seqres + # This can happen if the chain is not a polymer or if there's a parsing issue + # Create a basic mapping based on the chain_data + if chain_data[asym_id]: + # Sort by sequence index to create ordered mapping + sorted_indices = sorted(chain_data[asym_id].keys()) + ordered_mapping = {} + for i, seq_idx in enumerate(sorted_indices): + ordered_mapping[i] = chain_data[asym_id][seq_idx] + self.seqres_to_structure[auth_chain_id] = ordered_mapping + + # Ensure all chains have complete mappings + for chain_id in self.chain_to_seqres: + if chain_id not in self.seqres_to_structure: + seq_len = len(self.chain_to_seqres[chain_id]) + self.seqres_to_structure[chain_id] = { + i: Residue(residue_number=None, insertion_code="", hetflag=False) + for i in range(seq_len) + } + else: + # Fill in any missing indices + seq_len = len(self.chain_to_seqres[chain_id]) + mapping = self.seqres_to_structure[chain_id] + for i in range(seq_len): + if i not in mapping: + mapping[i] = Residue( + residue_number=None, insertion_code="", hetflag=False + ) + + # Fallback: create basic mappings from structure for missing chains + if ( + self.structure + and hasattr(self.structure, "chain_id") + and self.structure.chain_id is not None + and hasattr(self.structure.chain_id, "__iter__") + ): + for chain_id in set(self.structure.chain_id): + if chain_id not in self.seqres_to_structure: + chain_structure = self.structure[ + self.structure.chain_id == chain_id + ] + if ( + hasattr(chain_structure, "res_id") + and chain_structure.res_id is not None + and hasattr(chain_structure.res_id, "__iter__") + ): + residue_ids = list(set(chain_structure.res_id)) + residue_ids.sort() + + self.seqres_to_structure[chain_id] = { + i: Residue( + residue_number=res_id, insertion_code="", hetflag=False + ) + for i, res_id in enumerate(residue_ids) + } + + def _parse_nonpoly_from_mmcif(self) -> dict[tuple, bs.AtomArray]: + """Parse non-polymer coordinates from mmCIF block data.""" + nonpoly_coords = {} + + # Get non-polymer entities from the mmCIF block + assert self.raw is not None + block = self.raw.block + nonpoly_entities = set() + + # Find non-polymer entities + if "entity" in block: + entity_cat = block["entity"] + entity_ids = entity_cat["id"].as_array(str) + entity_types = entity_cat["type"].as_array(str) + + for entity_id, entity_type in zip(entity_ids, entity_types): + if entity_type.upper() in ["NON-POLYMER", "WATER", "BRANCHED"]: + nonpoly_entities.add(entity_id) + + # Map entities to chains for non-polymers + entity_to_chains = {} + if "pdbx_entity_nonpoly" in block: + nonpoly_cat = block["pdbx_entity_nonpoly"] + entity_ids = nonpoly_cat["entity_id"].as_array(str) + comp_ids = nonpoly_cat["comp_id"].as_array(str) + + for entity_id, comp_id in zip(entity_ids, comp_ids): + if entity_id in nonpoly_entities: + entity_to_chains[entity_id] = comp_id + + # Get atom site information for non-polymers + if "atom_site" in block: + atom_cat = block["atom_site"] + atom_chain_ids = atom_cat["label_asym_id"].as_array(str) + atom_entity_ids = atom_cat["label_entity_id"].as_array(str) + atom_comp_ids = atom_cat["label_comp_id"].as_array(str) + + # Group non-polymer atoms by entity and chain + nonpoly_atom_groups = {} + for i, (chain_id, entity_id, comp_id) in enumerate( + zip(atom_chain_ids, atom_entity_ids, atom_comp_ids) + ): + if entity_id in nonpoly_entities: + key = (comp_id, chain_id) + if key not in nonpoly_atom_groups: + nonpoly_atom_groups[key] = [] + nonpoly_atom_groups[key].append(i) + + # Extract coordinates for each non-polymer group + for (comp_id, chain_id), atom_indices in nonpoly_atom_groups.items(): + # Match atoms by comparing chain_id and residue name + structure_mask = (self.structure.chain_id == chain_id) & ( + self.structure.res_name == comp_id + ) + + if structure_mask.any(): + nonpoly_array = self.structure[structure_mask] + if ( + isinstance(nonpoly_array, (bs.AtomArray, bs.AtomArrayStack)) + and len(nonpoly_array) > 0 + ): + nonpoly_coords[(comp_id, chain_id)] = nonpoly_array + + return nonpoly_coords + + def _parse_nonpoly_fallback(self) -> dict[tuple, bs.AtomArray]: + """Fallback method to extract heteroatoms directly from structure.""" + nonpoly_coords = {} + + if not (self.structure and hasattr(self.structure, "chain_id")): + return nonpoly_coords + + # Create set of standard residues from residue_constants + standard_residues = set(residue_constants.resnames[:-1]) # Exclude 'UNK' + standard_residues.update({"A", "C", "G", "T", "U"}) # Add nucleic acids + + if hasattr(self.structure, "chain_id") and self.structure.chain_id is not None: + for chain_id in set(self.structure.chain_id): + chain_structure = self.structure[self.structure.chain_id == chain_id] + + # Find non-standard residues + if ( + hasattr(chain_structure, "res_name") + and chain_structure.res_name is not None + and hasattr(chain_structure.res_name, "__iter__") + ): + for res_name in set(chain_structure.res_name): + if res_name not in standard_residues: + res_mask = (chain_structure.chain_id == chain_id) & ( + chain_structure.res_name == res_name + ) + if res_mask.any() and isinstance( + chain_structure, (bs.AtomArray, bs.AtomArrayStack) + ): + nonpoly_array = chain_structure[res_mask] + nonpoly_coords[(res_name, chain_id)] = nonpoly_array + + return nonpoly_coords + + @functools.cached_property + def non_polymer_coords(self) -> dict[tuple, bs.AtomArray]: + """ + Extract non-polymer coordinates (ligands, cofactors, etc.) from mmCIF structure. + + Returns a dictionary mapping (nonpolymer_info, chain_id) tuples to AtomArrays. + """ + if not self.structure or not self.raw: + return {} + + try: + return self._parse_nonpoly_from_mmcif() + except Exception: + return self._parse_nonpoly_fallback() diff --git a/esm/utils/structure/protein_chain.py b/esm/utils/structure/protein_chain.py index 4fcc5dfd..4889886e 100644 --- a/esm/utils/structure/protein_chain.py +++ b/esm/utils/structure/protein_chain.py @@ -1,10 +1,11 @@ from __future__ import annotations import io +import warnings from dataclasses import asdict, dataclass, replace from functools import cached_property from pathlib import Path -from typing import Sequence, TypeVar, Union +from typing import Any, Mapping, Sequence import biotite.structure as bs import brotli @@ -12,51 +13,36 @@ import msgpack_numpy import numpy as np import torch -from Bio.Data import PDBData -from biotite.application.dssp import DsspApp from biotite.database import rcsb from biotite.structure.io.pdb import PDBFile -from cloudpathlib import CloudPath -from scipy.spatial import ConvexHull -from scipy.spatial.distance import pdist, squareform -from torch import Tensor +from biotite.structure.io.pdbx import CIFCategory, CIFColumn, CIFData, CIFFile +from biotite.structure.io.pdbx import set_structure as set_structure_pdbx +from scipy.spatial import ConvexHull, KDTree +from scipy.spatial.distance import cdist, pdist, squareform -from esm.utils import residue_constants as RC -from esm.utils.constants import esm3 as C +from esm.utils import residue_constants from esm.utils.misc import slice_python_object_as_numpy from esm.utils.structure.affine3d import Affine3D from esm.utils.structure.aligner import Aligner -from esm.utils.structure.metrics import compute_lddt_ca +from esm.utils.structure.atom_indexer import AtomIndexer +from esm.utils.structure.metrics import compute_gdt_ts, compute_lddt_ca +from esm.utils.structure.mmcif_parsing import MmcifWrapper, Residue from esm.utils.structure.normalize_coordinates import ( apply_frame_to_coords, get_protein_normalization_frame, - normalize_coordinates, ) +from esm.utils.structure.protein_structure import index_by_atom_name +from esm.utils.types import PathOrBuffer msgpack_numpy.patch() - CHAIN_ID_CONST = "A" -ArrayOrTensor = TypeVar("ArrayOrTensor", np.ndarray, Tensor) -PathLike = Union[str, Path, CloudPath] -PathOrBuffer = Union[PathLike, io.StringIO] - - -def index_by_atom_name( - atom37: ArrayOrTensor, atom_names: str | list[str], dim: int = -2 -) -> ArrayOrTensor: - squeeze = False - if isinstance(atom_names, str): - atom_names = [atom_names] - squeeze = True - indices = [RC.atom_order[atom_name] for atom_name in atom_names] - dim = dim % atom37.ndim - index = tuple(slice(None) if dim != i else indices for i in range(atom37.ndim)) - result = atom37[index] # type: ignore - if squeeze: - result = result.squeeze(dim) - return result +def _num_non_null_residues(seqres_to_structure_chain: Mapping[int, Residue]) -> int: + return sum( + residue.residue_number is not None + for residue in seqres_to_structure_chain.values() + ) def infer_CB(C, N, Ca, L: float = 1.522, A: float = 1.927, D: float = -2.143): @@ -78,34 +64,84 @@ def infer_CB(C, N, Ca, L: float = 1.522, A: float = 1.927, D: float = -2.143): return Ca + sum([m * d for m, d in zip(m, d)]) -class AtomIndexer: - def __init__(self, structure: ProteinChain, property: str, dim: int): - self.structure = structure - self.property = property - self.dim = dim - - def __getitem__(self, atom_names: str | list[str]) -> np.ndarray: - return index_by_atom_name( - getattr(self.structure, self.property), atom_names, self.dim - ) - - -@dataclass +def chain_to_ndarray( + atom_array: bs.AtomArray, mmcif: MmcifWrapper, chain_id: str, is_predicted=False +): + entity_id = None + for entity, chains in mmcif.entities.items(): + if chain_id in chains: + entity_id = entity + num_res = len(mmcif.chain_to_seqres[chain_id]) + sequence = mmcif.chain_to_seqres[chain_id] + + atom_positions = np.full([num_res, residue_constants.atom_type_num, 3], np.nan) + atom_mask = np.full([num_res, residue_constants.atom_type_num], False, dtype=bool) + residue_index = np.full([num_res], -1, dtype=np.int64) + insertion_code = np.full([num_res], "", dtype=" bs.AtomArray: chain_id="A" if self.chain_id is None else self.chain_id, res_id=res_idx, ins_code=ins_code, - res_name=RC.restype_1to3.get(res_name, "UNK"), + res_name=residue_constants.restype_1to3.get(res_name, "UNK"), hetero=False, - atom_name=RC.atom_types[i], - element=RC.atom_types[i][0], + atom_name=residue_constants.atom_types[i], + element=residue_constants.atom_types[i][0], b_factor=conf, ) atoms.append(atom) @@ -182,18 +218,20 @@ def atom_array_no_insertions(self) -> bs.AtomArray: # hard coded to as we currently only support single chain structures chain_id=CHAIN_ID_CONST, res_id=res_idx + 1, - res_name=RC.restype_1to3.get(res_name, "UNK"), + res_name=residue_constants.restype_1to3.get(res_name, "UNK"), hetero=False, - atom_name=RC.atom_types[i], - element=RC.atom_types[i][0], + atom_name=residue_constants.atom_types[i], + element=residue_constants.atom_types[i][0], b_factor=conf, ) atoms.append(atom) return bs.array(atoms) - def __getitem__(self, idx: int | list[int] | slice | np.ndarray): + def __getitem__(self, idx: int | list[int] | slice | np.ndarray | torch.Tensor): if isinstance(idx, int): idx = [idx] + if isinstance(idx, torch.Tensor): + idx = idx.cpu().numpy() sequence = slice_python_object_as_numpy(self.sequence, idx) return replace( @@ -216,17 +254,6 @@ def cbeta_contacts(self, distance_threshold: float = 8.0) -> np.ndarray: np.fill_diagonal(contacts, -1) return contacts - def to_structure_encoder_inputs( - self, should_normalize_coordinates: bool = True - ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]: - coords = torch.tensor(self.atom37_positions, dtype=torch.float32) - plddt = torch.tensor(self.confidence, dtype=torch.float32) - residue_index = torch.tensor(self.residue_index, dtype=torch.long) - - if should_normalize_coordinates: - coords = normalize_coordinates(coords) - return coords.unsqueeze(0), plddt.unsqueeze(0), residue_index.unsqueeze(0) - def to_pdb(self, path: PathOrBuffer, include_insertions: bool = True): """Dssp works better w/o insertions.""" f = PDBFile() @@ -242,11 +269,77 @@ def to_pdb_string(self, include_insertions: bool = True) -> str: buf.seek(0) return buf.read() + def to_mmcif(self, path: PathOrBuffer): + f = CIFFile() + set_structure_pdbx(f, self.atom_array, data_block=self.id) + + # incantations molstar needs to render pLDDT / confidence onto + # the structure with "alphafold-view" + f.block["ma_qa_metric"] = CIFCategory( + name="ma_qa_metric", + columns={ + "id": CIFColumn(data=CIFData(array=np.array([1, 2]), dtype=np.int64)), + "mode": CIFColumn( + data=CIFData(array=np.array(["global", "local"]), dtype=np.str_) + ), + "name": CIFColumn( + data=CIFData(array=np.array(["pLDDT", "pLDDT"]), dtype=np.str_) + ), + }, + ) + + # table is a duplicate of data already in the atom array, but + # needed by molstar to render pLDDT / confidence + resid_pldd_table = { + # hard coded to as we currently only support single chain structures + "label_asym_id": CIFColumn( + data=CIFData( + array=[CHAIN_ID_CONST] * len(self.residue_index), dtype=np.str_ + ) + ), + "label_comp_id": CIFColumn( + data=CIFData( + array=[ + residue_constants.restype_1to3.get(c, "UNK") + for c in self.sequence + ], + dtype=np.str_, + ) + ), + "label_seq_id": CIFColumn( + data=CIFData(array=self.residue_index, dtype=np.int64) + ), + "ordinal_id": CIFColumn( + data=CIFData(array=self.residue_index, dtype=np.int64) + ), + # hard coded to show these are all local plDDT values + "metric_id": CIFColumn( + data=CIFData(array=["2"] * len(self.residue_index), dtype=np.str_) + ), + "metric_value": CIFColumn( + data=CIFData(array=self.confidence, dtype=np.float32) + ), + # hard coded to show there are the initial version, there are no revisions + "model_id": CIFColumn( + data=CIFData(array=["1"] * len(self.residue_index), dtype=np.str_) + ), + } + f.block["ma_qa_metric_local"] = CIFCategory( + name="ma_qa_metric_local", columns=resid_pldd_table + ) + f.write(path) + + def to_mmcif_string(self) -> str: + buf = io.StringIO() + self.to_mmcif(buf) + buf.seek(0) + return buf.read() + def state_dict(self, backbone_only=False, json_serializable=False): """This state dict is optimized for storage, so it turns things to fp16 whenever possible. Note that we also only support int32 residue indices, I'm hoping we don't need more than 2**32 residues...""" - dct = {k: v for k, v in asdict(self).items()} + dct = {k: v for k, v in asdict(self).items() if k not in ["mmcif"]} if backbone_only: dct["atom37_mask"][:, 3:] = False dct["atom37_positions"] = dct["atom37_positions"][dct["atom37_mask"]] @@ -265,7 +358,11 @@ def state_dict(self, backbone_only=False, json_serializable=False): return dct def to_blob(self, backbone_only=False) -> bytes: - return brotli.compress(msgpack.dumps(self.state_dict(backbone_only))) + return brotli.compress(msgpack.dumps(self.state_dict(backbone_only)), quality=5) + + @classmethod + def from_open_source(cls, pc: ProteinChain): + return cls(**vars(pc)) @classmethod def from_state_dict(cls, dct): @@ -280,11 +377,11 @@ def from_state_dict(cls, dct): k: (v.astype(np.float32) if k in ["atom37_positions", "confidence"] else v) for k, v in dct.items() } - return cls(**dct) + return cls(**dct, mmcif=None) @classmethod def from_blob(cls, input: Path | str | io.BytesIO | bytes): - """NOTE: blob + sparse coding + brotli + fp16 reduces memory + """NOTE(@zlin): blob + sparse coding + brotli + fp16 reduces memory of chains from 52G/1M chains to 20G/1M chains, I think this is a good first shot at compressing and dumping chains to disk. I'm sure there's better ways.""" match input: @@ -296,24 +393,105 @@ def from_blob(cls, input: Path | str | io.BytesIO | bytes): bytes = input return cls.from_state_dict(msgpack.loads(brotli.decompress(bytes))) - def dssp(self): - dssp = DsspApp.annotate_sse(self.atom_array_no_insertions) - full_dssp = np.full(len(self.sequence), "X", dtype=" np.ndarray: + """Computes per-atom SAP score. + Can optionally aggregate by residue (by averaging over atoms. NOTE: this returns values only for residues that have coordinates!) + or full-protein (sum of SAP score for atoms with SAP > 0, as in Lauer et al. 2011).""" + sap_radius = 5.0 + arr = self.atom_array_no_insertions + + # asserts to avoid type errors assert arr.res_id is not None - assert np.array_equal( - np.sort(np.unique(arr.res_id)), np.arange(1, arr.res_id.max() + 1) - ), "SASA calculation expected contiguous res_ids in range(1, len(chain)+1)" - # NOTE: arr.res_id is 1-indexed, but np.bincount returns a sum for bin 0, so we strip. - sasa_per_residue = np.bincount(arr.res_id, weights=sasa_per_atom)[1:] - assert len(sasa_per_residue) == len(self) - return sasa_per_residue + assert arr.res_name is not None + assert arr.atom_name is not None + assert arr.coord is not None + + # compute SASA and residue-specific properties + sasa_per_atom = self.sasa(by_residue=False) + resid_to_resname = dict(zip(arr.res_id, arr.res_name)) + + max_side_chain_asa = np.full(len(self), np.nan) + res_hydrophobicity = np.full(len(self), np.nan) + resolved_res_mask = self.atom37_mask.any(-1) + num_trailing_residues = len(self) - arr.res_id.max() + + max_side_chain_asa[resolved_res_mask] = np.array( + [ + residue_constants.side_chain_asa[resid_to_resname[i]] + for i in np.unique(arr.res_id) + ] + ) + res_hydrophobicity[resolved_res_mask] = np.array( + [ + residue_constants.hydrophobicity[resid_to_resname[i]] + for i in np.unique(arr.res_id) + ] + ) + assert len(max_side_chain_asa) == len(self) + assert len(res_hydrophobicity) == len(self) + + # compute SAP score + is_side_chain = ~bs.filter_peptide_backbone(arr) + sasa_per_atom[is_side_chain] = 0 + kdtree = KDTree(arr.coord) + neighbors = kdtree.query_ball_tree(kdtree, sap_radius, p=2.0) + sap_by_atom = np.zeros_like(sasa_per_atom) + for i, nn_list in enumerate(neighbors): + saa_nn = np.zeros_like(sasa_per_atom) + saa_nn[nn_list] = sasa_per_atom[nn_list] + sasa_within_r = np.concatenate( + [ + np.bincount(arr.res_id, weights=saa_nn)[1:], + np.zeros(num_trailing_residues), + ] + ) + sap = np.nansum((sasa_within_r / max_side_chain_asa) * res_hydrophobicity) + sap_by_atom[i] = sap + + match aggregation: + case "atom": + return sap_by_atom + case "residue": + sap_by_residue = np.concatenate( + [ + np.bincount(arr.res_id, weights=sap_by_atom)[1:], + np.zeros(num_trailing_residues), + ] + ) / ( + np.concatenate( + [np.bincount(arr.res_id)[1:], np.zeros(num_trailing_residues)] + ) + + 1e-8 + ) + sap_by_residue[~resolved_res_mask] = np.nan + assert len(sap_by_residue) == len(self) + return sap_by_residue + case "protein": + return sum(sap_by_atom[sap_by_atom > 0]) # pyright: ignore[reportReturnType] + case _: + raise ValueError( + f"Invalid aggregation method: {aggregation}. Must be one of 'atom', 'residue', or 'protein'" + ) def globularity(self) -> float: # Computes globularity using total volumes divided by MVEE. @@ -325,10 +503,10 @@ def globularity(self) -> float: # NOTE(@zeming): due to the approximation we make here, that atoms never overlap, you might get >1 globularity mask = self.atom37_mask.any(-1) points = self.atom37_positions[self.atom37_mask] - sequence = [aa for aa, m in zip(self.sequence, mask) if m] + sequence = [aa for aa, m in zip(self.sequence, mask) if m] # type: ignore A, _ = self._mvee(points, tol=1e-3) mvee_volume = (4 * np.pi) / (3 * np.sqrt(np.linalg.det(A))) - volume = sum(RC.amino_acid_volumes[x] for x in sequence) + volume = sum(residue_constants.amino_acid_volumes[x] for x in sequence) ratio = volume / mvee_volume # The paper says you must compare the ellipsoidal profile with T, a measurement of @@ -388,6 +566,10 @@ def _mvee(P: np.ndarray, tol, max_iter=10000): return A, c + def radius_of_gyration(self): + arr = self.atom_array_no_insertions + return bs.gyration_radius(arr) + def align( self, target: ProteinChain, @@ -462,8 +644,8 @@ def lddt_ca( target_inds: list[int] | np.ndarray | None = None, **kwargs, ) -> float | np.ndarray: - """Compute the LDDT between this protein chain and another. - NOTE: LDDT IS NOT SYMMETRIC. The call should always be prediction.lddt_ca(native). + """Compute the LDDT between this protein chain and another. NOTE: LDDT IS NOT SYMMETRIC. + The call should always be prediction.lddt_ca(native). Arguments: native (ProteinChain): The ground truth protein chain @@ -482,6 +664,171 @@ def lddt_ca( ) return float(lddt) if lddt.numel() == 1 else lddt.numpy().flatten() + def gdt_ts( + self, + target: ProteinChain, + mobile_inds: list[int] | np.ndarray | None = None, + target_inds: list[int] | np.ndarray | None = None, + **kwargs, + ) -> float | np.ndarray: + """Compute the GDT_TS between this protein chain and another. + + Arguments: + target (ProteinChain): The other protein chain to compare to. + mobile_inds (list[int], np.ndarray, optional): The indices of the mobile atoms to align. These are NOT residue indices + target_inds (list[int], np.ndarray, optional): The indices of the target atoms to align. These are NOT residue indices + + Returns: + float: The GDT_TS score between the two protein chains. + """ + gdt_ts = compute_gdt_ts( + mobile=torch.tensor( + index_by_atom_name(self.atom37_positions[mobile_inds], "CA"), + dtype=torch.float32, + ).unsqueeze(0), + target=torch.tensor( + index_by_atom_name(target.atom37_positions[target_inds], "CA"), + dtype=torch.float32, + ).unsqueeze(0), + atom_exists_mask=torch.tensor( + index_by_atom_name(self.atom37_mask[mobile_inds], "CA", dim=-1) + & index_by_atom_name(target.atom37_mask[target_inds], "CA", dim=-1) + ).unsqueeze(0), + **kwargs, + ) + return float(gdt_ts) if gdt_ts.numel() == 1 else gdt_ts.numpy().flatten() + + @classmethod + def chain_iterable_from_mmcif( + cls, + path: PathOrBuffer | MmcifWrapper, + id: str | None = None, + is_predicted: bool = False, + keep_source: bool = False, + ): + """Return a list[ProteinChain] object from an mmcif file, a iterable list of all protein chain + from an mmcif file + """ + if isinstance(path, MmcifWrapper): + mmcif = path + else: + mmcif = MmcifWrapper.read(path, id) + for chain in bs.chain_iter(mmcif.structure): + chain = chain[bs.filter_amino_acids(chain) & ~chain.hetero] + if len(chain) == 0: + continue + chain_id = chain.chain_id[0] + entity_id = None + for entity, chains in mmcif.entities.items(): + if chain_id in chains: + entity_id = entity + assert entity_id is not None + ( + sequence, + atom_positions, + atom_mask, + residue_index, + insertion_code, + confidence, + _, + ) = chain_to_ndarray(chain, mmcif, chain_id, is_predicted) + assert all(sequence), "Some residue name was not specified correctly" + + yield cls( + id=mmcif.id, + sequence=sequence, + chain_id=chain_id, + entity_id=entity_id, + atom37_positions=atom_positions, + atom37_mask=atom_mask, + residue_index=residue_index, + insertion_code=insertion_code, + confidence=confidence, + mmcif=mmcif if keep_source else None, + ) + + @classmethod + def from_mmcif( + cls, + path: PathOrBuffer | MmcifWrapper, + chain_id: str | None = None, + entity_id: int | None = None, + id: str | None = None, + is_predicted: bool = False, + keep_source: bool = False, + ): + """Return a ProteinChain object from an mmcif file. + + Args: + path (str | Path | io.TextIO): Path or buffer to read mmcif file from. Should be uncompressed. + id (str, optional): String identifier to assign to structure. Will attempt to infer otherwise. + is_predicted (bool): If True, reads b factor as the confidence readout. Default: False. + chain_id (str, optional): Select a chain corresponding to (author) chain id. + entity_id (int, optional): Select a chain corresponding to a particular entity. + + If neither `chain_id` nor `entity_id` is specified, defaults to the first entity. + """ + if isinstance(path, MmcifWrapper): + mmcif = path + else: + mmcif = MmcifWrapper.read(path, id) + + # If neither chain_id nor entity_id is specified, default to the first entity + if chain_id is None and entity_id is None: + if not mmcif.entities: + raise ValueError("Structure contains no entities") + entity_id = min(mmcif.entities.keys()) # Pick the first entity by ID + + if entity_id is not None: + assert chain_id is None + if entity_id not in mmcif.entities: + raise ValueError( + f"Structure does not contain entity `{entity_id}`. Valid entities: {mmcif.entities.keys()}" + ) + chains = mmcif.entities[entity_id] + + # Select the chain id corresponding to the longest chain. If all are equal length, selects the first. + chain_id = max( + chains, + key=lambda chain: _num_non_null_residues( + mmcif.seqres_to_structure[chain] + ), + ) + else: + assert chain_id is not None + for entity, chains in mmcif.entities.items(): + if chain_id in chains: + entity_id = entity + if entity_id is None: + warnings.warn( + "Failed to detect entity_id from mmcif file, it may be malformed." + ) + + atom_array = mmcif.structure + ( + sequence, + atom_positions, + atom_mask, + residue_index, + insertion_code, + confidence, + _, + ) = chain_to_ndarray(atom_array, mmcif, chain_id, is_predicted) + assert all(sequence), "Some residue name was not specified correctly" + + return cls( + id=mmcif.id, + sequence=sequence, + chain_id=chain_id, + entity_id=entity_id, + atom37_positions=atom_positions, + atom37_mask=atom_mask.astype(bool), + residue_index=residue_index, + insertion_code=insertion_code, + confidence=confidence, + mmcif=mmcif if keep_source else None, + ) + @classmethod def from_atom37( cls, @@ -549,11 +896,11 @@ def from_atom37( return cls( id=id, - sequence=sequence, + sequence=sequence, # type: ignore chain_id=chain_id, entity_id=entity_id, atom37_positions=atom37_positions, - atom37_mask=atom_mask, + atom37_mask=atom_mask.astype(bool), residue_index=residue_index, insertion_code=insertion_code, confidence=confidence, @@ -604,10 +951,12 @@ def from_pdb( id: str | None = None, is_predicted: bool = False, ) -> "ProteinChain": - """Return a ProteinChain object from an pdb file. + """Return a ProteinChain object from an pdb file. NOTE: prefer mmcif for rcsb PDB files. + This function is mostly to interface with old PDB files and predicted structures - + it will not fill out the entity id correctly Args: - path (str | Path | io.TextIO): Path or buffer to read pdb file from. Should be uncompressed. + path (str | Path | io.TextIO): Path or buffer to read mmcif file from. Should be uncompressed. id (str, optional): String identifier to assign to structure. Will attempt to infer otherwise. is_predicted (bool): If True, reads b factor as the confidence readout. Default: False. chain_id (str, optional): Select a chain corresponding to (author) chain id. "detect" uses the @@ -637,20 +986,17 @@ def from_pdb( entity_id = 1 # Not supplied in PDBfiles sequence = "".join( - ( - r - if len(r := PDBData.protein_letters_3to1.get(monomer[0].res_name, "X")) - == 1 - else "X" - ) + residue_constants.restype_3to1.get(monomer[0].res_name, "X") for monomer in bs.residue_iter(atom_array) ) num_res = len(sequence) atom_positions = np.full( - [num_res, RC.atom_type_num, 3], np.nan, dtype=np.float32 + [num_res, residue_constants.atom_type_num, 3], np.nan, dtype=np.float32 + ) + atom_mask = np.full( + [num_res, residue_constants.atom_type_num], False, dtype=bool ) - atom_mask = np.full([num_res, RC.atom_type_num], False, dtype=bool) residue_index = np.full([num_res], -1, dtype=np.int64) insertion_code = np.full([num_res], "", dtype=" "ProteinChain": + return cls( + id=data["id"], + chain_id=data["chain_id"], + entity_id=data["entity_id"], + sequence=data["sequence"], + residue_index=data["residue_index"], + insertion_code=np.asarray(data["insertion_code"]), + atom37_positions=data["atom37_positions"], + atom37_mask=data["atom37_mask"].astype(bool), + confidence=data["confidence"], + mmcif=None, ) @classmethod - def from_rcsb(cls, pdb_id: str, chain_id: str = "detect"): - """Fetch a protein chain from the RCSB PDB database.""" - f: io.StringIO = rcsb.fetch(pdb_id, "pdb") # type: ignore - return cls.from_pdb(f, chain_id=chain_id, id=pdb_id) + def from_rcsb( + cls, + pdb_id: str, + chain_id: str | None = None, + entity_id: int | None = None, + keep_source: bool = False, + ) -> ProteinChain: + f: io.StringIO = rcsb.fetch(pdb_id, "cif") # type: ignore + return cls.from_mmcif( + f, + id=pdb_id, + chain_id=chain_id, + entity_id=entity_id, + keep_source=keep_source, + is_predicted=False, + ) @classmethod def from_atomarray( - cls, atom_array: bs.AtomArray, id: str | None = None + cls, atom_array: bs.AtomArray, id: str | None = None, is_predicted: bool = False ) -> "ProteinChain": """A simple converter from bs.AtomArray -> ProteinChain. Uses PDB file format as intermediate.""" @@ -711,7 +1087,7 @@ def from_atomarray( buf = io.StringIO() pdb_file.write(buf) buf.seek(0) - return cls.from_pdb(buf, id=id) + return cls.from_pdb(buf, id=id, is_predicted=is_predicted) def get_normalization_frame(self) -> Affine3D: """Given a set of coordinates, compute a single frame. @@ -745,6 +1121,8 @@ def normalize_coordinates(self) -> ProteinChain: def infer_oxygen(self) -> ProteinChain: """Oxygen position is fixed given N, CA, C atoms. Infer it if not provided.""" + O_missing_indices = np.argwhere(np.isnan(self.atoms["O"]).any(axis=1)).squeeze() + O_vector = torch.tensor([0.6240, -1.0613, 0.0103], dtype=torch.float32) N, CA, C = torch.from_numpy(self.atoms[["N", "CA", "C"]]).float().unbind(dim=1) N = torch.roll(N, -3) @@ -756,9 +1134,11 @@ def infer_oxygen(self) -> ProteinChain: atom37_positions = self.atom37_positions.copy() atom37_mask = self.atom37_mask.copy() - atom37_positions[:, RC.atom_order["O"]] = O.numpy() - atom37_mask[:, RC.atom_order["O"]] = ~np.isnan( - atom37_positions[:, RC.atom_order["O"]] + atom37_positions[O_missing_indices, residue_constants.atom_order["O"]] = O[ + O_missing_indices + ].numpy() + atom37_mask[O_missing_indices, residue_constants.atom_order["O"]] = ~np.isnan( + atom37_positions[O_missing_indices, residue_constants.atom_order["O"]] ).any(-1) new_chain = replace( self, atom37_positions=atom37_positions, atom37_mask=atom37_mask @@ -781,7 +1161,7 @@ def infer_cbeta(self, infer_cbeta_for_glycine: bool = False) -> ProteinChain: infer_cbeta_for_glycine (bool): If True, infers a beta carbon for glycine residues, even though that residue doesn't have one. Default off. - NOTE: The reason for having this switch in the first place + NOTE(rverkuil): The reason for having this switch in the first place is that sometimes we want a (inferred) CB coordinate for every residue, for example for making a pairwise distance matrix, or doing an RMSD calculation between two designs for a given structural template, w/ @@ -792,11 +1172,13 @@ def infer_cbeta(self, infer_cbeta_for_glycine: bool = False) -> ProteinChain: inferred_cbeta_positions = self.inferred_cbeta if not infer_cbeta_for_glycine: - inferred_cbeta_positions[np.array(list(self.sequence)) == "G", :] = np.NAN + inferred_cbeta_positions[np.array(list(self.sequence)) == "G", :] = np.nan - atom37_positions[:, RC.atom_order["CB"]] = inferred_cbeta_positions - atom37_mask[:, RC.atom_order["CB"]] = ~np.isnan( - atom37_positions[:, RC.atom_order["CB"]] + atom37_positions[:, residue_constants.atom_order["CB"]] = ( + inferred_cbeta_positions + ) + atom37_mask[:, residue_constants.atom_order["CB"]] = ~np.isnan( + atom37_positions[:, residue_constants.atom_order["CB"]] ).any(-1) new_chain = replace( self, atom37_positions=atom37_positions, atom37_mask=atom37_mask @@ -822,36 +1204,69 @@ def as_complex(cls, chains: Sequence[ProteinChain]): ) @classmethod - def concat(cls, chains: Sequence[ProteinChain]): - def join_arrays(arrays: Sequence[np.ndarray], sep: np.ndarray): - full_array = [] - for array in arrays: - full_array.append(array) - full_array.append(sep) - full_array = full_array[:-1] - return np.concatenate(full_array, 0) - + def concat(cls, chains: Sequence[ProteinChain], use_chainbreak: bool = True): sep_tokens = { "residue_index": np.array([-1]), "insertion_code": np.array([""]), - "atom37_positions": np.full([1, 37, 3], np.nan), - "atom37_mask": np.zeros([1, 37]), + "atom37_positions": np.full([1, 37, 3], np.inf), + "atom37_mask": np.zeros([1, 37], dtype=bool), "confidence": np.array([0]), } + def join_arrays(arrays: Sequence[np.ndarray], sep: np.ndarray): + if use_chainbreak: + full_array = [] + for array in arrays: + full_array.append(array) + full_array.append(sep) + full_array = full_array[:-1] + return np.concatenate(full_array, 0) + else: + return np.concatenate(arrays, 0) + array_args: dict[str, np.ndarray] = { name: join_arrays([getattr(chain, name) for chain in chains], sep) for name, sep in sep_tokens.items() } + chain_break = residue_constants.CHAIN_BREAK_TOKEN if use_chainbreak else "" return cls( id=chains[0].id, - sequence=C.CHAIN_BREAK_STR.join(chain.sequence for chain in chains), + sequence=chain_break.join(chain.sequence for chain in chains), chain_id="A", entity_id=None, + mmcif=None, **array_args, ) + def find_nonpolymer_contacts(self): + assert self.mmcif is not None + nonpolymer_and_chain_id_to_array = self.mmcif.non_polymer_coords + + results = [] + for ( + nonpolymer, + _, + ), nonpolymer_array in nonpolymer_and_chain_id_to_array.items(): + assert nonpolymer_array.coord is not None + chain_coords = self.atom37_positions[self.atom37_mask] + distance = cdist(nonpolymer_array.coord, chain_coords) + + is_contact = distance < 5 + if not is_contact.any(): + continue + contacting_atoms = np.where(is_contact.any(0))[0] + chain_index = np.where(self.atom37_mask)[0] + contacting_residues = np.unique(chain_index[contacting_atoms]) + + result = { + "ligand": nonpolymer.name, + "ligand_id": nonpolymer.comp_id, + "contacting_residues": contacting_residues.tolist(), + } + results.append(result) + return results + def select_residue_indices( self, indices: list[int | str], ignore_x_mismatch: bool = False ) -> ProteinChain: @@ -876,3 +1291,25 @@ def select_residue_indices( raise RuntimeError(mismatch_str) return new + + def to_structure_encoder_inputs( + self, + ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]: + """Convert protein chain to structure encoder inputs. + + Returns: + tuple: (coordinates, plddt, residue_index) where: + - coordinates: (1, L, 37, 3) tensor of atom positions + - plddt: (1, L) tensor of confidence scores + - residue_index: (1, L) tensor of residue indices + """ + # Convert to tensors and add batch dimension + coordinates = ( + torch.from_numpy(self.atom37_positions).float().unsqueeze(0) + ) # (1, L, 37, 3) + plddt = torch.from_numpy(self.confidence).float().unsqueeze(0) # (1, L) + residue_index = ( + torch.from_numpy(self.residue_index).long().unsqueeze(0) + ) # (1, L) + + return coordinates, plddt, residue_index diff --git a/esm/utils/structure/protein_complex.py b/esm/utils/structure/protein_complex.py index aa27017c..7e2c38de 100644 --- a/esm/utils/structure/protein_complex.py +++ b/esm/utils/structure/protein_complex.py @@ -2,6 +2,7 @@ import io import itertools +import random import re import warnings from dataclasses import asdict, dataclass, replace @@ -18,24 +19,28 @@ import numpy as np import torch from biotite.database import rcsb +from biotite.file import InvalidFileError from biotite.structure.io.pdb import PDBFile +from biotite.structure.io.pdbx import CIFCategory, CIFColumn, CIFData, CIFFile +from biotite.structure.io.pdbx import set_structure as set_structure_pdbx +from biotite.structure.io.pdbx.convert import _get_transformations, get_structure +from biotite.structure.util import matrix_rotate +from scipy.spatial import KDTree from esm.utils import residue_constants -from esm.utils.constants import esm3 as esm3_c from esm.utils.misc import slice_python_object_as_numpy from esm.utils.structure.affine3d import Affine3D from esm.utils.structure.aligner import Aligner -from esm.utils.structure.metrics import ( - compute_gdt_ts, - compute_lddt_ca, -) +from esm.utils.structure.atom_indexer import AtomIndexer +from esm.utils.structure.metrics import compute_gdt_ts, compute_lddt_ca +from esm.utils.structure.mmcif_parsing import MmcifWrapper, NoProteinError from esm.utils.structure.protein_chain import ( - PathOrBuffer, ProteinChain, -) -from esm.utils.structure.protein_structure import ( + chain_to_ndarray, index_by_atom_name, + infer_CB, ) +from esm.utils.types import PathOrBuffer msgpack_numpy.patch() @@ -44,35 +49,72 @@ ) -def protein_chain_to_protein_complex(chain: ProteinChain) -> ProteinComplex: - if "|" not in chain.sequence: - return ProteinComplex.from_chains([chain]) - chain_breaks = np.array(list(chain.sequence)) == "|" - chain_break_inds = np.where(chain_breaks)[0] - chain_break_inds = np.concatenate([[0], chain_break_inds, [len(chain)]]) - chain_break_inds = np.array(list(zip(chain_break_inds[:-1], chain_break_inds[1:]))) - complex_chains = [] - for start, end in chain_break_inds: - if start != 0: - start += 1 - complex_chains.append(chain[start:end]) - complex_chains = [ - ProteinChain.from_atom37( - chain.atom37_positions, - sequence=chain.sequence, - chain_id=SINGLE_LETTER_CHAIN_IDS[i], - entity_id=i, - ) - for i, chain in enumerate(complex_chains) - ] - return ProteinComplex.from_chains(complex_chains) +def _parse_operation_expression(expression): + """ + Get successive operation steps (IDs) for the given + ``oper_expression``. + Form the cartesian product, if necessary. + Copied from biotite and fixed a bug + """ + # Split groups by parentheses: + # use the opening parenthesis as delimiter + # and just remove the closing parenthesis + expressions_per_step = expression.replace(")", "").split("(") + expressions_per_step = [e for e in expressions_per_step if len(e) > 0] + # Important: Operations are applied from right to left + expressions_per_step.reverse() + + operations = [] + for expr in expressions_per_step: + cur_expr = expr.split(",") + cur_op = [] + # Deal with e='1-10,20-30,40-50' type expressions + for e in cur_expr: + if "-" in e: + first, last = e.split("-") + cur_op.extend(str(id) for id in range(int(first), int(last) + 1)) + else: + cur_op.append(e) + operations.append(cur_op) + + # Cartesian product of operations + return list(itertools.product(*operations)) + + +def _apply_transformations_fast(chains, transformation_dict, operations): + """ + Get subassembly by applying the given operations to the input + structure containing affected asym IDs. + """ + # Additional first dimesion for 'structure.repeat()' + results = [] + + # Apply corresponding transformation for each copy in the assembly + for c in chains: + for operation in operations: + coord = c.atom37_positions.copy() + # Execute for each transformation step + # in the operation expression + for op_step in operation: + T = transformation_dict[op_step] + # Rotate + coord = matrix_rotate(coord, T.rotation) + # Translate + coord += T.target_translation + new_chain = replace(c, atom37_positions=coord) + results.append(new_chain) + + return results @dataclass class ProteinComplexMetadata: entity_lookup: dict[int, int] chain_lookup: dict[int, str] - chain_boundaries: list[tuple[int, int]] + mmcif: MmcifWrapper | None = None + # This is a dictionary that maps assembly ids to the list of unique chains + # in that assembly. Allows for usage of `switch_assembly`. + assembly_composition: dict[str, list[str]] | None = None @dataclass @@ -100,19 +142,7 @@ class DockQResult: aligned_rmsd: float -class AtomIndexer: - def __init__(self, structure: ProteinComplex, property: str, dim: int): - self.structure = structure - self.property = property - self.dim = dim - - def __getitem__(self, atom_names: str | list[str]) -> np.ndarray: - return index_by_atom_name( - getattr(self.structure, self.property), atom_names, self.dim - ) - - -@dataclass +@dataclass(frozen=True) class ProteinComplex: """Dataclass with atom37 representation of an entire protein complex.""" @@ -126,6 +156,7 @@ class ProteinComplex: atom37_positions: np.ndarray atom37_mask: np.ndarray confidence: np.ndarray + # This metadata is parsed from the MMCIF file. For synthetic data, we do a best effort. metadata: ProteinComplexMetadata def __post_init__(self): @@ -195,15 +226,59 @@ def _unsafe_slice(self, idx: int | list[int] | slice | np.ndarray): def __len__(self): return len(self.sequence) + @property + def num_chains(self): + return len(self.chain_boundaries) + @cached_property def atoms(self) -> AtomIndexer: return AtomIndexer(self, property="atom37_positions", dim=-2) + @cached_property + def atom_mask(self) -> AtomIndexer: + return AtomIndexer(self, property="atom37_mask", dim=-1) + + @cached_property + def chain_lengths(self) -> np.ndarray: + return np.diff(self.chain_boundaries, axis=1).flatten() + + @cached_property + def chain_boundaries(self) -> list[tuple[int, int]]: + cb = [-1] + for i, s in enumerate(self.sequence): + if s == "|": + cb.append(i) + cb.append(len(self)) + return [(cb[i] + 1, cb[i + 1]) for i in range(len(cb) - 1)] + + def get_chain_by_index(self, index: int) -> ProteinChain: + try: + start, end = self.chain_boundaries[index] + return self[start:end].as_chain() + except IndexError: + raise IndexError(f"Chain index {index} out of bounds") + + def get_chain_by_id( + self, chain_id: str, sample_chain_if_duplicate: bool = True + ) -> ProteinChain: + valid_indices = [ + index + for index, id_of_index in self.metadata.chain_lookup.items() + if id_of_index == chain_id + ] + if not valid_indices: + raise KeyError(f"Chain ID {chain_id} not found") + if sample_chain_if_duplicate: + index_to_return = random.choice(valid_indices) + return self.get_chain_by_index(index_to_return) + else: + if len(valid_indices) > 1: + raise ValueError(f"Multiple chains with chain ID {chain_id} found") + return self.get_chain_by_index(valid_indices[0]) + def chain_iter(self) -> Iterable[ProteinChain]: - boundaries = [i for i, s in enumerate(self.sequence) if s == "|"] - boundaries = [-1, *boundaries, len(self)] - for i in range(len(boundaries) - 1): - c = self.__getitem__(slice(boundaries[i] + 1, boundaries[i + 1])) + for start, end in self.chain_boundaries: + c = self[start:end] yield c.as_chain() def as_chain(self, force_conversion: bool = False) -> ProteinChain: @@ -237,6 +312,7 @@ def as_chain(self, force_conversion: bool = False) -> ProteinChain: residue_index=self.residue_index, insertion_code=self.insertion_code, confidence=self.confidence, + mmcif=self.metadata.mmcif, ) @classmethod @@ -253,12 +329,6 @@ def from_pdb(cls, path: PathOrBuffer, id: str | None = None) -> "ProteinComplex" chains.append(ProteinChain.from_atomarray(chain, id)) return ProteinComplex.from_chains(chains) - @classmethod - def from_rcsb(cls, pdb_id: str): - """Fetch a protein complex from the RCSB PDB database.""" - f: io.StringIO = rcsb.fetch(pdb_id, "pdb") # type: ignore - return cls.from_pdb(f, id=pdb_id) - def to_pdb(self, path: PathOrBuffer, include_insertions: bool = True): atom_array = None for chain in self.chain_iter(): @@ -284,13 +354,29 @@ def normalize_chain_ids_for_pdb(self): ids = SINGLE_LETTER_CHAIN_IDS chains = [] for i, chain in enumerate(self.chain_iter()): - chain.chain_id = ids[i] + chain = replace(chain, chain_id=ids[i]) if i > len(ids): raise RuntimeError("Too many chains to write to PDB file") chains.append(chain) return ProteinComplex.from_chains(chains) + def find_assembly_ids_with_chain(self, id: str) -> list[str]: + good_chains = [] + if (comp := self.metadata.assembly_composition) is not None: + for assembly_id, chain_ids in comp.items(): + if id in chain_ids: + good_chains.append(assembly_id) + else: + raise ValueError( + "Cannot switch assemblies on this ProteinComplex, you must create the assembly from mmcif to support this" + ) + return good_chains + + def switch_assembly(self, id: str): + assert self.metadata.mmcif is not None + return get_assembly_fast(self.metadata.mmcif, assembly_id=id) + def state_dict(self, backbone_only=False): """This state dict is optimized for storage, so it turns things to fp16 whenever possible. Note that we also only support int32 residue indices, I'm hoping we don't @@ -308,6 +394,11 @@ def state_dict(self, backbone_only=False): elif isinstance(v, ProteinComplexMetadata): dct[k] = asdict(v) dct["atom37_positions"] = dct["atom37_positions"][dct["atom37_mask"]] + dct["metadata"]["mmcif"] = None + # These can be populated with non-serializable objects and are not needed for reconstruction + dct.pop("atoms", None) + dct.pop("atom_mask", None) + dct.pop("per_chain_kd_trees", None) return dct def to_blob(self, backbone_only=False) -> bytes: @@ -322,6 +413,10 @@ def from_state_dict(cls, dct): k: (v.astype(np.float32) if k in ["atom37_positions", "confidence"] else v) for k, v in dct.items() } + if "chain_boundaries" in dct: + del dct["chain_boundaries"] + if "chain_boundaries" in dct["metadata"]: + del dct["metadata"]["chain_boundaries"] dct["metadata"] = ProteinComplexMetadata(**dct["metadata"]) return cls(**dct) @@ -342,13 +437,45 @@ def from_blob(cls, input: Path | str | io.BytesIO | bytes): ) @classmethod - def from_chains(cls, chains: Sequence[ProteinChain]): + def from_rcsb(cls, pdb_id: str, keep_source: bool = False) -> ProteinComplex: + f: io.StringIO = rcsb.fetch(pdb_id, "cif") # type: ignore + return cls.from_mmcif(f, id=pdb_id, keep_source=keep_source, is_predicted=False) + + @classmethod + def from_mmcif( + cls, + path: PathOrBuffer, + id: str | None = None, + assembly_id: str | None = None, + is_predicted: bool = False, + keep_source: bool = False, + ): + """Return a ProteinComplex object from an mmcif file. + TODO(@zeming): there's actually multiple complexes per file, but for ease of implementation, + we only consider the first defined complex! + + Args: + path (str | Path | io.TextIO): Path or buffer to read mmcif file from. Should be uncompressed. + id (str, optional): String identifier to assign to structure. Will attempt to infer otherwise. + is_predicted (bool): If True, reads b factor as the confidence readout. Default: False. + chain_id (str, optional): Select a chain corresponding to (author) chain id. + """ + mmcif = MmcifWrapper.read(path, id) + return get_assembly_fast(mmcif, assembly_id=assembly_id) + + @classmethod + def from_chains( + cls, + chains: Sequence[ProteinChain], + mmcif: MmcifWrapper | None = None, + all_assembly_metadata_dictionary: dict[str, list[str]] | None = None, + ): if not chains: raise ValueError( "Cannot create a ProteinComplex from an empty list of chains" ) - # TODO: Make a proper protein complex class + # TODO(roshan): Make a proper protein complex class def join_arrays(arrays: Sequence[np.ndarray], sep: np.ndarray): full_array = [] for array in arrays: @@ -376,7 +503,6 @@ def join_arrays(arrays: Sequence[np.ndarray], sep: np.ndarray): ent2num_max = -1 ent2num = {} total_index = 0 - chain_boundaries = [] for i, c in enumerate(chains): num_res = c.residue_index.shape[0] if c.chain_id not in chain2num: @@ -401,7 +527,6 @@ def join_arrays(arrays: Sequence[np.ndarray], sep: np.ndarray): } ) - chain_boundaries.append((total_index, total_index + num_res)) total_index += num_res + 1 sep = np.array([-1]) @@ -412,20 +537,25 @@ def join_arrays(arrays: Sequence[np.ndarray], sep: np.ndarray): array_args.update(update) metadata = ProteinComplexMetadata( - chain_boundaries=chain_boundaries, + mmcif=mmcif, chain_lookup={v: k for k, v in chain2num.items()}, entity_lookup={v: k for k, v in ent2num.items()}, + assembly_composition=all_assembly_metadata_dictionary, ) return cls( id=chains[0].id, - sequence=esm3_c.CHAIN_BREAK_STR.join(chain.sequence for chain in chains), + sequence=residue_constants.CHAIN_BREAK_TOKEN.join( + chain.sequence for chain in chains + ), metadata=metadata, **array_args, ) def infer_oxygen(self) -> ProteinComplex: """Oxygen position is fixed given N, CA, C atoms. Infer it if not provided.""" + O_missing_indices = np.argwhere(np.isnan(self.atoms["O"]).any(axis=1)).squeeze() + O_vector = torch.tensor([0.6240, -1.0613, 0.0103], dtype=torch.float32) N, CA, C = torch.from_numpy(self.atoms[["N", "CA", "C"]]).float().unbind(dim=1) N = torch.roll(N, -3) @@ -437,15 +567,56 @@ def infer_oxygen(self) -> ProteinComplex: atom37_positions = self.atom37_positions.copy() atom37_mask = self.atom37_mask.copy() - atom37_positions[:, residue_constants.atom_order["O"]] = O.numpy() - atom37_mask[:, residue_constants.atom_order["O"]] = ~np.isnan( - atom37_positions[:, residue_constants.atom_order["O"]] + atom37_positions[O_missing_indices, residue_constants.atom_order["O"]] = O[ + O_missing_indices + ].numpy() + atom37_mask[O_missing_indices, residue_constants.atom_order["O"]] = ~np.isnan( + atom37_positions[O_missing_indices, residue_constants.atom_order["O"]] + ).any(-1) + new_chain = replace( + self, atom37_positions=atom37_positions, atom37_mask=atom37_mask + ) + return new_chain + + def infer_cbeta(self, infer_cbeta_for_glycine: bool = False) -> ProteinComplex: + """Return a new chain with inferred CB atoms at all residues except GLY. + + Args: + infer_cbeta_for_glycine (bool): If True, infers a beta carbon for glycine + residues, even though that residue doesn't have one. Default off. + + NOTE(rverkuil): The reason for having this switch in the first place + is that sometimes we want a (inferred) CB coordinate for every residue, + for example for making a pairwise distance matrix, or doing an RMSD + calculation between two designs for a given structural template, w/ + CB atoms. + """ + atom37_positions = self.atom37_positions.copy() + atom37_mask = self.atom37_mask.copy() + + N, CA, C = np.moveaxis(self.atoms[["N", "CA", "C"]], 1, 0) + # See usage in trDesign codebase. + # https://github.com/gjoni/trDesign/blob/f2d5930b472e77bfacc2f437b3966e7a708a8d37/02-GD/utils.py#L140 + inferred_cbeta_positions = infer_CB(C, N, CA, 1.522, 1.927, -2.143) + if not infer_cbeta_for_glycine: + inferred_cbeta_positions[np.array(list(self.sequence)) == "G", :] = np.nan + + atom37_positions[:, residue_constants.atom_order["CB"]] = ( + inferred_cbeta_positions + ) + atom37_mask[:, residue_constants.atom_order["CB"]] = ~np.isnan( + atom37_positions[:, residue_constants.atom_order["CB"]] ).any(-1) new_chain = replace( self, atom37_positions=atom37_positions, atom37_mask=atom37_mask ) return new_chain + @classmethod + def from_open_source(cls, pc: ProteinComplex): + # TODO(@zeming): deprecated, should delete + return pc + @classmethod def concat(cls, objs: list[ProteinComplex]) -> ProteinComplex: pdb_ids = [obj.id for obj in objs] @@ -463,6 +634,50 @@ def _sanity_check_complexes_are_comparable(self, other: ProteinComplex): list(other.chain_iter()) ), "Protein complexes must have the same number of chains" + def rmsd( + self, + target: ProteinComplex, + also_check_reflection: bool = False, + mobile_inds: list[int] | np.ndarray | None = None, + target_inds: list[int] | np.ndarray | None = None, + only_compute_backbone_rmsd: bool = False, + compute_chain_assignment: bool = True, + ): + """ + Compute the RMSD between this protein chain and another. + + Args: + target (ProteinComplex): The target (other) protein complex to compare to. + also_check_reflection (bool, optional): If True, also check if the reflection of the mobile atoms has a lower RMSD. + mobile_inds (list[int], optional): The indices of the mobile atoms to align. These are NOT residue indices + target_inds (list[int], optional): The indices of the target atoms to align. These are NOT residue indices + only_compute_backbone_rmsd (bool, optional): If True, only compute the RMSD of the backbone atoms. + """ + if compute_chain_assignment: + aligned = self.dockq(target).aligned + else: + aligned = self + + aligner = Aligner( + aligned if mobile_inds is None else aligned[mobile_inds], + target if target_inds is None else target[target_inds], + only_compute_backbone_rmsd, + ) + avg_rmsd = aligner.rmsd + + if not also_check_reflection: + return avg_rmsd + + aligner = Aligner( + aligned if mobile_inds is None else aligned[mobile_inds], + target if target_inds is None else target[target_inds], + only_compute_backbone_rmsd, + use_reflection=True, + ) + avg_rmsd_neg = aligner.rmsd + + return min(avg_rmsd, avg_rmsd_neg) + def lddt_ca( self, target: ProteinComplex, @@ -537,6 +752,10 @@ def dockq(self, native: ProteinComplex): # This function uses dockqv2 to compute the DockQ score. Because it does a mapping # over all possible chains, it's quite slow. Be careful not to use this in an inference loop # or something that requires fast scoring. It defaults to 8 CPUs. + # + # TODO(@zeming): Because we haven't properly implemented protein complexes for mmcif, + # if your protein has multi-letter or repeated chain IDs, this will fail. Please call + # pc = pc.normalize_chain_ids_for_pdb() before calling this function in that case (limit is 62 chains) try: pass @@ -658,3 +877,316 @@ def parse_dict(d: dict[str, Any]) -> DockQSingleScore: ) return result + + @cached_property + def per_chain_kd_trees(self): + # Iterate over chains, build KDTree for each chain + kdtrees = [] + + CA = self.atoms["CA"] + + for start, end in self.chain_boundaries: + chain_CA = CA[start:end] + chain_CA = chain_CA[np.isfinite(chain_CA).all(axis=-1)] + kdtrees.append(KDTree(chain_CA)) + + return kdtrees + + def chain_adjacency(self, cutoff: float = 8.0) -> np.ndarray: + # Compute adjacency matrix for protein complex + num_chains = self.num_chains + adjacency = np.zeros((num_chains, num_chains), dtype=bool) + for (i, kdtree), (j, kdtree2) in itertools.combinations( + enumerate(self.per_chain_kd_trees), 2 + ): + adj = kdtree.query_ball_tree(kdtree2, cutoff) + any_is_adjacent = any(len(a) > 0 for a in adj) + adjacency[i, j] = any_is_adjacent + adjacency[j, i] = any_is_adjacent + return adjacency + + def chain_adjacency_by_index(self, index: int, cutoff: float = 8.0) -> np.ndarray: + num_chains = len(self.chain_boundaries) + adjacency = np.zeros(num_chains, dtype=bool) + for i, kdtree in enumerate(self.per_chain_kd_trees): + if i == index: + continue + adj = kdtree.query_ball_tree(self.per_chain_kd_trees[index], cutoff) + adjacency[i] = any(len(a) > 0 for a in adj) + return adjacency + + def add_prefix_to_chain_ids(self, prefix: str) -> ProteinComplex: + """Rename all chains in the complex with a given prefix. + + Args: + prefix (str): The prefix to use for the new chain IDs. Each chain will be + named as "{prefix}_{chain_id}". + + Returns: + ProteinComplex: A new protein complex with renamed chains. + """ + new_chains = [] + for chain in self.chain_iter(): + # Create new chain with updated chain_id + new_chain = replace(chain, chain_id=f"{prefix}_{chain.chain_id}") + new_chains.append(new_chain) + return ProteinComplex.from_chains(new_chains) + + def sasa(self, by_residue: bool = True): + chain = self.as_chain(force_conversion=True) + return chain.sasa(by_residue=by_residue) + + def to_mmcif_string(self) -> str: + """Convert the ProteinComplex to mmCIF format. + + Returns: + str: The mmCIF content as a string. + """ + # Convert the ProteinComplex to a biotite AtomArray + # Collect all atoms from all chains + all_atoms = [] + for chain in self.chain_iter(): + chain_atom_array = chain.atom_array + # Convert AtomArray to list of atoms and add to collection + all_atoms.extend(chain_atom_array) + + # Create combined AtomArray from all atoms + if not all_atoms: + raise ValueError("No atoms found in protein complex") + + atom_array = bs.array(all_atoms) + + # Create CIF file + f = CIFFile() + set_structure_pdbx(f, atom_array, data_block=self.id) + + # Add entity information for proper mmCIF structure + self._add_entity_information(f) + + # Write to string + output = io.StringIO() + f.write(output) + return output.getvalue() + + def _add_entity_information(self, cif_file: CIFFile) -> None: + """Add entity, entity_poly, and struct_asym sections to CIF file.""" + + # Group chains by sequence to create unique entities + entity_map = {} # sequence -> entity_id + chain_to_entity = {} # chain_id -> entity_id + entity_sequences = {} # entity_id -> sequence + entity_id_counter = 1 + + for chain in self.chain_iter(): + sequence = chain.sequence + if sequence not in entity_map: + entity_map[sequence] = entity_id_counter + entity_sequences[entity_id_counter] = sequence + entity_id_counter += 1 + chain_to_entity[chain.chain_id] = entity_map[sequence] + + # Create _entity section + entity_ids = [] + entity_types = [] + entity_descriptions = [] + + for entity_id in sorted(entity_sequences.keys()): + entity_ids.append(str(entity_id)) + entity_types.append("polymer") + entity_descriptions.append(f"Protein chain (entity {entity_id})") + + cif_file.block["entity"] = CIFCategory( + name="entity", + columns={ + "id": CIFColumn( + data=CIFData(array=np.array(entity_ids), dtype=np.str_) + ), + "type": CIFColumn( + data=CIFData(array=np.array(entity_types), dtype=np.str_) + ), + "pdbx_description": CIFColumn( + data=CIFData(array=np.array(entity_descriptions), dtype=np.str_) + ), + }, + ) + + # Create _entity_poly section + poly_entity_ids = [] + poly_types = [] + poly_nstd_linkages = [] + poly_sequences = [] + + for entity_id in sorted(entity_sequences.keys()): + poly_entity_ids.append(str(entity_id)) + poly_types.append("polypeptide(L)") + poly_nstd_linkages.append("no") + poly_sequences.append(entity_sequences[entity_id]) + + cif_file.block["entity_poly"] = CIFCategory( + name="entity_poly", + columns={ + "entity_id": CIFColumn( + data=CIFData(array=np.array(poly_entity_ids), dtype=np.str_) + ), + "type": CIFColumn( + data=CIFData(array=np.array(poly_types), dtype=np.str_) + ), + "nstd_linkage": CIFColumn( + data=CIFData(array=np.array(poly_nstd_linkages), dtype=np.str_) + ), + "pdbx_seq_one_letter_code": CIFColumn( + data=CIFData(array=np.array(poly_sequences), dtype=np.str_) + ), + }, + ) + + # Create _struct_asym section + asym_ids = [] + asym_entity_ids = [] + asym_details = [] + + for chain in self.chain_iter(): + asym_ids.append(chain.chain_id) + asym_entity_ids.append(str(chain_to_entity[chain.chain_id])) + asym_details.append("") + + cif_file.block["struct_asym"] = CIFCategory( + name="struct_asym", + columns={ + "id": CIFColumn(data=CIFData(array=np.array(asym_ids), dtype=np.str_)), + "entity_id": CIFColumn( + data=CIFData(array=np.array(asym_entity_ids), dtype=np.str_) + ), + "details": CIFColumn( + data=CIFData(array=np.array(asym_details), dtype=np.str_) + ), + }, + ) + + +def get_assembly_fast( + mmcif: MmcifWrapper, + assembly_id=None, + model=None, + data_block=None, + altloc="first", + use_author_fields=True, +): + pdbx_file = mmcif.raw + if pdbx_file is None: + raise InvalidFileError("No mmCIF data loaded") + assembly_gen_category = pdbx_file.block["pdbx_struct_assembly_gen"] + if assembly_gen_category is None: + raise InvalidFileError("File has no 'pdbx_struct_assembly_gen' category") + + struct_oper_category = pdbx_file.block["pdbx_struct_oper_list"] + if struct_oper_category is None: + raise InvalidFileError("File has no 'pdbx_struct_oper_list' category") + + if assembly_id is None: + assembly_id = assembly_gen_category["assembly_id"].data.array[0] + elif assembly_id not in assembly_gen_category["assembly_id"].data.array: + raise KeyError(f"File has no Assembly ID '{assembly_id}'") + + ### Calculate all possible transformations + transformations = _get_transformations(struct_oper_category) + + ### Get structure according to additional parameters + structure = get_structure( + pdbx_file, model, data_block, altloc, ["label_asym_id"], use_author_fields + )[0] # type: ignore + # TODO(@zeming) This line will remove all non-protein structural elements, + # we should remove this when we want to parse these too. + structure: bs.AtomArray = structure[ + bs.filter_amino_acids(structure) & ~structure.hetero # type: ignore + ] + if len(structure) == 0: + raise NoProteinError + unique_asym_ids = np.unique(structure.label_asym_id) # type: ignore + asym2chain = {} + asym2auth = {} + for asym_id in unique_asym_ids: + sub_structure: bs.AtomArray = structure[structure.label_asym_id == asym_id] # type: ignore + chain_id: str = sub_structure[0].chain_id # type: ignore + ( + sequence, + atom_positions, + atom_mask, + residue_index, + insertion_code, + confidence, + entity_id, + ) = chain_to_ndarray(sub_structure, mmcif, chain_id, False) + + asym2chain[asym_id] = ProteinChain( + id=mmcif.id or "unknown", + sequence=sequence, + chain_id=chain_id, + entity_id=entity_id, + atom37_positions=atom_positions, + atom37_mask=atom_mask, + residue_index=residue_index, + insertion_code=insertion_code, + confidence=confidence, + mmcif=None, + ) + asym2auth[asym_id] = chain_id + + ### Get transformations and apply them to the affected asym IDs + assembly = [] + assembly_id_dict: dict[str, list[str]] = {} + + # Process the target assembly ID + for aid, op_expr, asym_id_expr in zip( + assembly_gen_category["assembly_id"].data.array, + assembly_gen_category["oper_expression"].data.array, + assembly_gen_category["asym_id_list"].data.array, + ): + if aid == assembly_id: + # Parse operations and asym IDs for this specific entry + operations = _parse_operation_expression(op_expr) + asym_ids = asym_id_expr.split(",") + + # Filter affected asym IDs to only protein chains, preserving order + sub_structures = [ + asym2chain[asym_id] for asym_id in asym_ids if asym_id in asym2chain + ] + + # Apply transformations + sub_assembly = _apply_transformations_fast( + sub_structures, transformations, operations + ) + assembly.extend(sub_assembly) + + # Build assembly_id_dict for this entry + assembly_id_dict[aid] = assembly_id_dict.get(aid, []) + [ + asym2auth[id_] for id_ in asym_ids if id_ in asym2auth + ] + + if len(assembly) == 0: + raise NoProteinError + return ProteinComplex.from_chains(assembly, mmcif, assembly_id_dict) + + +def protein_chain_to_protein_complex(chain: ProteinChain) -> ProteinComplex: + if "|" not in chain.sequence: + return ProteinComplex.from_chains([chain]) + chain_breaks = np.array(list(chain.sequence)) == "|" + chain_break_inds = np.where(chain_breaks)[0] + chain_break_inds = np.concatenate([[0], chain_break_inds, [len(chain)]]) + chain_break_inds = np.array(list(zip(chain_break_inds[:-1], chain_break_inds[1:]))) + complex_chains = [] + for start, end in chain_break_inds: + if start != 0: + start += 1 + complex_chains.append(chain[start:end]) + complex_chains = [ + ProteinChain.from_atom37( + chain.atom37_positions, + sequence=chain.sequence, + chain_id=SINGLE_LETTER_CHAIN_IDS[i], + entity_id=i, + ) + for i, chain in enumerate(complex_chains) + ] + return ProteinComplex.from_chains(complex_chains) diff --git a/esm/utils/structure/protein_structure.py b/esm/utils/structure/protein_structure.py index 7f213eaa..cb91d110 100644 --- a/esm/utils/structure/protein_structure.py +++ b/esm/utils/structure/protein_structure.py @@ -194,7 +194,7 @@ def compute_rmsd_no_alignment( mean_squared_error = diff.square().view(diff.size(0), -1, 9).mean(dim=-1) else: mean_squared_error = diff.square().sum(dim=(1, 2)) / ( - num_valid_atoms.squeeze(-1) * 3 + num_valid_atoms.squeeze(-1) ) rmsd = torch.sqrt(mean_squared_error) diff --git a/esm/widgets/components/function_annotator.py b/esm/widgets/components/function_annotator.py index 714238f9..f567f94d 100644 --- a/esm/widgets/components/function_annotator.py +++ b/esm/widgets/components/function_annotator.py @@ -4,9 +4,7 @@ from ipywidgets import widgets from esm.sdk.api import FunctionAnnotation -from esm.tokenization.function_tokenizer import ( - InterProQuantizedTokenizer, -) +from esm.tokenization.function_tokenizer import InterProQuantizedTokenizer TRIE: pygtrie.CharTrie | None = None diff --git a/esm/widgets/components/results_visualizer.py b/esm/widgets/components/results_visualizer.py index 99692e51..261c0a5d 100644 --- a/esm/widgets/components/results_visualizer.py +++ b/esm/widgets/components/results_visualizer.py @@ -7,15 +7,11 @@ import matplotlib.pyplot as plt from esm.sdk.api import ESMProtein -from esm.widgets.utils.drawing.draw_category_array import ( - draw_data_array, -) +from esm.widgets.utils.drawing.draw_category_array import draw_data_array from esm.widgets.utils.drawing.draw_function_annotations import ( draw_function_annotations, ) -from esm.widgets.utils.drawing.draw_protein_structure import ( - draw_protein_structure, -) +from esm.widgets.utils.drawing.draw_protein_structure import draw_protein_structure from esm.widgets.utils.serialization import ( create_download_button_from_buffer, protein_to_pdb_buffer, diff --git a/esm/widgets/components/sasa_prompt_selector.py b/esm/widgets/components/sasa_prompt_selector.py index ecc5c1b4..9c026500 100644 --- a/esm/widgets/components/sasa_prompt_selector.py +++ b/esm/widgets/components/sasa_prompt_selector.py @@ -3,16 +3,9 @@ import ipywidgets as widgets from esm.utils.structure.protein_chain import ProteinChain -from esm.widgets.utils.drawing.colors import ( - hex_to_rgba_tuple, - rgba_tuple_to_hex, -) -from esm.widgets.utils.drawing.draw_category_array import ( - draw_data_array, -) -from esm.widgets.utils.parsing import ( - convert_range_string_to_list_of_ranges, -) +from esm.widgets.utils.drawing.colors import hex_to_rgba_tuple, rgba_tuple_to_hex +from esm.widgets.utils.drawing.draw_category_array import draw_data_array +from esm.widgets.utils.parsing import convert_range_string_to_list_of_ranges from esm.widgets.utils.prompting import PromptManager diff --git a/esm/widgets/components/secondary_structure_prompt_selector.py b/esm/widgets/components/secondary_structure_prompt_selector.py index 020180fe..b8007d69 100644 --- a/esm/widgets/components/secondary_structure_prompt_selector.py +++ b/esm/widgets/components/secondary_structure_prompt_selector.py @@ -4,16 +4,9 @@ import pydssp from esm.utils.structure.protein_chain import ProteinChain -from esm.widgets.utils.drawing.colors import ( - hex_to_rgba_tuple, - rgba_tuple_to_hex, -) -from esm.widgets.utils.drawing.draw_category_array import ( - draw_data_array, -) -from esm.widgets.utils.parsing import ( - convert_range_string_to_list_of_ranges, -) +from esm.widgets.utils.drawing.colors import hex_to_rgba_tuple, rgba_tuple_to_hex +from esm.widgets.utils.drawing.draw_category_array import draw_data_array +from esm.widgets.utils.parsing import convert_range_string_to_list_of_ranges from esm.widgets.utils.prompting import PromptManager diff --git a/esm/widgets/components/sequence_prompt_selector.py b/esm/widgets/components/sequence_prompt_selector.py index d538e670..c5e3526f 100644 --- a/esm/widgets/components/sequence_prompt_selector.py +++ b/esm/widgets/components/sequence_prompt_selector.py @@ -6,9 +6,7 @@ hex_to_rgba_tuple, rgba_tuple_to_rgba_html_string, ) -from esm.widgets.utils.parsing import ( - convert_range_string_to_list_of_ranges, -) +from esm.widgets.utils.parsing import convert_range_string_to_list_of_ranges from esm.widgets.utils.prompting import PromptManager diff --git a/esm/widgets/components/structure_prompt_selector.py b/esm/widgets/components/structure_prompt_selector.py index f4b497c0..13a9df78 100644 --- a/esm/widgets/components/structure_prompt_selector.py +++ b/esm/widgets/components/structure_prompt_selector.py @@ -10,12 +10,8 @@ from esm.utils.structure.protein_chain import ProteinChain from esm.widgets.utils import indexing -from esm.widgets.utils.drawing.draw_protein_structure import ( - draw_protein_structure, -) -from esm.widgets.utils.parsing import ( - convert_range_string_to_list_of_ranges, -) +from esm.widgets.utils.drawing.draw_protein_structure import draw_protein_structure +from esm.widgets.utils.parsing import convert_range_string_to_list_of_ranges from esm.widgets.utils.printing import wrapped_print from esm.widgets.utils.prompting import PromptManager diff --git a/esm/widgets/utils/drawing/draw_function_annotations.py b/esm/widgets/utils/drawing/draw_function_annotations.py index c71e5434..59e9f7cf 100644 --- a/esm/widgets/utils/drawing/draw_function_annotations.py +++ b/esm/widgets/utils/drawing/draw_function_annotations.py @@ -9,10 +9,7 @@ from PIL import Image from esm.sdk.api import FunctionAnnotation -from esm.utils.function.interpro import ( - InterPro, - InterProEntryType, -) +from esm.utils.function.interpro import InterPro, InterProEntryType @contextmanager diff --git a/esm/widgets/utils/prompting.py b/esm/widgets/utils/prompting.py index 1ce6d9c6..1e89bb64 100644 --- a/esm/widgets/utils/prompting.py +++ b/esm/widgets/utils/prompting.py @@ -9,9 +9,7 @@ from esm.utils import encoding from esm.widgets.utils import indexing from esm.widgets.utils.drawing.colors import rgba_tuple_to_hex -from esm.widgets.utils.drawing.draw_category_array import ( - draw_data_array, -) +from esm.widgets.utils.drawing.draw_category_array import draw_data_array from esm.widgets.utils.printing import wrapped_print diff --git a/esm/widgets/views/esm3_generation_launcher.py b/esm/widgets/views/esm3_generation_launcher.py index e94c60ff..f8bf8f3b 100644 --- a/esm/widgets/views/esm3_generation_launcher.py +++ b/esm/widgets/views/esm3_generation_launcher.py @@ -13,13 +13,9 @@ GenerationConfig, ) from esm.utils.constants import models -from esm.widgets.components.results_visualizer import ( - create_results_visualizer, -) +from esm.widgets.components.results_visualizer import create_results_visualizer from esm.widgets.utils.printing import wrapped_print -from esm.widgets.utils.serialization import ( - create_download_results_button, -) +from esm.widgets.utils.serialization import create_download_results_button def create_esm3_generation_launcher( diff --git a/esm/widgets/views/esm3_prompt_selector.py b/esm/widgets/views/esm3_prompt_selector.py index 035db28e..f7e60686 100644 --- a/esm/widgets/views/esm3_prompt_selector.py +++ b/esm/widgets/views/esm3_prompt_selector.py @@ -1,8 +1,6 @@ from ipywidgets import widgets -from esm.widgets.components.sasa_prompt_selector import ( - create_sasa_prompt_selector, -) +from esm.widgets.components.sasa_prompt_selector import create_sasa_prompt_selector from esm.widgets.components.secondary_structure_prompt_selector import ( create_secondary_structure_prompt_selector, ) diff --git a/esm/widgets/views/generation.py b/esm/widgets/views/generation.py index 19015f60..fdec2094 100644 --- a/esm/widgets/views/generation.py +++ b/esm/widgets/views/generation.py @@ -4,20 +4,12 @@ from esm.sdk.api import ESM3InferenceClient, ESMProtein from esm.utils.constants import esm3 as C -from esm.widgets.components.function_annotator import ( - create_function_annotator, -) +from esm.widgets.components.function_annotator import create_function_annotator from esm.widgets.utils.prompting import PromptManagerCollection from esm.widgets.utils.protein_import import ProteinImporter -from esm.widgets.views.esm3_generation_launcher import ( - create_esm3_generation_launcher, -) -from esm.widgets.views.esm3_prompt_preview import ( - create_esm3_prompt_preview, -) -from esm.widgets.views.esm3_prompt_selector import ( - create_esm3_prompt_selector, -) +from esm.widgets.views.esm3_generation_launcher import create_esm3_generation_launcher +from esm.widgets.views.esm3_prompt_preview import create_esm3_prompt_preview +from esm.widgets.views.esm3_prompt_selector import create_esm3_prompt_selector def create_generation_ui( diff --git a/esm/widgets/views/inverse_folding.py b/esm/widgets/views/inverse_folding.py index 8becb8eb..50d9a128 100644 --- a/esm/widgets/views/inverse_folding.py +++ b/esm/widgets/views/inverse_folding.py @@ -6,9 +6,7 @@ ESMProteinError, GenerationConfig, ) -from esm.widgets.components.results_visualizer import ( - create_results_visualizer, -) +from esm.widgets.components.results_visualizer import create_results_visualizer from esm.widgets.utils.printing import wrapped_print from esm.widgets.utils.protein_import import ProteinImporter diff --git a/esm/widgets/views/login.py b/esm/widgets/views/login.py index 2d8be5a3..5c7b6706 100644 --- a/esm/widgets/views/login.py +++ b/esm/widgets/views/login.py @@ -4,10 +4,7 @@ from ipywidgets import widgets -from esm.widgets.utils.clients import ( - get_forge_client, - get_local_client, -) +from esm.widgets.utils.clients import get_forge_client, get_local_client from esm.widgets.utils.types import ClientInitContainer diff --git a/esm/widgets/views/prediction.py b/esm/widgets/views/prediction.py index de6666d2..94ff49dc 100644 --- a/esm/widgets/views/prediction.py +++ b/esm/widgets/views/prediction.py @@ -6,9 +6,7 @@ ESMProteinError, GenerationConfig, ) -from esm.widgets.components.results_visualizer import ( - create_results_visualizer, -) +from esm.widgets.components.results_visualizer import create_results_visualizer from esm.widgets.utils.printing import wrapped_print from esm.widgets.utils.protein_import import ProteinImporter diff --git a/pixi.lock b/pixi.lock index d3fa4fa3..f5f12d46 100644 --- a/pixi.lock +++ b/pixi.lock @@ -12,6 +12,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/backports-1.0-pyhd8ed1ab_5.conda - conda: https://conda.anaconda.org/conda-forge/noarch/backports.tarfile-1.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.1.0-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py312h2ec8cdc_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.4-hb9d3cd8_0.conda @@ -21,11 +23,16 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cmake-4.0.0-h74e3db0_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cmarkgfm-2024.11.20-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py312hd9148b4_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.12.9-py312hd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cryptography-44.0.2-py312hda17c39_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.13.6-h5008d03_3.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.21.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/expat-2.7.0-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.18.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.59.0-py312h8a5da7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.13.3-ha770c72_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda @@ -39,45 +46,78 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/jeepney-0.9.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/keyring-25.6.0-pyha804496_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.8-py312h68727a3_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.17-h717163a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_4.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/libblas-3.9.0-31_h59b9bed_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-31_he106b2a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.13.0-h332b0f4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.23-h86f0d12_0.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/libev-4.33-hd590300_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.0-h5888daf_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.6-h2dba641_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.13.3-ha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.13.3-h48d6fc4_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.2.0-h767d61c_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.2.0-h69a702a_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.84.0-h2ff4ddf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.2.0-h69a702a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.2.0-hf1ad2bd_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.84.1-h2ff4ddf_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.2.0-h767d61c_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h4ce23a2_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.0-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-31_h7ac8fdf_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.6.4-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.64.0-h161d5f1_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.29-pthreads_h94d23a6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.50-h421ea60_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.49.1-hee588c1_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hf672d98_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.2.0-h8f9b012_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.2.0-h4852527_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-hd9ff511_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.50.0-hb9d3cd8_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/libzlib-1.3.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.5-py312he3d6523_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/more-itertools-10.6.0-pyhd8ed1ab_0.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/linux-64/nh3-0.2.21-py39h77e2912_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.2-py312h33ff503_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.3-h55fea9a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.4.1-h7b32b05_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.3.1-py312hf79963d_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.44-hba22ea6_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-11.3.0-py312h80c1187_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pip-25.0.1-pyh8b19718_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pkg-config-0.29.2-h4bc722e_1009.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/pycparser-2.22-pyh29332c3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.3-pyhe01879c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.9-h9e4cc4f_1_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-gil-3.12.9-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.12-6_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py312h178313f_2.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/readline-8.2-h8c095d6_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/readme_renderer-44.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda @@ -85,41 +125,57 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3986-2.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/rhash-1.4.5-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.0.0-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.16.0-py312hf734454_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/secretstorage-3.3.3-py312h7900ff3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-75.8.2-pyhff2d567_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/tk-8.6.13-noxft_h4845f30_101.conda - conda: https://conda.anaconda.org/conda-forge/noarch/twine-6.1.0-pyh29332c3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.13.0-pyh29332c3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-16.0.0-py312h66e93f0_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py312h66e93f0_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda + - pypi: https://files.pythonhosted.org/packages/6f/12/e5e0282d673bb9746bacfb6e2dba8719989d3660cdb2ea79aee9a9651afb/anyio-4.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b4/0d/b7a0f10f5100dcf51ae36ba31490169bfa45617323bd82af43e1fb0098fb/biopython-1.85-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/3e/04/3f09ba937cdd5381743ffabd48c18d790a684a11fb6b23dfd8d30f0ec858/biotite-1.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/80/cd/c607b6337ddcf55cc0249527819b727ef28481d7c56ace54cca69400c2b9/biotraj-1.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/b6/6d/79fad38fcd7e1fc6961061b46cc87706c5c946088bc4620abf0d0aa49420/boto3-1.40.9-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/02/e9/367e81e114deb92a6e0d5740f0bff4548af710be318af65265b9aad72237/botocore-1.40.9-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e8/0f/b1a9b09a84ef98b9fc38d50c6b2815cb2256b804a78e7d838ddfbdc035c7/cloudpathlib-0.21.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/52/f2/9e726e4489f947557290605e3ee33b6ba09fe5dac9213263e0ada75200cf/dna_features_viewer-3.1.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/87/62/9773de14fe6c45c23649e98b83231fffd7b9892b6cf863251dc2afa73643/einops-0.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7b/8f/c4d9bafc34ad7ad5d8dc16dd1347ee0e507a52c3adb6bfa8887e1c6a26ba/executing-2.2.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl + - pypi: direct+https://github.com/Dao-AILab/flash-attention/releases/download/v2.8.1/flash_attn-2.8.1+cu12torch2.7cxx11abiFALSE-cp312-cp312-linux_x86_64.whl - pypi: https://files.pythonhosted.org/packages/44/4b/e0cfc1a6f17e990f3e64b7d941ddc4acdc7b19d6edd51abf495f32b1a9e4/fsspec-2025.3.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/99/e3/2232d0e726d4d6ea69643b9593d97d0e7e6ea69c2fe9ed5de34d476c1c47/huggingface_hub-0.30.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/20/3a/917cb9e72f4e1a4ea13c862533205ae1319bd664119189ee5cc9e4e95ebf/ipython-9.0.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/58/6a/9166369a2f092bd286d24e6307de555d63616e8ddb373ebad2b5635ca4cd/ipywidgets-8.1.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/31/b4/b9b800c45527aadd64d5b442f9b932b00648617eb5d63d2c7a6587b7cafc/jmespath-1.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/91/29/df4b9b42f2be0b623cbd5e2140cafcaa2bef0759a00b7b70104dcfe2fb51/joblib-1.4.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/43/6a/ca128561b22b60bd5a0c4ea26649e68c8556b82bc70a0c396eebc977fe86/jupyterlab_widgets-3.0.15-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f1/54/65af8de681fa8255402c80eda2a501ba467921d5a7a028c9c22a2c2eedb5/msgpack-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/9b/5d/f25ac7d4fb77cbd53ddc6d05d833c6bf52b12770a44fa9a447eed470ca9a/msgpack_numpy-0.4.8-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/02/e2/e2cbb8d634151aab9528ef7b8bab52ee4ab10e076509285602c2a3a686e0/numpy-2.2.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/ae/71/1c91302526c45ab494c23f61c7a84aa568b8c1f9d196efa5993957faf906/nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/67/42/f4f60238e8194a3106d06a058d494b18e006c10bb2b915655bd9f6ea4cb1/nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/2c/14/91ae57cd4db3f9ef7aa99f4019cfa8d54cb4caa7e00975df6467e9725a9f/nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl @@ -133,21 +189,19 @@ environments: - pypi: https://files.pythonhosted.org/packages/df/99/12cd266d6233f47d00daf3a72739872bdc10267d0383508b0b9c84a18bb6/nvidia_nccl_cu12-2.21.5-py3-none-manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/ff/ff/847841bacfbefc97a00036e0fce5a0f086b640756dc38caea5e1bb002655/nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/87/20/199b8713428322a2f22b722c62b8cc278cc53dffa9705d744484b5035ee9/nvidia_nvtx_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/38/f8/d8fddee9ed0d0c0f4a2132c1dfcf0e3e53265055da8df952a53e7eaf178c/pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/c6/ac/dac4a63f978e4dcb3c6d3a78c4d8e0192a113d288502a1216950c41b1027/parso-0.8.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/38/0d/84200ed6a871ce386ddc82904bfadc0c6b28b0c0ec78176871a4679e40b3/pillow-11.1.0-cp312-cp312-manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/e4/ea/d836f008d33151c7a1f62caf3d8dd782e4d15f6a43897f64480c2b8de2ad/prompt_toolkit-3.0.50-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/a3/17/c476487ba903c7d793db633b4e8ca4a420ae272890302189d9402ba8ff85/py3dmol-2.5.2-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/00/78/9cbcc1c073b9d4918e925af1a059762265dc65004e020511b2a06fbfd020/pydssp-0.9.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ec/cd/bd196b2cf014afb1009de8b0f05ecd54011d881944e62763f3c1b1e8ef37/pygtrie-2.5.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fb/13/e3b075031a738c9598c51cfbc4c7879e26729c53aa9cca59211c44235314/regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/6d/4f/d073e09df851cfa251ef7840007d04db3293a0482ce607d2b993926089be/s3transfer-0.13.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a6/f8/dae3421624fcc87a89d42e1898a798bc7ff72c61f38973a65d60df8f124c/safetensors-0.5.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/29/7a/8bce8968883e9465de20be15542f4c7e221952441727c4dad24d534c6d99/scikit_learn-1.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/c0/53/eaada1a414c026673eb983f8b4a55fe5eb172725d33d62c1b21f63ff6ca4/scipy-1.15.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b2/fe/81695a1aa331a842b582453b605175f419fe8540355886031328089d840a/sympy-1.13.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl @@ -160,10 +214,712 @@ environments: - pypi: https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7b/9f/92d3091c44cb19add044064af1bf1345cd35fbb84d32a3690f912800a295/transformers-4.48.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/06/00/59500052cb1cf8cf5316be93598946bc451f14072c6ff256904428eaf03c/triton-3.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ca/51/5447876806d1088a0f8f71e16542bf350918128d0a69437df26047c8e46f/widgetsnbextension-4.0.14-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/92/a7/5f4c6c2a62dd88f58133e333af1c251dd08f98d48445f650889d85e43b7b/zstd-1.5.6.7.tar.gz - pypi: ./ + osx-arm64: + - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backports-1.0-pyhd8ed1ab_5.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backports.tarfile-1.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.1.0-h5505292_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.1.0-h5505292_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.1.0-py312hd8f9ff3_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h99b78c6_7.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.5-h5505292_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.8.3-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.8.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-1.17.1-py312h0fad829_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cmake-4.1.0-hae74ae4_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cmarkgfm-2024.11.20-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py312ha0dd364_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.12.11-py312hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.22-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.18.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fonttools-4.59.0-py312h6daa0e5_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.13.3-hce30654_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/id-1.5.0-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.5.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.classes-3.4.0-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.context-6.0.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.functools-4.2.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/keyring-25.6.0-pyh534df25_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.4.9-py312hdc12c9d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.17-h7eeda09_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/libblas-3.9.0-34_h10e41b3_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.1.0-h5505292_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.1.0-h5505292_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.1.0-h5505292_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.9.0-34_hb3479ef_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.14.1-h73640d1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-20.1.8-hf598326_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.24-h5773f1b_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.1-hec049ff_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.4.6-h1da3d7d_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.13.3-hce30654_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.13.3-h1d14073_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.1.0-hfdf1602_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.1.0-hb74de2c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.84.3-h587fa63_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-h23cfdf5_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libintl-0.25.1-h493aca8_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.1.0-h5505292_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.9.0-34_hc9a63f6_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.64.0-h6d7220d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.30-openmp_h60d53f8_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.50-h280e0eb_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.50.4-h4237e3c_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.0-h025e3ab_6.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-20.1.8-hbb9b287_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.10.5-py312h05635fa_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/more-itertools-10.7.0-pyhd8ed1ab_0.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/osx-arm64/nh3-0.3.0-py39h24c5d98_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.3.2-py312h2f38b44_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.3-h889cd5d_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.5.2-he92f556_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-2.3.1-py312h98f7732_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.45-ha881caa_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-11.3.0-py312h50aef2c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pip-25.2-pyh8b19718_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pkg-config-0.29.2-hde07d2e_1009.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/pycparser-2.22-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.3-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.11-hc22306f_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-gil-3.12.11-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.2-py312h998013c_2.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/readline-8.2-h1d1bf99_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/readme_renderer-44.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-toolbelt-1.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3986-2.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rhash-1.4.6-h5505292_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.1.0-pyhe01879c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.16.1-py312h286a95b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_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/tk-8.6.13-h892fb3f_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/twine-6.1.0-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.14.1-pyhe01879c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-16.0.0-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-h5505292_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hd74edd7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h925e9cb_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.23.0-py312hea69d52_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-h6491c7d_2.conda + - pypi: https://files.pythonhosted.org/packages/6f/12/e5e0282d673bb9746bacfb6e2dba8719989d3660cdb2ea79aee9a9651afb/anyio-4.10.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/54/5b/8b3b029c94c63ab4c1781d141615b4a837e658422381d460c5573d5d8262/biopython-1.85-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/21/e0/64716fb2ce9d7749bb41cc932c667b0f6d478e09367b36d273b1f77b06f3/biotite-1.4.0-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/c4/d2/369ed44ad23f8c0464f7b80d41143d717896097b39fe3dd3e07e6d162fef/biotraj-1.2.2-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/b6/6d/79fad38fcd7e1fc6961061b46cc87706c5c946088bc4620abf0d0aa49420/boto3-1.40.9-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/02/e9/367e81e114deb92a6e0d5740f0bff4548af710be318af65265b9aad72237/botocore-1.40.9-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/40/e7/6fea57b887f8e367c1e4a496ba03bfaf57824b766f777723ce1faf28834b/cloudpathlib-0.21.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/52/f2/9e726e4489f947557290605e3ee33b6ba09fe5dac9213263e0ada75200cf/dna_features_viewer-3.1.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/87/62/9773de14fe6c45c23649e98b83231fffd7b9892b6cf863251dc2afa73643/einops-0.8.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7b/8f/c4d9bafc34ad7ad5d8dc16dd1347ee0e507a52c3adb6bfa8887e1c6a26ba/executing-2.2.0-py2.py3-none-any.whl + - pypi: direct+https://github.com/Dao-AILab/flash-attention/releases/download/v2.8.1/flash_attn-2.8.1+cu12torch2.7cxx11abiFALSE-cp312-cp312-linux_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/2f/e0/014d5d9d7a4564cf1c40b5039bc882db69fd881111e03ab3657ac0b218e2/fsspec-2025.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/51/a3/fa5897099454aa287022a34a30e68dbff0e617760f774f8bd1db17f06bd4/hf_xet-1.1.7-cp37-abi3-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/39/7b/bb06b061991107cd8783f300adff3e7b7f284e330fd82f507f2a1417b11d/huggingface_hub-0.34.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/63/f8/0031ee2b906a15a33d6bfc12dd09c3dfa966b3cb5b284ecfb7549e6ac3c4/ipython-9.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/58/6a/9166369a2f092bd286d24e6307de555d63616e8ddb373ebad2b5635ca4cd/ipywidgets-8.1.7-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/31/b4/b9b800c45527aadd64d5b442f9b932b00648617eb5d63d2c7a6587b7cafc/jmespath-1.0.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7d/4f/1195bbac8e0c2acc5f740661631d8d750dc38d4a32b23ee5df3cde6f4e0d/joblib-1.5.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/43/6a/ca128561b22b60bd5a0c4ea26649e68c8556b82bc70a0c396eebc977fe86/jupyterlab_widgets-3.0.15-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ab/65/7d1de38c8a22cf8b1551469159d4b6cf49be2126adc2482de50976084d78/msgpack-1.1.1-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/9b/5d/f25ac7d4fb77cbd53ddc6d05d833c6bf52b12770a44fa9a447eed470ca9a/msgpack_numpy-0.4.8-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/eb/8d/776adee7bbf76365fdd7f2552710282c79a4ead5d2a46408c9043a2b70ba/networkx-3.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c6/ac/dac4a63f978e4dcb3c6d3a78c4d8e0192a113d288502a1216950c41b1027/parso-0.8.4-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ce/4f/5249960887b1fbe561d9ff265496d170b55a735b76724f10ef19f9e40716/prompt_toolkit-3.0.51-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a3/17/c476487ba903c7d793db633b4e8ca4a420ae272890302189d9402ba8ff85/py3dmol-2.5.2-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/00/78/9cbcc1c073b9d4918e925af1a059762265dc65004e020511b2a06fbfd020/pydssp-0.9.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ec/cd/bd196b2cf014afb1009de8b0f05ecd54011d881944e62763f3c1b1e8ef37/pygtrie-2.5.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/cd/70/69506d53397b4bd6954061bae75677ad34deb7f6ca3ba199660d6f728ff5/regex-2025.7.34-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/6d/4f/d073e09df851cfa251ef7840007d04db3293a0482ce607d2b993926089be/s3transfer-0.13.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/8c/c9/bb114c158540ee17907ec470d01980957fdaf87b4aa07914c24eba87b9c6/safetensors-0.6.2-cp38-abi3-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/67/4e/899317092f5efcab0e9bc929e3391341cec8fb0e816c4789686770024580/scikit_learn-1.7.1-cp312-cp312-macosx_12_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/8d/a6/28975479e35ddc751dc1ddc97b9b69bf7fcf074db31548aab37f8116674c/tokenizers-0.21.4-cp39-abi3-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/be/66/5c9a321b325aaecb92d4d1855421e3a055abd77903b7dab6575ca07796db/torch-2.8.0-cp312-none-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/8b/80/c78f88844e576c21580e64bd93de9df9bca223fef3e8195345680eb6f87f/torchtext-0.18.0-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/df/1d/0ea0b34bde92a86d42620f29baa6dcbb5c2fc85990316df5cb8f7abb8ea2/torchvision-0.23.0-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7b/9f/92d3091c44cb19add044064af1bf1345cd35fbb84d32a3690f912800a295/transformers-4.48.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ca/51/5447876806d1088a0f8f71e16542bf350918128d0a69437df26047c8e46f/widgetsnbextension-4.0.14-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/08/b8/2bc2590a34c733ea0570f366e6ad7d889d05c7825bd3ccab01f36ece71c6/zstd-1.5.7.2-cp312-cp312-macosx_11_0_arm64.whl + - pypi: ./ + dev: + channels: + - url: https://conda.anaconda.org/conda-forge/ + indexes: + - https://pypi.org/simple + packages: + linux-64: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.14-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backports-1.0-pyhd8ed1ab_5.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backports.tarfile-1.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.1.0-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py312h2ec8cdc_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.4-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2025.1.31-hbcca054_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3394656_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.1.31-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.1-py312h06ac9bb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cfgv-3.3.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cmake-4.0.0-h74e3db0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cmarkgfm-2024.11.20-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py312hd9148b4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.10.2-py312h8a5da7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.12.9-py312hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cryptography-44.0.2-py312hda17c39_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.27-h54b06d7_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.13.6-h5008d03_3.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.21.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.3.1-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/expat-2.7.0-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.18.0-pyhd8ed1ab_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.15.0-h7e30c49_1.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-0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.59.0-py312h8a5da7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.13.3-ha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.14-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-11.2.1-h3beb420_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/id-1.5.0-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.12-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.6.1-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.5.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.classes-3.4.0-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.context-6.0.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.functools-4.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jeepney-0.9.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/keyring-25.6.0-pyha804496_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.8-py312h68727a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.17-h717163a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_4.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/libblas-3.9.0-31_h59b9bed_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-31_he106b2a_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp20.1-20.1.4-default_h1df26ce_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-20.1.4-default_he06ed0a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-hb8b1518_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.13.0-h332b0f4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.23-h86f0d12_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.125-hb9d3cd8_0.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.0-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.6-h2dba641_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.13.3-ha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.13.3-h48d6fc4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.2.0-h767d61c_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.2.0-h69a702a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.2.0-h69a702a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.2.0-hf1ad2bd_2.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.84.1-h2ff4ddf_0.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/libgomp-14.2.0-h767d61c_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h4ce23a2_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.0-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-31_h7ac8fdf_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm20-20.1.4-he9d0ab4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.6.4-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.64.0-h161d5f1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.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/libopenblas-0.3.29-pthreads_h94d23a6_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.50-h421ea60_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-17.4-h27ae623_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.49.1-hee588c1_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hf672d98_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.2.0-h8f9b012_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.2.0-h4852527_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-hd9ff511_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.50.0-hb9d3cd8_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.9.2-h65c71a3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.7-h8d12d68_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.39-h76b75d6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.5-py312h7900ff3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.5-py312he3d6523_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/more-itertools-10.6.0-pyhd8ed1ab_0.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/mysql-common-9.0.1-h266115a_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-9.0.1-he0572af_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/nh3-0.2.21-py39h77e2912_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.9.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/nodejs-22.13.0-hf235a45_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.2-py312h33ff503_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.3-h55fea9a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.9-he970967_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.4.1-h7b32b05_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.3.1-py312hf79963d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/patsy-1.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.44-hba22ea6_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-11.3.0-py312h80c1187_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pip-25.0.1-pyh8b19718_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h537e5f6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pkg-config-0.29.2-h4bc722e_1009.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.8-pyhe01879c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pre-commit-4.2.0-pyha770c72_0.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/pycparser-2.22-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.3-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyright-1.1.399-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.9.0-py312h91f0f75_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.2.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.9-h9e4cc4f_1_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-gil-3.12.9-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.12-6_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py312h178313f_2.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.9.0-h6441bc3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/readme_renderer-44.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-toolbelt-1.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3986-2.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rhash-1.4.5-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.0.0-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.16.0-py312hf734454_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/seaborn-0.13.2-hd8ed1ab_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/seaborn-base-0.13.2-pyhd8ed1ab_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/secretstorage-3.3.3-py312h7900ff3_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-75.8.2-pyhff2d567_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/statsmodels-0.14.5-py312h8b63200_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.2.1-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.1-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/twine-6.1.0-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.13.0-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.0.1-py312h68727a3_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-16.0.0-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/virtualenv-20.33.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.24.0-h3e06ad9_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda + - 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.5-hb9d3cd8_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.45-hb9d3cd8_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.12-h4f16b4b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.6-hb9d3cd8_2.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-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.6-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.1-hb9d3cd8_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.4-hb9d3cd8_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.6-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py312h66e93f0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda + - pypi: https://files.pythonhosted.org/packages/6f/12/e5e0282d673bb9746bacfb6e2dba8719989d3660cdb2ea79aee9a9651afb/anyio-4.10.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b4/0d/b7a0f10f5100dcf51ae36ba31490169bfa45617323bd82af43e1fb0098fb/biopython-1.85-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/3e/04/3f09ba937cdd5381743ffabd48c18d790a684a11fb6b23dfd8d30f0ec858/biotite-1.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/80/cd/c607b6337ddcf55cc0249527819b727ef28481d7c56ace54cca69400c2b9/biotraj-1.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/b6/6d/79fad38fcd7e1fc6961061b46cc87706c5c946088bc4620abf0d0aa49420/boto3-1.40.9-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/02/e9/367e81e114deb92a6e0d5740f0bff4548af710be318af65265b9aad72237/botocore-1.40.9-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e8/0f/b1a9b09a84ef98b9fc38d50c6b2815cb2256b804a78e7d838ddfbdc035c7/cloudpathlib-0.21.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/52/f2/9e726e4489f947557290605e3ee33b6ba09fe5dac9213263e0ada75200cf/dna_features_viewer-3.1.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/87/62/9773de14fe6c45c23649e98b83231fffd7b9892b6cf863251dc2afa73643/einops-0.8.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7b/8f/c4d9bafc34ad7ad5d8dc16dd1347ee0e507a52c3adb6bfa8887e1c6a26ba/executing-2.2.0-py2.py3-none-any.whl + - pypi: direct+https://github.com/Dao-AILab/flash-attention/releases/download/v2.8.1/flash_attn-2.8.1+cu12torch2.7cxx11abiFALSE-cp312-cp312-linux_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/44/4b/e0cfc1a6f17e990f3e64b7d941ddc4acdc7b19d6edd51abf495f32b1a9e4/fsspec-2025.3.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/99/e3/2232d0e726d4d6ea69643b9593d97d0e7e6ea69c2fe9ed5de34d476c1c47/huggingface_hub-0.30.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/20/3a/917cb9e72f4e1a4ea13c862533205ae1319bd664119189ee5cc9e4e95ebf/ipython-9.0.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/58/6a/9166369a2f092bd286d24e6307de555d63616e8ddb373ebad2b5635ca4cd/ipywidgets-8.1.7-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/31/b4/b9b800c45527aadd64d5b442f9b932b00648617eb5d63d2c7a6587b7cafc/jmespath-1.0.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/91/29/df4b9b42f2be0b623cbd5e2140cafcaa2bef0759a00b7b70104dcfe2fb51/joblib-1.4.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/43/6a/ca128561b22b60bd5a0c4ea26649e68c8556b82bc70a0c396eebc977fe86/jupyterlab_widgets-3.0.15-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f1/54/65af8de681fa8255402c80eda2a501ba467921d5a7a028c9c22a2c2eedb5/msgpack-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/9b/5d/f25ac7d4fb77cbd53ddc6d05d833c6bf52b12770a44fa9a447eed470ca9a/msgpack_numpy-0.4.8-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ae/71/1c91302526c45ab494c23f61c7a84aa568b8c1f9d196efa5993957faf906/nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/67/42/f4f60238e8194a3106d06a058d494b18e006c10bb2b915655bd9f6ea4cb1/nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/2c/14/91ae57cd4db3f9ef7aa99f4019cfa8d54cb4caa7e00975df6467e9725a9f/nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/ea/27/1795d86fe88ef397885f2e580ac37628ed058a92ed2c39dc8eac3adf0619/nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/9f/fd/713452cd72343f682b1c7b9321e23829f00b842ceaedcda96e742ea0b0b3/nvidia_cudnn_cu12-9.1.0.70-py3-none-manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/27/94/3266821f65b92b3138631e9c8e7fe1fb513804ac934485a8d05776e1dd43/nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/8a/6d/44ad094874c6f1b9c654f8ed939590bdc408349f137f9b98a3a23ccec411/nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/3a/e1/5b9089a4b2a4790dfdea8b3a006052cfecff58139d5a4e34cb1a51df8d6f/nvidia_cusolver_cu12-11.6.1.9-py3-none-manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/db/f7/97a9ea26ed4bbbfc2d470994b8b4f338ef663be97b8f677519ac195e113d/nvidia_cusparse_cu12-12.3.1.170-py3-none-manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/78/a8/bcbb63b53a4b1234feeafb65544ee55495e1bb37ec31b999b963cbccfd1d/nvidia_cusparselt_cu12-0.6.2-py3-none-manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/df/99/12cd266d6233f47d00daf3a72739872bdc10267d0383508b0b9c84a18bb6/nvidia_nccl_cu12-2.21.5-py3-none-manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/ff/ff/847841bacfbefc97a00036e0fce5a0f086b640756dc38caea5e1bb002655/nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/87/20/199b8713428322a2f22b722c62b8cc278cc53dffa9705d744484b5035ee9/nvidia_nvtx_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/c6/ac/dac4a63f978e4dcb3c6d3a78c4d8e0192a113d288502a1216950c41b1027/parso-0.8.4-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e4/ea/d836f008d33151c7a1f62caf3d8dd782e4d15f6a43897f64480c2b8de2ad/prompt_toolkit-3.0.50-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a3/17/c476487ba903c7d793db633b4e8ca4a420ae272890302189d9402ba8ff85/py3dmol-2.5.2-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/00/78/9cbcc1c073b9d4918e925af1a059762265dc65004e020511b2a06fbfd020/pydssp-0.9.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ec/cd/bd196b2cf014afb1009de8b0f05ecd54011d881944e62763f3c1b1e8ef37/pygtrie-2.5.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fb/13/e3b075031a738c9598c51cfbc4c7879e26729c53aa9cca59211c44235314/regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/6d/4f/d073e09df851cfa251ef7840007d04db3293a0482ce607d2b993926089be/s3transfer-0.13.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a6/f8/dae3421624fcc87a89d42e1898a798bc7ff72c61f38973a65d60df8f124c/safetensors-0.5.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/29/7a/8bce8968883e9465de20be15542f4c7e221952441727c4dad24d534c6d99/scikit_learn-1.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b2/fe/81695a1aa331a842b582453b605175f419fe8540355886031328089d840a/sympy-1.13.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/8a/63/38be071b0c8e06840bc6046991636bcb30c27f6bb1e670f4f4bc87cf49cc/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/e5/35/0c52d708144c2deb595cd22819a609f78fdd699b95ff6f0ebcd456e3c7c1/torch-2.6.0-cp312-cp312-manylinux1_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/65/13/26c37c5433658d3f1eb30be07a4b42b29893bc42ff7cb7261ef6e474fc3c/torchtext-0.18.0-cp312-cp312-manylinux1_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/36/63/0722e153fd27d64d5b0af45b5c8cb0e80b35a68cf0130303bc9a8bb095c7/torchvision-0.21.0-cp312-cp312-manylinux1_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7b/9f/92d3091c44cb19add044064af1bf1345cd35fbb84d32a3690f912800a295/transformers-4.48.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/06/00/59500052cb1cf8cf5316be93598946bc451f14072c6ff256904428eaf03c/triton-3.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ca/51/5447876806d1088a0f8f71e16542bf350918128d0a69437df26047c8e46f/widgetsnbextension-4.0.14-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/92/a7/5f4c6c2a62dd88f58133e333af1c251dd08f98d48445f650889d85e43b7b/zstd-1.5.6.7.tar.gz + - pypi: ./ + osx-arm64: + - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backports-1.0-pyhd8ed1ab_5.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backports.tarfile-1.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.1.0-h5505292_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.1.0-h5505292_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.1.0-py312hd8f9ff3_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h99b78c6_7.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.5-h5505292_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.8.3-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.8.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-1.17.1-py312h0fad829_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cfgv-3.3.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cmake-4.1.0-hae74ae4_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cmarkgfm-2024.11.20-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py312ha0dd364_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coverage-7.10.3-py312h6daa0e5_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.12.11-py312hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.22-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.18.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fonttools-4.59.0-py312h6daa0e5_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.13.3-hce30654_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/id-1.5.0-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.13-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.5.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.classes-3.4.0-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.context-6.0.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.functools-4.2.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/keyring-25.6.0-pyh534df25_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.4.9-py312hdc12c9d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.17-h7eeda09_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/libblas-3.9.0-34_h10e41b3_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.1.0-h5505292_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.1.0-h5505292_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.1.0-h5505292_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.9.0-34_hb3479ef_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.14.1-h73640d1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-20.1.8-hf598326_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.24-h5773f1b_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.1-hec049ff_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.4.6-h1da3d7d_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.13.3-hce30654_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.13.3-h1d14073_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.1.0-hfdf1602_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.1.0-hb74de2c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.84.3-h587fa63_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-h23cfdf5_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libintl-0.25.1-h493aca8_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.1.0-h5505292_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.9.0-34_hc9a63f6_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.64.0-h6d7220d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.30-openmp_h60d53f8_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.50-h280e0eb_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.50.4-h4237e3c_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.0-h025e3ab_6.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-20.1.8-hbb9b287_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.10.5-py312h1f38498_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.10.5-py312h05635fa_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/more-itertools-10.7.0-pyhd8ed1ab_0.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/osx-arm64/nh3-0.3.0-py39h24c5d98_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.9.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nodejs-24.4.1-hab9d20b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.3.2-py312h2f38b44_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.3-h889cd5d_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.5.2-he92f556_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-2.3.1-py312h98f7732_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/patsy-1.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.45-ha881caa_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-11.3.0-py312h50aef2c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pip-25.2-pyh8b19718_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pkg-config-0.29.2-hde07d2e_1009.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.8-pyhe01879c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pre-commit-4.3.0-pyha770c72_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/pycparser-2.22-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.3-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyright-1.1.399-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.2.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.11-hc22306f_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-gil-3.12.11-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.2-py312h998013c_2.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/readline-8.2-h1d1bf99_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/readme_renderer-44.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-toolbelt-1.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3986-2.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rhash-1.4.6-h5505292_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.1.0-pyhe01879c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.16.1-py312h286a95b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/seaborn-0.13.2-hd8ed1ab_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/seaborn-base-0.13.2-pyhd8ed1ab_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_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/statsmodels-0.14.5-py312hcde60ef_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h892fb3f_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.2.1-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.2-py312h163523d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/twine-6.1.0-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.14.1-pyhe01879c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ukkonen-1.0.1-py312h6142ec9_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-16.0.0-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/virtualenv-20.34.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-h5505292_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hd74edd7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h925e9cb_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.23.0-py312hea69d52_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-h6491c7d_2.conda + - pypi: https://files.pythonhosted.org/packages/6f/12/e5e0282d673bb9746bacfb6e2dba8719989d3660cdb2ea79aee9a9651afb/anyio-4.10.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/54/5b/8b3b029c94c63ab4c1781d141615b4a837e658422381d460c5573d5d8262/biopython-1.85-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/21/e0/64716fb2ce9d7749bb41cc932c667b0f6d478e09367b36d273b1f77b06f3/biotite-1.4.0-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/c4/d2/369ed44ad23f8c0464f7b80d41143d717896097b39fe3dd3e07e6d162fef/biotraj-1.2.2-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/b6/6d/79fad38fcd7e1fc6961061b46cc87706c5c946088bc4620abf0d0aa49420/boto3-1.40.9-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/02/e9/367e81e114deb92a6e0d5740f0bff4548af710be318af65265b9aad72237/botocore-1.40.9-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/40/e7/6fea57b887f8e367c1e4a496ba03bfaf57824b766f777723ce1faf28834b/cloudpathlib-0.21.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/52/f2/9e726e4489f947557290605e3ee33b6ba09fe5dac9213263e0ada75200cf/dna_features_viewer-3.1.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/87/62/9773de14fe6c45c23649e98b83231fffd7b9892b6cf863251dc2afa73643/einops-0.8.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7b/8f/c4d9bafc34ad7ad5d8dc16dd1347ee0e507a52c3adb6bfa8887e1c6a26ba/executing-2.2.0-py2.py3-none-any.whl + - pypi: direct+https://github.com/Dao-AILab/flash-attention/releases/download/v2.8.1/flash_attn-2.8.1+cu12torch2.7cxx11abiFALSE-cp312-cp312-linux_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/2f/e0/014d5d9d7a4564cf1c40b5039bc882db69fd881111e03ab3657ac0b218e2/fsspec-2025.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/51/a3/fa5897099454aa287022a34a30e68dbff0e617760f774f8bd1db17f06bd4/hf_xet-1.1.7-cp37-abi3-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/39/7b/bb06b061991107cd8783f300adff3e7b7f284e330fd82f507f2a1417b11d/huggingface_hub-0.34.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/63/f8/0031ee2b906a15a33d6bfc12dd09c3dfa966b3cb5b284ecfb7549e6ac3c4/ipython-9.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/58/6a/9166369a2f092bd286d24e6307de555d63616e8ddb373ebad2b5635ca4cd/ipywidgets-8.1.7-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/31/b4/b9b800c45527aadd64d5b442f9b932b00648617eb5d63d2c7a6587b7cafc/jmespath-1.0.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7d/4f/1195bbac8e0c2acc5f740661631d8d750dc38d4a32b23ee5df3cde6f4e0d/joblib-1.5.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/43/6a/ca128561b22b60bd5a0c4ea26649e68c8556b82bc70a0c396eebc977fe86/jupyterlab_widgets-3.0.15-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ab/65/7d1de38c8a22cf8b1551469159d4b6cf49be2126adc2482de50976084d78/msgpack-1.1.1-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/9b/5d/f25ac7d4fb77cbd53ddc6d05d833c6bf52b12770a44fa9a447eed470ca9a/msgpack_numpy-0.4.8-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/eb/8d/776adee7bbf76365fdd7f2552710282c79a4ead5d2a46408c9043a2b70ba/networkx-3.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c6/ac/dac4a63f978e4dcb3c6d3a78c4d8e0192a113d288502a1216950c41b1027/parso-0.8.4-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ce/4f/5249960887b1fbe561d9ff265496d170b55a735b76724f10ef19f9e40716/prompt_toolkit-3.0.51-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a3/17/c476487ba903c7d793db633b4e8ca4a420ae272890302189d9402ba8ff85/py3dmol-2.5.2-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/00/78/9cbcc1c073b9d4918e925af1a059762265dc65004e020511b2a06fbfd020/pydssp-0.9.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ec/cd/bd196b2cf014afb1009de8b0f05ecd54011d881944e62763f3c1b1e8ef37/pygtrie-2.5.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/cd/70/69506d53397b4bd6954061bae75677ad34deb7f6ca3ba199660d6f728ff5/regex-2025.7.34-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/6d/4f/d073e09df851cfa251ef7840007d04db3293a0482ce607d2b993926089be/s3transfer-0.13.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/8c/c9/bb114c158540ee17907ec470d01980957fdaf87b4aa07914c24eba87b9c6/safetensors-0.6.2-cp38-abi3-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/67/4e/899317092f5efcab0e9bc929e3391341cec8fb0e816c4789686770024580/scikit_learn-1.7.1-cp312-cp312-macosx_12_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/8d/a6/28975479e35ddc751dc1ddc97b9b69bf7fcf074db31548aab37f8116674c/tokenizers-0.21.4-cp39-abi3-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/be/66/5c9a321b325aaecb92d4d1855421e3a055abd77903b7dab6575ca07796db/torch-2.8.0-cp312-none-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/8b/80/c78f88844e576c21580e64bd93de9df9bca223fef3e8195345680eb6f87f/torchtext-0.18.0-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/df/1d/0ea0b34bde92a86d42620f29baa6dcbb5c2fc85990316df5cb8f7abb8ea2/torchvision-0.23.0-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7b/9f/92d3091c44cb19add044064af1bf1345cd35fbb84d32a3690f912800a295/transformers-4.48.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ca/51/5447876806d1088a0f8f71e16542bf350918128d0a69437df26047c8e46f/widgetsnbextension-4.0.14-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/08/b8/2bc2590a34c733ea0570f366e6ad7d889d05c7825bd3ccab01f36ece71c6/zstd-1.5.7.2-cp312-cp312-macosx_11_0_arm64.whl + - pypi: ./ packages: - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 sha256: fe51de6107f9edc7aa4f786a70f4a883943bc9d39b3bb7307c04c41410990726 @@ -197,6 +953,39 @@ packages: purls: [] size: 8283 timestamp: 1736938720099 +- conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda + sha256: a3967b937b9abf0f2a99f3173fa4630293979bd1644709d89580e7c62a544661 + md5: aaa2a381ccc56eac91d63b6c1240312f + depends: + - cpython + - python-gil + license: MIT + license_family: MIT + purls: [] + size: 8191 + timestamp: 1744137672556 +- conda: https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.14-hb9d3cd8_0.conda + sha256: b9214bc17e89bf2b691fad50d952b7f029f6148f4ac4fe7c60c08f093efdf745 + md5: 76df83c2a9035c54df5d04ff81bcc02d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: LGPL-2.1-or-later + license_family: GPL + purls: [] + size: 566531 + timestamp: 1744668655747 +- pypi: https://files.pythonhosted.org/packages/6f/12/e5e0282d673bb9746bacfb6e2dba8719989d3660cdb2ea79aee9a9651afb/anyio-4.10.0-py3-none-any.whl + name: anyio + version: 4.10.0 + sha256: 60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1 + requires_dist: + - exceptiongroup>=1.0.2 ; python_full_version < '3.11' + - idna>=2.8 + - sniffio>=1.1 + - typing-extensions>=4.5 ; python_full_version < '3.13' + - trio>=0.26.1 ; extra == 'trio' + requires_python: '>=3.9' - pypi: https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl name: asttokens version: 3.0.0 @@ -276,6 +1065,13 @@ packages: - pkg:pypi/backports-tarfile?source=hash-mapping size: 32786 timestamp: 1733325872620 +- pypi: https://files.pythonhosted.org/packages/54/5b/8b3b029c94c63ab4c1781d141615b4a837e658422381d460c5573d5d8262/biopython-1.85-cp312-cp312-macosx_11_0_arm64.whl + name: biopython + version: '1.85' + sha256: 6fe47d704c2d3afac99aeb461219ec5f00273120d2d99835dc0a9a617f520141 + requires_dist: + - numpy + requires_python: '>=3.9' - pypi: https://files.pythonhosted.org/packages/b4/0d/b7a0f10f5100dcf51ae36ba31490169bfa45617323bd82af43e1fb0098fb/biopython-1.85-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl name: biopython version: '1.85' @@ -299,6 +1095,22 @@ packages: - pytest ; extra == 'test' - pytest-codspeed ; extra == 'test' requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/21/e0/64716fb2ce9d7749bb41cc932c667b0f6d478e09367b36d273b1f77b06f3/biotite-1.4.0-cp312-cp312-macosx_11_0_arm64.whl + name: biotite + version: 1.4.0 + sha256: 19b4cfbdbe1ad94319dfea61d3dcdb07d476ba5bf55fb507021d6a5a5d4762d8 + requires_dist: + - biotraj>=1.0,<2.0 + - msgpack>=0.5.6 + - networkx>=2.0 + - numpy>=1.25 + - packaging>=24.0 + - requests>=2.12 + - numpydoc==1.8.0 ; extra == 'lint' + - ruff==0.9.7 ; extra == 'lint' + - pytest ; extra == 'test' + - pytest-codspeed ; extra == 'test' + requires_python: '>=3.11' - pypi: https://files.pythonhosted.org/packages/80/cd/c607b6337ddcf55cc0249527819b727ef28481d7c56ace54cca69400c2b9/biotraj-1.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl name: biotraj version: 1.2.2 @@ -311,6 +1123,91 @@ packages: - psutil ; extra == 'test' - netcdf4>=1.7.1 ; extra == 'test' requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/c4/d2/369ed44ad23f8c0464f7b80d41143d717896097b39fe3dd3e07e6d162fef/biotraj-1.2.2-cp312-cp312-macosx_11_0_arm64.whl + name: biotraj + version: 1.2.2 + sha256: 7bab65f5e3975a844c1018ea5873acd7dd21a64e8ebf0c145635c1d2fb9ef9bd + requires_dist: + - numpy>=1.25 + - scipy>=1.13 + - ruff==0.6.1 ; extra == 'lint' + - pytest ; extra == 'test' + - psutil ; extra == 'test' + - netcdf4>=1.7.1 ; extra == 'test' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/b6/6d/79fad38fcd7e1fc6961061b46cc87706c5c946088bc4620abf0d0aa49420/boto3-1.40.9-py3-none-any.whl + name: boto3 + version: 1.40.9 + sha256: 516f5e3f7552b2a7ca4d2c89b338fb4684998c676b11b906e2ab694c91716ba6 + requires_dist: + - botocore>=1.40.9,<1.41.0 + - jmespath>=0.7.1,<2.0.0 + - s3transfer>=0.13.0,<0.14.0 + - botocore[crt]>=1.21.0,<2.0a0 ; extra == 'crt' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/02/e9/367e81e114deb92a6e0d5740f0bff4548af710be318af65265b9aad72237/botocore-1.40.9-py3-none-any.whl + name: botocore + version: 1.40.9 + sha256: d4960a39aab9658bcd0272490003001cb4a8d12b89bb297ccef994ee023fb638 + requires_dist: + - jmespath>=0.7.1,<2.0.0 + - python-dateutil>=2.1,<3.0.0 + - urllib3>=1.25.4,<1.27 ; python_full_version < '3.10' + - urllib3>=1.25.4,!=2.2.0,<3 ; python_full_version >= '3.10' + - awscrt==0.23.8 ; extra == 'crt' + requires_python: '>=3.9' +- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hb9d3cd8_2.conda + sha256: fcb0b5b28ba7492093e54f3184435144e074dfceab27ac8e6a9457e736565b0b + md5: 98514fe74548d768907ce7a13f680e8f + depends: + - __glibc >=2.17,<3.0.a0 + - brotli-bin 1.1.0 hb9d3cd8_2 + - libbrotlidec 1.1.0 hb9d3cd8_2 + - libbrotlienc 1.1.0 hb9d3cd8_2 + - libgcc >=13 + license: MIT + license_family: MIT + purls: [] + size: 19264 + timestamp: 1725267697072 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.1.0-h5505292_3.conda + sha256: 97e2a90342869cc122921fdff0e6be2f5c38268555c08ba5d14e1615e4637e35 + md5: 03c7865dd4dbf87b7b7d363e24c632f1 + depends: + - __osx >=11.0 + - brotli-bin 1.1.0 h5505292_3 + - libbrotlidec 1.1.0 h5505292_3 + - libbrotlienc 1.1.0 h5505292_3 + license: MIT + license_family: MIT + purls: [] + size: 20094 + timestamp: 1749230390021 +- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.1.0-hb9d3cd8_2.conda + sha256: 261364d7445513b9a4debc345650fad13c627029bfc800655a266bf1e375bc65 + md5: c63b5e52939e795ba8d26e35d767a843 + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlidec 1.1.0 hb9d3cd8_2 + - libbrotlienc 1.1.0 hb9d3cd8_2 + - libgcc >=13 + license: MIT + license_family: MIT + purls: [] + size: 18881 + timestamp: 1725267688731 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.1.0-h5505292_3.conda + sha256: 5c6a808326c3bbb6f015a57c9eb463d65f259f67154f4f06783d8829ce9239b4 + md5: cc435eb5160035fd8503e9a58036c5b5 + depends: + - __osx >=11.0 + - libbrotlidec 1.1.0 h5505292_3 + - libbrotlienc 1.1.0 h5505292_3 + license: MIT + license_family: MIT + purls: [] + size: 17185 + timestamp: 1749230373519 - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py312h2ec8cdc_2.conda sha256: f2a59ccd20b4816dea9a2a5cb917eb69728271dbf1aeab4e1b7e609330a50b6f md5: b0b867af6fc74b2a0aa206da29c0f3cf @@ -328,6 +1225,23 @@ packages: - pkg:pypi/brotli?source=hash-mapping size: 349867 timestamp: 1725267732089 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.1.0-py312hd8f9ff3_3.conda + sha256: 35df7079768b4c51764149c42b14ccc25c4415e4365ecc06c38f74562d9e4d16 + md5: c7c728df70dc05a443f1e337c28de22d + depends: + - __osx >=11.0 + - libcxx >=18 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - libbrotlicommon 1.1.0 h5505292_3 + license: MIT + license_family: MIT + purls: + - pkg:pypi/brotli?source=hash-mapping + size: 339365 + timestamp: 1749230606596 - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda sha256: 5ced96500d945fb286c9c838e54fa759aa04a7129c59800f0846b4335cee770d md5: 62ee74e96c5ebb0af99386de58cf9553 @@ -339,6 +1253,16 @@ packages: purls: [] size: 252783 timestamp: 1720974456583 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h99b78c6_7.conda + sha256: adfa71f158cbd872a36394c56c3568e6034aa55c623634b37a4836bd036e6b91 + md5: fc6948412dbbbe9a4c9ddbbcfe0a79ab + depends: + - __osx >=11.0 + license: bzip2-1.0.6 + license_family: BSD + purls: [] + size: 122909 + timestamp: 1720974522888 - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.4-hb9d3cd8_0.conda sha256: d4f28d87b6339b94f74762c0076e29c8ef8ddfff51a564a92da2843573c18320 md5: e2775acf57efd5af15b8e3d1d74d72d3 @@ -350,13 +1274,58 @@ packages: purls: [] size: 206085 timestamp: 1734208189009 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.5-h5505292_0.conda + sha256: b4bb55d0806e41ffef94d0e3f3c97531f322b3cb0ca1f7cdf8e47f62538b7a2b + md5: f8cd1beb98240c7edb1a95883360ccfa + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 179696 + timestamp: 1744128058734 - conda: https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2025.1.31-hbcca054_0.conda sha256: bf832198976d559ab44d6cdb315642655547e26d826e34da67cbee6624cda189 md5: 19f3a56f68d2fd06c516076bff482c52 license: ISC purls: [] - size: 158144 - timestamp: 1738298224464 + size: 158144 + timestamp: 1738298224464 +- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.8.3-hbd8a1cb_0.conda + sha256: 837b795a2bb39b75694ba910c13c15fa4998d4bb2a622c214a6a5174b2ae53d1 + md5: 74784ee3d225fc3dca89edb635b4e5cc + depends: + - __unix + license: ISC + purls: [] + size: 154402 + timestamp: 1754210968730 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3394656_0.conda + sha256: 3bd6a391ad60e471de76c0e9db34986c4b5058587fbf2efa5a7f54645e28c2c7 + md5: 09262e66b19567aff4f592fb53b28760 + depends: + - __glibc >=2.17,<3.0.a0 + - fontconfig >=2.15.0,<3.0a0 + - fonts-conda-ecosystem + - freetype >=2.12.1,<3.0a0 + - icu >=75.1,<76.0a0 + - libexpat >=2.6.4,<3.0a0 + - libgcc >=13 + - libglib >=2.82.2,<3.0a0 + - libpng >=1.6.47,<1.7.0a0 + - libstdcxx >=13 + - libxcb >=1.17.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - pixman >=0.44.2,<1.0a0 + - xorg-libice >=1.1.2,<2.0a0 + - xorg-libsm >=1.2.5,<2.0a0 + - xorg-libx11 >=1.8.11,<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: 978114 + timestamp: 1741554591855 - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.1.31-pyhd8ed1ab_0.conda sha256: 42a78446da06a2568cb13e69be3355169fbd0ea424b00fc80b7d840f5baaacf3 md5: c207fa5ac7ea99b149344385a9c0880d @@ -367,6 +1336,16 @@ packages: - pkg:pypi/certifi?source=compressed-mapping size: 162721 timestamp: 1739515973129 +- conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.8.3-pyhd8ed1ab_0.conda + sha256: a1ad5b0a2a242f439608f22a538d2175cac4444b7b3f4e2b8c090ac337aaea40 + md5: 11f59985f49df4620890f3e746ed7102 + depends: + - python >=3.9 + license: ISC + purls: + - pkg:pypi/certifi?source=compressed-mapping + size: 158692 + timestamp: 1754231530168 - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.1-py312h06ac9bb_0.conda sha256: cba6ea83c4b0b4f5b5dc59cb19830519b28f95d7ebef7c9c5cf1c14843621457 md5: a861504bbea4161a9170b85d4d2be840 @@ -383,6 +1362,33 @@ packages: - pkg:pypi/cffi?source=hash-mapping size: 294403 timestamp: 1725560714366 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-1.17.1-py312h0fad829_0.conda + sha256: 8d91a0d01358b5c3f20297c6c536c5d24ccd3e0c2ddd37f9d0593d0f0070226f + md5: 19a5456f72f505881ba493979777b24e + depends: + - __osx >=11.0 + - libffi >=3.4,<4.0a0 + - pycparser + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + purls: + - pkg:pypi/cffi?source=hash-mapping + size: 281206 + timestamp: 1725560813378 +- conda: https://conda.anaconda.org/conda-forge/noarch/cfgv-3.3.1-pyhd8ed1ab_1.conda + sha256: d5696636733b3c301054b948cdd793f118efacce361d9bd4afb57d5980a9064f + md5: 57df494053e17dce2ac3a0b33e1b2a2e + depends: + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/cfgv?source=hash-mapping + size: 12973 + timestamp: 1734267180483 - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.1-pyhd8ed1ab_0.conda sha256: 4e0ee91b97e5de3e74567bdacea27f0139709fceca4db8adffbe24deffccb09b md5: e83a31202d1c0a000fce3e9cf3825875 @@ -394,6 +1400,17 @@ packages: - pkg:pypi/charset-normalizer?source=hash-mapping size: 47438 timestamp: 1735929811779 +- conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.3-pyhd8ed1ab_0.conda + sha256: 838d5a011f0e7422be6427becba3de743c78f3874ad2743c341accbba9bb2624 + md5: 7e7d5ef1b9ed630e4a1c358d6bc62284 + depends: + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/charset-normalizer?source=hash-mapping + size: 51033 + timestamp: 1754767444665 - pypi: https://files.pythonhosted.org/packages/e8/0f/b1a9b09a84ef98b9fc38d50c6b2815cb2256b804a78e7d838ddfbdc035c7/cloudpathlib-0.21.0-py3-none-any.whl name: cloudpathlib version: 0.21.0 @@ -408,6 +1425,20 @@ packages: - google-cloud-storage ; extra == 'gs' - boto3>=1.34.0 ; extra == 's3' requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/40/e7/6fea57b887f8e367c1e4a496ba03bfaf57824b766f777723ce1faf28834b/cloudpathlib-0.21.1-py3-none-any.whl + name: cloudpathlib + version: 0.21.1 + sha256: bfe580ad72ec030472ec233cd7380701b2d3227da7b2898387bd170aa70c803c + requires_dist: + - typing-extensions>4 ; python_full_version < '3.11' + - cloudpathlib[azure] ; extra == 'all' + - cloudpathlib[gs] ; extra == 'all' + - cloudpathlib[s3] ; extra == 'all' + - azure-storage-blob>=12 ; extra == 'azure' + - azure-storage-file-datalake>=12 ; extra == 'azure' + - google-cloud-storage ; extra == 'gs' + - boto3>=1.34.0 ; extra == 's3' + requires_python: '>=3.9' - conda: https://conda.anaconda.org/conda-forge/linux-64/cmake-4.0.0-h74e3db0_0.conda sha256: 1178601b5969001e1797fdc8564a1bcb2d448a45ad20dacb233faaaeda6f794c md5: b8f039327d73a96c66c0e9c56d8c1cd8 @@ -429,6 +1460,26 @@ packages: purls: [] size: 20440638 timestamp: 1743111798861 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cmake-4.1.0-hae74ae4_0.conda + sha256: 0e00e9c1944e594f293e10b4e4abd4505f098d8d63c95c455b80775abcf134fa + md5: 65d333c04dcdbea01b16993358df3364 + depends: + - __osx >=11.0 + - bzip2 >=1.0.8,<2.0a0 + - libcurl >=8.14.1,<9.0a0 + - libcxx >=19 + - libexpat >=2.7.1,<3.0a0 + - liblzma >=5.8.1,<6.0a0 + - libuv >=1.51.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - rhash >=1.4.6,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 16940415 + timestamp: 1754436958182 - conda: https://conda.anaconda.org/conda-forge/linux-64/cmarkgfm-2024.11.20-py312h66e93f0_0.conda sha256: 294643f1ad5cbaa8646f803b89cc2da2b43c41cf4d3855883662ab0bb5455d3e md5: bf99b4a864e31ecd9244affd27f3ceb6 @@ -444,6 +1495,112 @@ packages: - pkg:pypi/cmarkgfm?source=hash-mapping size: 139452 timestamp: 1732193337513 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cmarkgfm-2024.11.20-py312hea69d52_0.conda + sha256: 8606d2948e8bdfe0f412e838f2329b47b44e281bc4229527a7de01b3e3a9432b + md5: 41c468aad74976b25c5726c660e662d8 + depends: + - __osx >=11.0 + - cffi >=1.0.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + purls: + - pkg:pypi/cmarkgfm?source=hash-mapping + size: 113005 + timestamp: 1732193458717 +- conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + sha256: ab29d57dc70786c1269633ba3dff20288b81664d3ff8d21af995742e2bb03287 + md5: 962b9857ee8e7018c22f2776ffa0b2d7 + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/colorama?source=hash-mapping + size: 27011 + timestamp: 1733218222191 +- pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl + name: comm + version: 0.2.3 + sha256: c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417 + requires_dist: + - pytest ; extra == 'test' + requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py312hd9148b4_1.conda + sha256: d9cb7f97a184a383bf0c72e1fa83b983a1caa68d7564f4449a4de7c97df9cb3f + md5: e25ed6c2e3b1effedfe9cd10a15ca8d8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - numpy >=1.25 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/contourpy?source=compressed-mapping + size: 291827 + timestamp: 1754063770363 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py312ha0dd364_1.conda + sha256: a51a6f7f7e236cadc45790880dc0b7c91cf6a950277ffe839b689f072783a8d0 + md5: e0b0bffaccf76ef33679dd2e5309442e + depends: + - __osx >=11.0 + - libcxx >=19 + - numpy >=1.25 + - 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/contourpy?source=hash-mapping + size: 257410 + timestamp: 1754063952152 +- conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.10.2-py312h8a5da7c_0.conda + sha256: 211754cf4be7ce64b11955509fcab60cd33090633c362d4aface4a5320adbf77 + md5: f8830a2249f4b8a5f9c33cb44ad5c31a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - tomli + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/coverage?source=hash-mapping + size: 377963 + timestamp: 1754308562631 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/coverage-7.10.3-py312h6daa0e5_0.conda + sha256: 5395c4d622d56fbab9489136b7daf254d325fe2101bdb95f91c78dc88e0da2ad + md5: 724b721d0f9a50826207779456d2c172 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - tomli + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/coverage?source=hash-mapping + size: 380056 + timestamp: 1754910268875 +- conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.12.11-py312hd8ed1ab_0.conda + noarch: generic + sha256: 7e7bc8e73a2f3736444a8564cbece7216464c00f0bc38e604b0c792ff60d621a + md5: e5279009e7a7f7edd3cd2880c502b3cc + depends: + - python >=3.12,<3.13.0a0 + - python_abi * *_cp312 + license: Python-2.0 + purls: [] + size: 45852 + timestamp: 1749047748072 - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.12.9-py312hd8ed1ab_1.conda noarch: generic sha256: 58a637bc8328b115c9619de3fcd664ec26662083319e3c106917a1b3ee4d7594 @@ -473,6 +1630,31 @@ packages: - pkg:pypi/cryptography?source=hash-mapping size: 1590060 timestamp: 1740893871300 +- conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_1.conda + sha256: 9827efa891e507a91a8a2acf64e210d2aff394e1cde432ad08e1f8c66b12293c + md5: 44600c4667a319d67dbe0681fc0bc833 + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/cycler?source=hash-mapping + size: 13399 + timestamp: 1733332563512 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.27-h54b06d7_7.conda + sha256: d2ea5e52da745c4249e1a818095a28f9c57bd4df22cbfc645352defa468e86c2 + md5: dce22f70b4e5a407ce88f2be046f4ceb + depends: + - krb5 >=1.21.1,<1.22.0a0 + - libgcc-ng >=12 + - libntlm + - libstdcxx-ng >=12 + - openssl >=3.1.1,<4.0a0 + license: BSD-3-Clause-Attribution + license_family: BSD + purls: [] + size: 219527 + timestamp: 1690061203707 - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.13.6-h5008d03_3.tar.bz2 sha256: 8f5f995699a2d9dbdd62c61385bfeeb57c82a681a7c8c5313c395aa0ccab68a5 md5: ecfff944ba3960ecb334b9a2663d708d @@ -490,6 +1672,25 @@ packages: version: 5.2.1 sha256: d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.0-pyhd8ed1ab_0.conda + sha256: 6d977f0b2fc24fee21a9554389ab83070db341af6d6f09285360b2e09ef8b26e + md5: 003b8ba0a94e2f1e117d0bd46aebc901 + depends: + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/distlib?source=hash-mapping + size: 275642 + timestamp: 1752823081585 +- pypi: https://files.pythonhosted.org/packages/52/f2/9e726e4489f947557290605e3ee33b6ba09fe5dac9213263e0ada75200cf/dna_features_viewer-3.1.5-py3-none-any.whl + name: dna-features-viewer + version: 3.1.5 + sha256: a8a383d5340d35979be50224f12f4f426ab004394d9b9a33c99d59404469fb7a + requires_dist: + - matplotlib>=3 + - biopython + - packaging - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.21.2-pyhd8ed1ab_1.conda sha256: fa5966bb1718bbf6967a85075e30e4547901410cc7cb7b16daf68942e9a94823 md5: 24c1ca34138ee57de72a943237cde4cc @@ -500,6 +1701,28 @@ packages: - pkg:pypi/docutils?source=hash-mapping size: 402700 timestamp: 1733217860944 +- conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.22-pyhd8ed1ab_0.conda + sha256: dd585e49f231ec414e6550783f2aff85027fa829e5d66004ad702e1cfa6324aa + md5: 140faac6cff4382f5ea077ca618b2931 + depends: + - python >=3.9 + license: CC-PDDC AND BSD-3-Clause AND BSD-2-Clause AND ZPL-2.1 + purls: + - pkg:pypi/docutils?source=hash-mapping + size: 436452 + timestamp: 1753875179563 +- conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.3.1-h5888daf_0.conda + sha256: 1bcc132fbcc13f9ad69da7aa87f60ea41de7ed4d09f3a00ff6e0e70e1c690bc2 + md5: bfd56492d8346d669010eccafe0ba058 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 69544 + timestamp: 1739569648873 - pypi: https://files.pythonhosted.org/packages/87/62/9773de14fe6c45c23649e98b83231fffd7b9892b6cf863251dc2afa73643/einops-0.8.1-py3-none-any.whl name: einops version: 0.8.1 @@ -508,7 +1731,7 @@ packages: - pypi: ./ name: esm version: 3.2.1 - sha256: 185b48ecfe9e718846b1d103c1c5a3e9abd737f986bf122801a81ff18c721afd + sha256: 114cd4b1eee83b5f746b9da2a2509c36faecefed37bace6de1a910c71a108f4b requires_dist: - torch>=2.2.0 - torchvision @@ -524,10 +1747,40 @@ packages: - attrs - pandas - cloudpathlib + - httpx - tenacity - zstd - requires_python: '>=3.10' + - ipywidgets + - py3dmol + - pydssp + - boto3 + - pygtrie + - dna-features-viewer + - flash-attn @ https://github.com/Dao-AILab/flash-attention/releases/download/v2.8.1/flash_attn-2.8.1+cu12torch2.7cxx11abiFALSE-cp312-cp312-linux_x86_64.whl + requires_python: '>=3.12,<3.13' editable: true +- conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda + sha256: ce61f4f99401a4bd455b89909153b40b9c823276aefcbb06f2044618696009ca + md5: 72e42d28960d875c7654614f8b50939a + depends: + - python >=3.9 + - typing_extensions >=4.6.0 + license: MIT and PSF-2.0 + purls: + - pkg:pypi/exceptiongroup?source=hash-mapping + size: 21284 + timestamp: 1746947398083 +- conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_1.conda + sha256: 9abc6c128cd40733e9b24284d0462e084d4aff6afe614f0754aa8533ebe505e4 + md5: a71efeae2c160f6789900ba2631a2c90 + depends: + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/execnet?source=hash-mapping + size: 38835 + timestamp: 1733231086305 - pypi: https://files.pythonhosted.org/packages/7b/8f/c4d9bafc34ad7ad5d8dc16dd1347ee0e507a52c3adb6bfa8887e1c6a26ba/executing-2.2.0-py2.py3-none-any.whl name: executing version: 2.2.0 @@ -553,29 +1806,258 @@ packages: purls: [] size: 140050 timestamp: 1743431809745 -- pypi: https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl - name: filelock - version: 3.18.0 - sha256: c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de - requires_dist: - - furo>=2024.8.6 ; extra == 'docs' - - sphinx-autodoc-typehints>=3 ; extra == 'docs' - - sphinx>=8.1.3 ; extra == 'docs' - - covdefaults>=2.3 ; extra == 'testing' - - coverage>=7.6.10 ; extra == 'testing' - - diff-cover>=9.2.1 ; extra == 'testing' - - pytest-asyncio>=0.25.2 ; extra == 'testing' - - pytest-cov>=6 ; extra == 'testing' - - pytest-mock>=3.14 ; extra == 'testing' - - pytest-timeout>=2.3.1 ; extra == 'testing' - - pytest>=8.3.4 ; extra == 'testing' - - virtualenv>=20.28.1 ; extra == 'testing' - - typing-extensions>=4.12.2 ; python_full_version < '3.11' and extra == 'typing' +- conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.18.0-pyhd8ed1ab_0.conda + sha256: de7b6d4c4f865609ae88db6fa03c8b7544c2452a1aa5451eb7700aad16824570 + md5: 4547b39256e296bb758166893e909a7c + depends: + - python >=3.9 + license: Unlicense + purls: + - pkg:pypi/filelock?source=hash-mapping + size: 17887 + timestamp: 1741969612334 +- pypi: direct+https://github.com/Dao-AILab/flash-attention/releases/download/v2.8.1/flash_attn-2.8.1+cu12torch2.7cxx11abiFALSE-cp312-cp312-linux_x86_64.whl + name: flash-attn + version: 2.8.1 + requires_dist: + - torch + - einops + requires_python: '>=3.9' +- 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.15.0-h7e30c49_1.conda + sha256: 7093aa19d6df5ccb6ca50329ef8510c6acb6b0d8001191909397368b65b02113 + md5: 8f5b0b297b59e1ac160ad4beec99dbee + depends: + - __glibc >=2.17,<3.0.a0 + - freetype >=2.12.1,<3.0a0 + - libexpat >=2.6.3,<3.0a0 + - libgcc >=13 + - libuuid >=2.38.1,<3.0a0 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 265599 + timestamp: 1730283881107 +- 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-0.tar.bz2 + sha256: 53f23a3319466053818540bcdf2091f253cbdbab1e0e9ae7b9e509dcaa2a5e38 + md5: f766549260d6815b0c52253f1fb1bb29 + depends: + - font-ttf-dejavu-sans-mono + - font-ttf-inconsolata + - font-ttf-source-code-pro + - font-ttf-ubuntu + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 4102 + timestamp: 1566932280397 +- conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.59.0-py312h8a5da7c_0.conda + sha256: ead830a4d12f26066f09b6ea54fb5c9e26a548c901063381412636db92cf7f61 + md5: 008d44a468c24a59d2e67c014fba8f12 + 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: 2854951 + timestamp: 1752723143 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fonttools-4.59.0-py312h6daa0e5_0.conda + sha256: fb5dabc7db09891e611723622c762f625f287fc54d1f914497baf95b713513c3 + md5: 0fed8437f0bd51c23d4caa1a61fe7b3b + 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: 2794146 + timestamp: 1752723166136 +- conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.13.3-ha770c72_1.conda + sha256: 7ef7d477c43c12a5b4cddcf048a83277414512d1116aba62ebadfa7056a7d84f + md5: 9ccd736d31e0c6e41f54e704e5312811 + depends: + - libfreetype 2.13.3 ha770c72_1 + - libfreetype6 2.13.3 h48d6fc4_1 + license: GPL-2.0-only OR FTL + purls: [] + size: 172450 + timestamp: 1745369996765 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.13.3-hce30654_1.conda + sha256: 6b63c72ea51a41d41964841404564c0729fdddd3e952e2715839fd759b7cfdfc + md5: e684de4644067f1956a580097502bf03 + depends: + - libfreetype 2.13.3 hce30654_1 + - libfreetype6 2.13.3 h1d14073_1 + license: GPL-2.0-only OR FTL + purls: [] + size: 172220 + timestamp: 1745370149658 +- pypi: https://files.pythonhosted.org/packages/44/4b/e0cfc1a6f17e990f3e64b7d941ddc4acdc7b19d6edd51abf495f32b1a9e4/fsspec-2025.3.2-py3-none-any.whl + name: fsspec + version: 2025.3.2 + sha256: 2daf8dc3d1dfa65b6aa37748d112773a7a08416f6c70d96b264c96476ecaf711 + requires_dist: + - adlfs ; extra == 'abfs' + - adlfs ; extra == 'adl' + - pyarrow>=1 ; extra == 'arrow' + - dask ; extra == 'dask' + - distributed ; extra == 'dask' + - pre-commit ; extra == 'dev' + - ruff ; extra == 'dev' + - numpydoc ; extra == 'doc' + - sphinx ; extra == 'doc' + - sphinx-design ; extra == 'doc' + - sphinx-rtd-theme ; extra == 'doc' + - yarl ; extra == 'doc' + - dropbox ; extra == 'dropbox' + - dropboxdrivefs ; extra == 'dropbox' + - requests ; extra == 'dropbox' + - adlfs ; extra == 'full' + - aiohttp!=4.0.0a0,!=4.0.0a1 ; extra == 'full' + - dask ; extra == 'full' + - distributed ; extra == 'full' + - dropbox ; extra == 'full' + - dropboxdrivefs ; extra == 'full' + - fusepy ; extra == 'full' + - gcsfs ; extra == 'full' + - libarchive-c ; extra == 'full' + - ocifs ; extra == 'full' + - panel ; extra == 'full' + - paramiko ; extra == 'full' + - pyarrow>=1 ; extra == 'full' + - pygit2 ; extra == 'full' + - requests ; extra == 'full' + - s3fs ; extra == 'full' + - smbprotocol ; extra == 'full' + - tqdm ; extra == 'full' + - fusepy ; extra == 'fuse' + - gcsfs ; extra == 'gcs' + - pygit2 ; extra == 'git' + - requests ; extra == 'github' + - gcsfs ; extra == 'gs' + - panel ; extra == 'gui' + - pyarrow>=1 ; extra == 'hdfs' + - aiohttp!=4.0.0a0,!=4.0.0a1 ; extra == 'http' + - libarchive-c ; extra == 'libarchive' + - ocifs ; extra == 'oci' + - s3fs ; extra == 's3' + - paramiko ; extra == 'sftp' + - smbprotocol ; extra == 'smb' + - paramiko ; extra == 'ssh' + - aiohttp!=4.0.0a0,!=4.0.0a1 ; extra == 'test' + - numpy ; extra == 'test' + - pytest ; extra == 'test' + - pytest-asyncio!=0.22.0 ; extra == 'test' + - pytest-benchmark ; extra == 'test' + - pytest-cov ; extra == 'test' + - pytest-mock ; extra == 'test' + - pytest-recording ; extra == 'test' + - pytest-rerunfailures ; extra == 'test' + - requests ; extra == 'test' + - aiobotocore>=2.5.4,<3.0.0 ; extra == 'test-downstream' + - dask[dataframe,test] ; extra == 'test-downstream' + - moto[server]>4,<5 ; extra == 'test-downstream' + - pytest-timeout ; extra == 'test-downstream' + - xarray ; extra == 'test-downstream' + - adlfs ; extra == 'test-full' + - aiohttp!=4.0.0a0,!=4.0.0a1 ; extra == 'test-full' + - cloudpickle ; extra == 'test-full' + - dask ; extra == 'test-full' + - distributed ; extra == 'test-full' + - dropbox ; extra == 'test-full' + - dropboxdrivefs ; extra == 'test-full' + - fastparquet ; extra == 'test-full' + - fusepy ; extra == 'test-full' + - gcsfs ; extra == 'test-full' + - jinja2 ; extra == 'test-full' + - kerchunk ; extra == 'test-full' + - libarchive-c ; extra == 'test-full' + - lz4 ; extra == 'test-full' + - notebook ; extra == 'test-full' + - numpy ; extra == 'test-full' + - ocifs ; extra == 'test-full' + - pandas ; extra == 'test-full' + - panel ; extra == 'test-full' + - paramiko ; extra == 'test-full' + - pyarrow ; extra == 'test-full' + - pyarrow>=1 ; extra == 'test-full' + - pyftpdlib ; extra == 'test-full' + - pygit2 ; extra == 'test-full' + - pytest ; extra == 'test-full' + - pytest-asyncio!=0.22.0 ; extra == 'test-full' + - pytest-benchmark ; extra == 'test-full' + - pytest-cov ; extra == 'test-full' + - pytest-mock ; extra == 'test-full' + - pytest-recording ; extra == 'test-full' + - pytest-rerunfailures ; extra == 'test-full' + - python-snappy ; extra == 'test-full' + - requests ; extra == 'test-full' + - smbprotocol ; extra == 'test-full' + - tqdm ; extra == 'test-full' + - urllib3 ; extra == 'test-full' + - zarr ; extra == 'test-full' + - zstandard ; extra == 'test-full' + - tqdm ; extra == 'tqdm' requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/44/4b/e0cfc1a6f17e990f3e64b7d941ddc4acdc7b19d6edd51abf495f32b1a9e4/fsspec-2025.3.2-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/2f/e0/014d5d9d7a4564cf1c40b5039bc882db69fd881111e03ab3657ac0b218e2/fsspec-2025.7.0-py3-none-any.whl name: fsspec - version: 2025.3.2 - sha256: 2daf8dc3d1dfa65b6aa37748d112773a7a08416f6c70d96b264c96476ecaf711 + version: 2025.7.0 + sha256: 8b012e39f63c7d5f10474de957f3ab793b47b45ae7d39f2fb735f8bbe25c0e21 requires_dist: - adlfs ; extra == 'abfs' - adlfs ; extra == 'adl' @@ -583,7 +2065,7 @@ packages: - dask ; extra == 'dask' - distributed ; extra == 'dask' - pre-commit ; extra == 'dev' - - ruff ; extra == 'dev' + - ruff>=0.5 ; extra == 'dev' - numpydoc ; extra == 'doc' - sphinx ; extra == 'doc' - sphinx-design ; extra == 'doc' @@ -676,9 +2158,26 @@ packages: - tqdm ; extra == 'test-full' - urllib3 ; extra == 'test-full' - zarr ; extra == 'test-full' - - zstandard ; extra == 'test-full' + - zstandard ; python_full_version < '3.14' and extra == 'test-full' - tqdm ; extra == 'tqdm' requires_python: '>=3.9' +- conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.14-hecca717_1.conda + sha256: 060dbb9e8f025cd09819586dd9c5a9c29bfcff0ac222435c90f4a83655caef7e + md5: d8f05f0493cacd0b29cbc0049669151f + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: LGPL-2.0-or-later + license_family: LGPL + purls: [] + size: 99475 + timestamp: 1754260291932 +- pypi: https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl + name: h11 + version: 0.16.0 + sha256: 63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86 + requires_python: '>=3.8' - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.2.0-pyhd8ed1ab_0.conda sha256: 0aa1cdc67a9fe75ea95b5644b734a756200d6ec9d0dff66530aec3d1c1e9df75 md5: b4754fb1bdcb70c8fd54f918301582c6 @@ -692,6 +2191,34 @@ packages: - pkg:pypi/h2?source=hash-mapping size: 53888 timestamp: 1738578623567 +- conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-11.2.1-h3beb420_0.conda + sha256: 5bd0f3674808862838d6e2efc0b3075e561c34309c5c2f4c976f7f1f57c91112 + md5: 0e6e192d4b3d95708ad192d957cf3163 + depends: + - __glibc >=2.17,<3.0.a0 + - cairo >=1.18.4,<2.0a0 + - freetype + - graphite2 + - icu >=75.1,<76.0a0 + - libexpat >=2.7.0,<3.0a0 + - libfreetype >=2.13.3 + - libfreetype6 >=2.13.3 + - libgcc >=13 + - libglib >=2.84.1,<3.0a0 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 1730226 + timestamp: 1747091044218 +- pypi: https://files.pythonhosted.org/packages/51/a3/fa5897099454aa287022a34a30e68dbff0e617760f774f8bd1db17f06bd4/hf_xet-1.1.7-cp37-abi3-macosx_11_0_arm64.whl + name: hf-xet + version: 1.1.7 + sha256: b109f4c11e01c057fc82004c9e51e6cdfe2cb230637644ade40c599739067b2e + requires_dist: + - pytest ; extra == 'tests' + requires_python: '>=3.8' - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda sha256: 6ad78a180576c706aabeb5b4c8ceb97c0cb25f1e112d76495bff23e3779948ba md5: 0a802cb9888dd14eeefc611f05c40b6e @@ -703,6 +2230,36 @@ packages: - pkg:pypi/hpack?source=hash-mapping size: 30731 timestamp: 1737618390337 +- pypi: https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl + name: httpcore + version: 1.0.9 + sha256: 2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55 + requires_dist: + - certifi + - h11>=0.16 + - anyio>=4.0,<5.0 ; extra == 'asyncio' + - h2>=3,<5 ; extra == 'http2' + - socksio==1.* ; extra == 'socks' + - trio>=0.22.0,<1.0 ; extra == 'trio' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl + name: httpx + version: 0.28.1 + sha256: d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad + requires_dist: + - anyio + - certifi + - httpcore==1.* + - idna + - brotli ; platform_python_implementation == 'CPython' and extra == 'brotli' + - brotlicffi ; platform_python_implementation != 'CPython' and extra == 'brotli' + - click==8.* ; extra == 'cli' + - pygments==2.* ; extra == 'cli' + - rich>=10,<14 ; extra == 'cli' + - h2>=3,<5 ; extra == 'http2' + - socksio==1.* ; extra == 'socks' + - zstandard>=0.18.0 ; extra == 'zstd' + requires_python: '>=3.8' - pypi: https://files.pythonhosted.org/packages/99/e3/2232d0e726d4d6ea69643b9593d97d0e7e6ea69c2fe9ed5de34d476c1c47/huggingface_hub-0.30.1-py3-none-any.whl name: huggingface-hub version: 0.30.1 @@ -814,6 +2371,137 @@ packages: - types-tqdm ; extra == 'typing' - types-urllib3 ; extra == 'typing' requires_python: '>=3.8.0' +- pypi: https://files.pythonhosted.org/packages/39/7b/bb06b061991107cd8783f300adff3e7b7f284e330fd82f507f2a1417b11d/huggingface_hub-0.34.4-py3-none-any.whl + name: huggingface-hub + version: 0.34.4 + sha256: 9b365d781739c93ff90c359844221beef048403f1bc1f1c123c191257c3c890a + requires_dist: + - filelock + - fsspec>=2023.5.0 + - packaging>=20.9 + - pyyaml>=5.1 + - requests + - tqdm>=4.42.1 + - typing-extensions>=3.7.4.3 + - hf-xet>=1.1.3,<2.0.0 ; platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64' + - inquirerpy==0.3.4 ; extra == 'all' + - aiohttp ; extra == 'all' + - authlib>=1.3.2 ; extra == 'all' + - fastapi ; extra == 'all' + - httpx ; extra == 'all' + - itsdangerous ; extra == 'all' + - jedi ; extra == 'all' + - jinja2 ; extra == 'all' + - pytest>=8.1.1,<8.2.2 ; extra == 'all' + - pytest-cov ; extra == 'all' + - pytest-env ; extra == 'all' + - pytest-xdist ; extra == 'all' + - pytest-vcr ; extra == 'all' + - pytest-asyncio ; extra == 'all' + - pytest-rerunfailures ; extra == 'all' + - pytest-mock ; extra == 'all' + - urllib3<2.0 ; extra == 'all' + - soundfile ; extra == 'all' + - pillow ; extra == 'all' + - gradio>=4.0.0 ; extra == 'all' + - numpy ; extra == 'all' + - ruff>=0.9.0 ; extra == 'all' + - libcst>=1.4.0 ; extra == 'all' + - typing-extensions>=4.8.0 ; extra == 'all' + - types-pyyaml ; extra == 'all' + - types-requests ; extra == 'all' + - types-simplejson ; extra == 'all' + - types-toml ; extra == 'all' + - types-tqdm ; extra == 'all' + - types-urllib3 ; extra == 'all' + - mypy>=1.14.1,<1.15.0 ; python_full_version == '3.8.*' and extra == 'all' + - mypy==1.15.0 ; python_full_version >= '3.9' and extra == 'all' + - inquirerpy==0.3.4 ; extra == 'cli' + - inquirerpy==0.3.4 ; extra == 'dev' + - aiohttp ; extra == 'dev' + - authlib>=1.3.2 ; extra == 'dev' + - fastapi ; extra == 'dev' + - httpx ; extra == 'dev' + - itsdangerous ; extra == 'dev' + - jedi ; extra == 'dev' + - jinja2 ; extra == 'dev' + - pytest>=8.1.1,<8.2.2 ; extra == 'dev' + - pytest-cov ; extra == 'dev' + - pytest-env ; extra == 'dev' + - pytest-xdist ; extra == 'dev' + - pytest-vcr ; extra == 'dev' + - pytest-asyncio ; extra == 'dev' + - pytest-rerunfailures ; extra == 'dev' + - pytest-mock ; extra == 'dev' + - urllib3<2.0 ; extra == 'dev' + - soundfile ; extra == 'dev' + - pillow ; extra == 'dev' + - gradio>=4.0.0 ; extra == 'dev' + - numpy ; extra == 'dev' + - ruff>=0.9.0 ; extra == 'dev' + - libcst>=1.4.0 ; extra == 'dev' + - typing-extensions>=4.8.0 ; extra == 'dev' + - types-pyyaml ; extra == 'dev' + - types-requests ; extra == 'dev' + - types-simplejson ; extra == 'dev' + - types-toml ; extra == 'dev' + - types-tqdm ; extra == 'dev' + - types-urllib3 ; extra == 'dev' + - mypy>=1.14.1,<1.15.0 ; python_full_version == '3.8.*' and extra == 'dev' + - mypy==1.15.0 ; python_full_version >= '3.9' and extra == 'dev' + - toml ; extra == 'fastai' + - fastai>=2.4 ; extra == 'fastai' + - fastcore>=1.3.27 ; extra == 'fastai' + - hf-transfer>=0.1.4 ; extra == 'hf-transfer' + - hf-xet>=1.1.2,<2.0.0 ; extra == 'hf-xet' + - aiohttp ; extra == 'inference' + - mcp>=1.8.0 ; extra == 'mcp' + - typer ; extra == 'mcp' + - aiohttp ; extra == 'mcp' + - authlib>=1.3.2 ; extra == 'oauth' + - fastapi ; extra == 'oauth' + - httpx ; extra == 'oauth' + - itsdangerous ; extra == 'oauth' + - ruff>=0.9.0 ; extra == 'quality' + - libcst>=1.4.0 ; extra == 'quality' + - mypy>=1.14.1,<1.15.0 ; python_full_version == '3.8.*' and extra == 'quality' + - mypy==1.15.0 ; python_full_version >= '3.9' and extra == 'quality' + - tensorflow ; extra == 'tensorflow' + - pydot ; extra == 'tensorflow' + - graphviz ; extra == 'tensorflow' + - tensorflow ; extra == 'tensorflow-testing' + - keras<3.0 ; extra == 'tensorflow-testing' + - inquirerpy==0.3.4 ; extra == 'testing' + - aiohttp ; extra == 'testing' + - authlib>=1.3.2 ; extra == 'testing' + - fastapi ; extra == 'testing' + - httpx ; extra == 'testing' + - itsdangerous ; extra == 'testing' + - jedi ; extra == 'testing' + - jinja2 ; extra == 'testing' + - pytest>=8.1.1,<8.2.2 ; extra == 'testing' + - pytest-cov ; extra == 'testing' + - pytest-env ; extra == 'testing' + - pytest-xdist ; extra == 'testing' + - pytest-vcr ; extra == 'testing' + - pytest-asyncio ; extra == 'testing' + - pytest-rerunfailures ; extra == 'testing' + - pytest-mock ; extra == 'testing' + - urllib3<2.0 ; extra == 'testing' + - soundfile ; extra == 'testing' + - pillow ; extra == 'testing' + - gradio>=4.0.0 ; extra == 'testing' + - numpy ; extra == 'testing' + - torch ; extra == 'torch' + - safetensors[torch] ; extra == 'torch' + - typing-extensions>=4.8.0 ; extra == 'typing' + - types-pyyaml ; extra == 'typing' + - types-requests ; extra == 'typing' + - types-simplejson ; extra == 'typing' + - types-toml ; extra == 'typing' + - types-tqdm ; extra == 'typing' + - types-urllib3 ; extra == 'typing' + requires_python: '>=3.8.0' - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda sha256: 77af6f5fe8b62ca07d09ac60127a30d9069fdc3c68d6b256754d0ffb1f7779f8 md5: 8e6923fc12f1fe8f8c4e5c9f343256ac @@ -825,6 +2513,28 @@ packages: - pkg:pypi/hyperframe?source=hash-mapping size: 17397 timestamp: 1737618427549 +- conda: https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda + sha256: 71e750d509f5fa3421087ba88ef9a7b9be11c53174af3aa4d06aff4c18b38e8e + md5: 8b189310083baabfb622af68fd9d3ae3 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: MIT + license_family: MIT + purls: [] + size: 12129203 + timestamp: 1720853576813 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda + sha256: 9ba12c93406f3df5ab0a43db8a4b4ef67a5871dfd401010fbe29b218b2cbe620 + md5: 5eb22c1d7b3fc4abb50d92d621583137 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 11857802 + timestamp: 1720853997952 - conda: https://conda.anaconda.org/conda-forge/noarch/id-1.5.0-pyh29332c3_0.conda sha256: 161e3eb5aba887d0329bb4099f72cb92eed9072cf63f551d08540480116e69a2 md5: d37314c8f553e3b4b44d113a0ee10196 @@ -838,6 +2548,30 @@ packages: - pkg:pypi/id?source=hash-mapping size: 24444 timestamp: 1737528654512 +- conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.12-pyhd8ed1ab_0.conda + sha256: 4debbae49a183d61f0747a5f594fca2bf5121e8508a52116f50ccd0eb2f7bb55 + md5: 84463b10c1eb198541cd54125c7efe90 + depends: + - python >=3.9 + - ukkonen + license: MIT + license_family: MIT + purls: + - pkg:pypi/identify?source=hash-mapping + size: 78926 + timestamp: 1748049754416 +- conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.13-pyhd8ed1ab_0.conda + sha256: 7183512c24050c541d332016c1dd0f2337288faf30afc42d60981a49966059f7 + md5: 52083ce9103ec11c8130ce18517d3e83 + depends: + - python >=3.9 + - ukkonen + license: MIT + license_family: MIT + purls: + - pkg:pypi/identify?source=hash-mapping + size: 79080 + timestamp: 1754777609249 - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda sha256: d7a472c9fd479e2e8dcb83fb8d433fce971ea369d704ece380e876f9c3494e87 md5: 39a4f67be3286c86d696df570b1201b7 @@ -861,6 +2595,19 @@ packages: - pkg:pypi/importlib-metadata?source=compressed-mapping size: 29141 timestamp: 1737420302391 +- conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda + sha256: c18ab120a0613ada4391b15981d86ff777b5690ca461ea7e9e49531e8f374745 + md5: 63ccfdc3a3ce25b027b8767eb722fca8 + depends: + - python >=3.9 + - zipp >=3.20 + - python + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/importlib-metadata?source=hash-mapping + size: 34641 + timestamp: 1747934053147 - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.5.2-pyhd8ed1ab_0.conda sha256: acc1d991837c0afb67c75b77fdc72b4bf022aac71fedd8b9ea45918ac9b08a80 md5: c85c76dc67d75619a92f51dfbce06992 @@ -875,6 +2622,17 @@ packages: - pkg:pypi/importlib-resources?source=hash-mapping size: 33781 timestamp: 1736252433366 +- conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda + sha256: 0ec8f4d02053cd03b0f3e63168316530949484f80e16f5e2fb199a1d117a89ca + md5: 6837f3eff7dcea42ecd714ce1ac2b108 + depends: + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/iniconfig?source=hash-mapping + size: 11474 + timestamp: 1733223232820 - pypi: https://files.pythonhosted.org/packages/20/3a/917cb9e72f4e1a4ea13c862533205ae1319bd664119189ee5cc9e4e95ebf/ipython-9.0.2-py3-none-any.whl name: ipython version: 9.0.2 @@ -917,6 +2675,51 @@ packages: - matplotlib ; extra == 'matplotlib' - ipython[doc,matplotlib,test,test-extra] ; extra == 'all' requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/63/f8/0031ee2b906a15a33d6bfc12dd09c3dfa966b3cb5b284ecfb7549e6ac3c4/ipython-9.4.0-py3-none-any.whl + name: ipython + version: 9.4.0 + sha256: 25850f025a446d9b359e8d296ba175a36aedd32e83ca9b5060430fe16801f066 + requires_dist: + - colorama ; sys_platform == 'win32' + - decorator + - ipython-pygments-lexers + - jedi>=0.16 + - matplotlib-inline + - pexpect>4.3 ; sys_platform != 'emscripten' and sys_platform != 'win32' + - prompt-toolkit>=3.0.41,<3.1.0 + - pygments>=2.4.0 + - stack-data + - traitlets>=5.13.0 + - typing-extensions>=4.6 ; python_full_version < '3.12' + - black ; extra == 'black' + - docrepr ; extra == 'doc' + - exceptiongroup ; extra == 'doc' + - intersphinx-registry ; extra == 'doc' + - ipykernel ; extra == 'doc' + - ipython[test] ; extra == 'doc' + - matplotlib ; extra == 'doc' + - setuptools>=18.5 ; extra == 'doc' + - sphinx-toml==0.0.4 ; extra == 'doc' + - sphinx-rtd-theme ; extra == 'doc' + - sphinx>=1.3 ; extra == 'doc' + - typing-extensions ; extra == 'doc' + - pytest ; extra == 'test' + - pytest-asyncio<0.22 ; extra == 'test' + - testpath ; extra == 'test' + - packaging ; extra == 'test' + - ipython[test] ; extra == 'test-extra' + - curio ; extra == 'test-extra' + - jupyter-ai ; extra == 'test-extra' + - matplotlib!=3.2.0 ; extra == 'test-extra' + - nbformat ; extra == 'test-extra' + - nbclient ; extra == 'test-extra' + - ipykernel ; extra == 'test-extra' + - numpy>=1.23 ; extra == 'test-extra' + - pandas ; extra == 'test-extra' + - trio ; extra == 'test-extra' + - matplotlib ; extra == 'matplotlib' + - ipython[doc,matplotlib,test,test-extra] ; extra == 'all' + requires_python: '>=3.11' - pypi: https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl name: ipython-pygments-lexers version: 1.1.1 @@ -924,6 +2727,22 @@ packages: requires_dist: - pygments requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/58/6a/9166369a2f092bd286d24e6307de555d63616e8ddb373ebad2b5635ca4cd/ipywidgets-8.1.7-py3-none-any.whl + name: ipywidgets + version: 8.1.7 + sha256: 764f2602d25471c213919b8a1997df04bef869251db4ca8efba1b76b1bd9f7bb + requires_dist: + - comm>=0.1.3 + - ipython>=6.1.0 + - traitlets>=4.3.1 + - widgetsnbextension~=4.0.14 + - jupyterlab-widgets~=3.0.15 + - jsonschema ; extra == 'test' + - ipykernel ; extra == 'test' + - pytest>=3.6.0 ; extra == 'test' + - pytest-cov ; extra == 'test' + - pytz ; extra == 'test' + requires_python: '>=3.7' - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.classes-3.4.0-pyhd8ed1ab_2.conda sha256: 3d16a0fa55a29fe723c918a979b2ee927eb0bf9616381cdfd26fa9ea2b649546 md5: ade6b25a6136661dadd1a43e4350b10b @@ -960,6 +2779,18 @@ packages: - pkg:pypi/jaraco-functools?source=hash-mapping size: 15545 timestamp: 1733746481844 +- conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.functools-4.2.1-pyhd8ed1ab_0.conda + sha256: f132ac71f89e3133fe159034ec85cec946c75f2c60e2039a8bbd1012721a785e + md5: c2c206c4054db7a655761c9e5bbb11f7 + depends: + - more-itertools + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/jaraco-functools?source=hash-mapping + size: 16187 + timestamp: 1751918863003 - pypi: https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl name: jedi version: 0.19.2 @@ -1019,11 +2850,43 @@ packages: - markupsafe>=2.0 - babel>=2.7 ; extra == 'i18n' requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/31/b4/b9b800c45527aadd64d5b442f9b932b00648617eb5d63d2c7a6587b7cafc/jmespath-1.0.1-py3-none-any.whl + name: jmespath + version: 1.0.1 + sha256: 02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980 + requires_python: '>=3.7' - pypi: https://files.pythonhosted.org/packages/91/29/df4b9b42f2be0b623cbd5e2140cafcaa2bef0759a00b7b70104dcfe2fb51/joblib-1.4.2-py3-none-any.whl name: joblib version: 1.4.2 sha256: 06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6 requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/7d/4f/1195bbac8e0c2acc5f740661631d8d750dc38d4a32b23ee5df3cde6f4e0d/joblib-1.5.1-py3-none-any.whl + name: joblib + version: 1.5.1 + sha256: 4719a31f054c7d766948dcd83e9613686b27114f190f717cec7eaa2084f8a74a + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/43/6a/ca128561b22b60bd5a0c4ea26649e68c8556b82bc70a0c396eebc977fe86/jupyterlab_widgets-3.0.15-py3-none-any.whl + name: jupyterlab-widgets + version: 3.0.15 + sha256: d59023d7d7ef71400d51e6fee9a88867f6e65e10a4201605d2d7f3e8f012a31c + requires_python: '>=3.7' +- conda: https://conda.anaconda.org/conda-forge/noarch/keyring-25.6.0-pyh534df25_0.conda + sha256: c8b436fa9853bf8b836c96afbb7684a04955b80b37f5d5285fd836b6a8566cc5 + md5: d2c0c5bda93c249f877c7fceea9e63af + depends: + - __osx + - importlib-metadata >=4.11.4 + - importlib_resources + - jaraco.classes + - jaraco.context + - jaraco.functools + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/keyring?source=hash-mapping + size: 37280 + timestamp: 1735210369348 - conda: https://conda.anaconda.org/conda-forge/noarch/keyring-25.6.0-pyha804496_0.conda sha256: b6f57c17cf098022c32fe64e85e9615d427a611c48a5947cdfc357490210a124 md5: cdd58ab99c214b55d56099108a914282 @@ -1052,6 +2915,36 @@ packages: purls: [] size: 117831 timestamp: 1646151697040 +- conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.8-py312h68727a3_1.conda + sha256: 34814cea4b92d17237211769f2ec5b739a328849b152a2f5736183c52d48cafc + md5: a8ea818e46addfa842348701a9dbe8f8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/kiwisolver?source=hash-mapping + size: 72166 + timestamp: 1751493973594 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.4.9-py312hdc12c9d_0.conda + sha256: 290d8f1016c9581bd4d2246bb21832ba4e4ba1c7b059eb9106d92bba561bccc7 + md5: 91384df8de4c340a1232793cf39a12ce + 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: 67692 + timestamp: 1754889447292 - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda sha256: 99df692f7a8a5c27cd14b5fb1374ee55e756631b9c3d659ed3ee60830249b238 md5: 3f43953b7d3fb3aaa1d0d0723d91e368 @@ -1067,6 +2960,45 @@ packages: purls: [] size: 1370023 timestamp: 1719463201255 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda + sha256: 4442f957c3c77d69d9da3521268cad5d54c9033f1a73f99cde0a3658937b159b + md5: c6dc8a0fdec13a0565936655c33069a1 + depends: + - __osx >=11.0 + - libcxx >=16 + - libedit >=3.1.20191231,<3.2.0a0 + - libedit >=3.1.20191231,<4.0a0 + - openssl >=3.3.1,<4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 1155530 + timestamp: 1719463474401 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.17-h717163a_0.conda + sha256: d6a61830a354da022eae93fa896d0991385a875c6bba53c82263a289deda9db8 + md5: 000e85703f0fd9594c81710dd5066471 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.7.0,<4.8.0a0 + license: MIT + license_family: MIT + purls: [] + size: 248046 + timestamp: 1739160907615 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.17-h7eeda09_0.conda + sha256: 310a62c2f074ebd5aa43b3cd4b00d46385ce680fa2132ecee255a200e2d2f15f + md5: 92a61fd30b19ebd5c1621a5bfe6d8b5f + depends: + - __osx >=11.0 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.7.0,<4.8.0a0 + license: MIT + license_family: MIT + purls: [] + size: 212125 + timestamp: 1739161108467 - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_4.conda sha256: db73f38155d901a610b2320525b9dd3b31e4949215c870685fd92ea61b5ce472 md5: 01f8d123c96816249efd255a31ad7712 @@ -1079,6 +3011,202 @@ packages: purls: [] size: 671240 timestamp: 1740155456116 +- 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/libblas-3.9.0-31_h59b9bed_openblas.conda + build_number: 31 + sha256: 9839fc4ac0cbb0aa3b9eea520adfb57311838959222654804e58f6f2d1771db5 + md5: 728dbebd0f7a20337218beacffd37916 + depends: + - libopenblas >=0.3.29,<0.3.30.0a0 + - libopenblas >=0.3.29,<1.0a0 + constrains: + - liblapacke =3.9.0=31*_openblas + - liblapack =3.9.0=31*_openblas + - blas =2.131=openblas + - mkl <2025 + - libcblas =3.9.0=31*_openblas + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 16859 + timestamp: 1740087969120 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.9.0-34_h10e41b3_openblas.conda + build_number: 34 + sha256: 5de3c3bfcdc8ba05da1a7815c9953fe392c2065d9efdc2491f91df6d0d1d9e76 + md5: cdb3e1ca1661dbf19f9aad7dad524996 + depends: + - libopenblas >=0.3.30,<0.3.31.0a0 + - libopenblas >=0.3.30,<1.0a0 + constrains: + - blas 2.134 openblas + - mkl <2025 + - liblapacke 3.9.0 34*_openblas + - libcblas 3.9.0 34*_openblas + - liblapack 3.9.0 34*_openblas + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 19533 + timestamp: 1754678956963 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb9d3cd8_2.conda + sha256: d9db2de60ea917298e658143354a530e9ca5f9c63471c65cf47ab39fd2f429e3 + md5: 41b599ed2b02abcfdd84302bff174b23 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + purls: [] + size: 68851 + timestamp: 1725267660471 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.1.0-h5505292_3.conda + sha256: 0e9c196ad8569ca199ea05103707cde0ae3c7e97d0cdf0417d873148ea9ad640 + md5: fbc4d83775515e433ef22c058768b84d + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 68972 + timestamp: 1749230317752 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb9d3cd8_2.conda + sha256: 2892d512cad096cb03f1b66361deeab58b64e15ba525d6592bb6d609e7045edf + md5: 9566f0bd264fbd463002e759b8a82401 + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlicommon 1.1.0 hb9d3cd8_2 + - libgcc >=13 + license: MIT + license_family: MIT + purls: [] + size: 32696 + timestamp: 1725267669305 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.1.0-h5505292_3.conda + sha256: d888c228e7d4f0f2303538f6a9705498c81d56fedaab7811e1186cb6e24d689b + md5: 01c4b35a1c4b94b60801f189f1ac6ee3 + depends: + - __osx >=11.0 + - libbrotlicommon 1.1.0 h5505292_3 + license: MIT + license_family: MIT + purls: [] + size: 29249 + timestamp: 1749230338861 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb9d3cd8_2.conda + sha256: 779f58174e99de3600e939fa46eddb453ec5d3c60bb46cdaa8b4c127224dbf29 + md5: 06f70867945ea6a84d35836af780f1de + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlicommon 1.1.0 hb9d3cd8_2 + - libgcc >=13 + license: MIT + license_family: MIT + purls: [] + size: 281750 + timestamp: 1725267679782 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.1.0-h5505292_3.conda + sha256: 0734a54db818ddfdfbf388fa53c5036a06bbe17de14005f33215d865d51d8a5e + md5: 1ce5e315293309b5bf6778037375fb08 + depends: + - __osx >=11.0 + - libbrotlicommon 1.1.0 h5505292_3 + license: MIT + license_family: MIT + purls: [] + size: 274404 + timestamp: 1749230355483 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-31_he106b2a_openblas.conda + build_number: 31 + sha256: ede8545011f5b208b151fe3e883eb4e31d495ab925ab7b9ce394edca846e0c0d + md5: abb32c727da370c481a1c206f5159ce9 + depends: + - libblas 3.9.0 31_h59b9bed_openblas + constrains: + - liblapacke =3.9.0=31*_openblas + - liblapack =3.9.0=31*_openblas + - blas =2.131=openblas + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 16796 + timestamp: 1740087984429 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.9.0-34_hb3479ef_openblas.conda + build_number: 34 + sha256: 6639f6c6b2e76cb1be62cd6d9033bda7dc3fab2e5a80f5be4b5c522c27dcba17 + md5: e15018d609b8957c146dcb6c356dd50c + depends: + - libblas 3.9.0 34_h10e41b3_openblas + constrains: + - liblapack 3.9.0 34*_openblas + - blas 2.134 openblas + - liblapacke 3.9.0 34*_openblas + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 19521 + timestamp: 1754678970336 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp20.1-20.1.4-default_h1df26ce_0.conda + sha256: 5c28304eaabc2aaeb47e2ba847ae41e0ad7e2a520a7e19a2c5e846c69b417a5b + md5: 96f8d5b2e94c9ba4fef19f1adf068a15 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libllvm20 >=20.1.4,<20.2.0a0 + - libstdcxx >=13 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 20897372 + timestamp: 1746074729385 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-20.1.4-default_he06ed0a_0.conda + sha256: bdcfeb2dde582bec466f86c1fb1f8cbbd413653f60c030040fe1c842fc6da1b4 + md5: 2d933632c8004be47deb2be61bf013be + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libllvm20 >=20.1.4,<20.2.0a0 + - libstdcxx >=13 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 12096222 + timestamp: 1746074937700 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-hb8b1518_5.conda + sha256: cb83980c57e311783ee831832eb2c20ecb41e7dee6e86e8b70b8cef0e43eab55 + md5: d4a250da4737ee127fb1fa6452a9002e + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 4523621 + timestamp: 1749905341688 - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.13.0-h332b0f4_0.conda sha256: 38e528acfaa0276b7052f4de44271ff9293fdb84579650601a8c49dac171482a md5: cbdc92ac0d93fe3c796e36ad65c7905c @@ -1096,6 +3224,65 @@ packages: purls: [] size: 438088 timestamp: 1743601695669 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.14.1-h73640d1_0.conda + sha256: 0055b68137309db41ec34c938d95aec71d1f81bd9d998d5be18f32320c3ccba0 + md5: 1af57c823803941dfc97305248a56d57 + depends: + - __osx >=11.0 + - krb5 >=1.21.3,<1.22.0a0 + - libnghttp2 >=1.64.0,<2.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.0,<4.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: curl + license_family: MIT + purls: [] + size: 403456 + timestamp: 1749033320430 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-20.1.8-hf598326_1.conda + sha256: 119b3ac75cb1ea29981e5053c2cb10d5f0b06fcc81b486cb7281f160daf673a1 + md5: a69ef3239d3268ef8602c7a7823fd982 + depends: + - __osx >=11.0 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 568267 + timestamp: 1752814881595 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.23-h86f0d12_0.conda + sha256: 4db2f70a1441317d964e84c268e388110ad9cf75ca98994d1336d670e62e6f07 + md5: 27fe770decaf469a53f3e3a6d593067f + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + purls: [] + size: 72783 + timestamp: 1745260463421 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.24-h5773f1b_0.conda + sha256: 417d52b19c679e1881cce3f01cad3a2d542098fa2d6df5485aac40f01aede4d1 + md5: 3baf58a5a87e7c2f4d243ce2f8f2fe5c + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 54790 + timestamp: 1747040549847 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.125-hb9d3cd8_0.conda + sha256: f53458db897b93b4a81a6dbfd7915ed8fa4a54951f97c698dde6faa028aadfd2 + md5: 4c0ab57463117fbb8df85268415082f5 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libpciaccess >=0.18,<0.19.0a0 + license: MIT + license_family: MIT + purls: [] + size: 246161 + timestamp: 1749904704373 - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda sha256: d789471216e7aba3c184cd054ed61ce3f6dac6f87a50ec69291b9297f8c18724 md5: c277e0a4d549b03ac1e9d6cbbe3d017b @@ -1109,6 +3296,28 @@ packages: 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 @@ -1119,6 +3328,14 @@ packages: 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.0-h5888daf_0.conda sha256: 33ab03438aee65d6aa667cf7d90c91e5e7d734c19a67aa4c7040742c0a13d505 md5: db0bfbe7dd197b68ad5f30333bae6ce0 @@ -1132,6 +3349,18 @@ packages: purls: [] size: 74427 timestamp: 1743431794976 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.7.1-hec049ff_0.conda + sha256: 8fbb17a56f51e7113ed511c5787e0dec0d4b10ef9df921c4fd1cccca0458f648 + md5: b1ca5f21335782f71a8bd69bdc093f67 + depends: + - __osx >=11.0 + constrains: + - expat 2.7.1.* + license: MIT + license_family: MIT + purls: [] + size: 65971 + timestamp: 1752719657566 - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.6-h2dba641_1.conda sha256: 764432d32db45466e87f10621db5b74363a9f847d2b8b1f9743746cd160f06ab md5: ede4673863426c0883c0063d853bbd85 @@ -1143,6 +3372,61 @@ packages: purls: [] size: 57433 timestamp: 1743434498161 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.4.6-h1da3d7d_1.conda + sha256: c6a530924a9b14e193ea9adfe92843de2a806d1b7dbfd341546ece9653129e60 + md5: c215a60c2935b517dcda8cad4705734d + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 39839 + timestamp: 1743434670405 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.13.3-ha770c72_1.conda + sha256: 7be9b3dac469fe3c6146ff24398b685804dfc7a1de37607b84abd076f57cc115 + md5: 51f5be229d83ecd401fb369ab96ae669 + depends: + - libfreetype6 >=2.13.3 + license: GPL-2.0-only OR FTL + purls: [] + size: 7693 + timestamp: 1745369988361 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.13.3-hce30654_1.conda + sha256: 1f8c16703fe333cdc2639f7cdaf677ac2120843453222944a7c6c85ec342903c + md5: d06282e08e55b752627a707d58779b8f + depends: + - libfreetype6 >=2.13.3 + license: GPL-2.0-only OR FTL + purls: [] + size: 7813 + timestamp: 1745370144506 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.13.3-h48d6fc4_1.conda + sha256: 7759bd5c31efe5fbc36a7a1f8ca5244c2eabdbeb8fc1bee4b99cf989f35c7d81 + md5: 3c255be50a506c50765a93a6644f32fe + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libpng >=1.6.47,<1.7.0a0 + - libzlib >=1.3.1,<2.0a0 + constrains: + - freetype >=2.13.3 + license: GPL-2.0-only OR FTL + purls: [] + size: 380134 + timestamp: 1745369987697 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.13.3-h1d14073_1.conda + sha256: c278df049b1a071841aa0aca140a338d087ea594e07dcf8a871d2cfe0e330e75 + md5: b163d446c55872ef60530231879908b9 + depends: + - __osx >=11.0 + - libpng >=1.6.47,<1.7.0a0 + - libzlib >=1.3.1,<2.0a0 + constrains: + - freetype >=2.13.3 + license: GPL-2.0-only OR FTL + purls: [] + size: 333529 + timestamp: 1745370142848 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.2.0-h767d61c_2.conda sha256: 3a572d031cb86deb541d15c1875aaa097baefc0c580b54dc61f5edab99215792 md5: ef504d1acbd74b7cc6849ef8af47dd03 @@ -1167,22 +3451,116 @@ packages: purls: [] size: 53758 timestamp: 1740240660904 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.84.0-h2ff4ddf_0.conda - sha256: 8e8737ca776d897d81a97e3de28c4bb33c45b5877bbe202b9b0ad2f61ca39397 - md5: 40cdeafb789a5513415f7bdbef053cf5 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.2.0-h69a702a_2.conda + sha256: e05263e8960da03c341650f2a3ffa4ccae4e111cb198e8933a2908125459e5a6 + md5: fb54c4ea68b460c278d26eea89cfbcc3 + depends: + - libgfortran5 14.2.0 hf1ad2bd_2 + constrains: + - libgfortran-ng ==14.2.0=*_2 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 53733 + timestamp: 1740240690977 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.1.0-hfdf1602_0.conda + sha256: 9620b4ac9d32fe7eade02081cd60d6a359a927d42bb8e121bd16489acd3c4d8c + md5: e3b7dca2c631782ca1317a994dfe19ec + depends: + - libgfortran5 15.1.0 hb74de2c_0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 133859 + timestamp: 1750183546047 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.2.0-hf1ad2bd_2.conda + sha256: c17b7cf3073a1f4e1f34d50872934fa326346e104d3c445abc1e62481ad6085c + md5: 556a4fdfac7287d349b8f09aba899693 depends: - __glibc >=2.17,<3.0.a0 - - libffi >=3.4,<4.0a0 + - libgcc >=14.2.0 + constrains: + - libgfortran 14.2.0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 1461978 + timestamp: 1740240671964 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.1.0-hb74de2c_0.conda + sha256: 44b8ce4536cc9a0e59c09ff404ef1b0120d6a91afc32799331d85268cbe42438 + md5: 8b158ccccd67a40218e12626a39065a1 + depends: + - llvm-openmp >=8.0.0 + constrains: + - libgfortran 15.1.0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 758352 + timestamp: 1750182604206 +- 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.84.1-h2ff4ddf_0.conda + sha256: 18e354d30a60441b0bf5fcbb125b6b22fd0df179620ae834e2533d44d1598211 + md5: 0305434da649d4fb48a425e588b79ea6 + depends: + - __glibc >=2.17,<3.0.a0 + - libffi >=3.4.6,<3.5.0a0 - libgcc >=13 - libiconv >=1.18,<2.0a0 - libzlib >=1.3.1,<2.0a0 - pcre2 >=10.44,<10.45.0a0 constrains: - - glib 2.84.0 *_0 + - glib 2.84.1 *_0 + license: LGPL-2.1-or-later + purls: [] + size: 3947789 + timestamp: 1743773764878 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.84.3-h587fa63_0.conda + sha256: a30510a18f0b85a036f99c744750611b5f26b972cfa70cc9f130b9f42e5bbc18 + md5: bb98995c244b6038892fd59a694a93ed + depends: + - __osx >=11.0 + - libffi >=3.4.6,<3.5.0a0 + - libiconv >=1.18,<2.0a0 + - libintl >=0.25.1,<1.0a0 + - libzlib >=1.3.1,<2.0a0 + - pcre2 >=10.45,<10.46.0a0 + constrains: + - glib 2.84.3 *_0 license: LGPL-2.1-or-later purls: [] - size: 3998765 - timestamp: 1743038881905 + size: 3661135 + timestamp: 1754315631978 +- 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/libgomp-14.2.0-h767d61c_2.conda sha256: 1a3130e0b9267e781b89399580f3163632d59fe5b0142900d63052ab1a53490e md5: 06d02030237f4d5b3d9a7e7d348fe3c6 @@ -1203,6 +3581,93 @@ packages: purls: [] size: 713084 timestamp: 1740128065462 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-h23cfdf5_2.conda + sha256: de0336e800b2af9a40bdd694b03870ac4a848161b35c8a2325704f123f185f03 + md5: 4d5a7445f0b25b6a3ddbb56e790f5251 + depends: + - __osx >=11.0 + license: LGPL-2.1-only + purls: [] + size: 750379 + timestamp: 1754909073836 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libintl-0.25.1-h493aca8_0.conda + sha256: 99d2cebcd8f84961b86784451b010f5f0a795ed1c08f1e7c76fbb3c22abf021a + md5: 5103f6a6b210a3912faf8d7db516918c + depends: + - __osx >=11.0 + - libiconv >=1.18,<2.0a0 + license: LGPL-2.1-or-later + purls: [] + size: 90957 + timestamp: 1751558394144 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.0-hb9d3cd8_0.conda + sha256: 98b399287e27768bf79d48faba8a99a2289748c65cd342ca21033fab1860d4a4 + md5: 9fa334557db9f63da6c9285fd2a48638 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + purls: [] + size: 628947 + timestamp: 1745268527144 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.1.0-h5505292_0.conda + sha256: 78df2574fa6aa5b6f5fc367c03192f8ddf8e27dc23641468d54e031ff560b9d4 + md5: 01caa4fbcaf0e6b08b3aef1151e91745 + depends: + - __osx >=11.0 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + purls: [] + size: 553624 + timestamp: 1745268405713 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-31_h7ac8fdf_openblas.conda + build_number: 31 + sha256: f583661921456e798aba10972a8abbd9d33571c655c1f66eff450edc9cbefcf3 + md5: 452b98eafe050ecff932f0ec832dd03f + depends: + - libblas 3.9.0 31_h59b9bed_openblas + constrains: + - libcblas =3.9.0=31*_openblas + - liblapacke =3.9.0=31*_openblas + - blas =2.131=openblas + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 16790 + timestamp: 1740087997375 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.9.0-34_hc9a63f6_openblas.conda + build_number: 34 + sha256: 659c7cc2d7104c5fa33482d28a6ce085fd116ff5625a117b7dd45a3521bf8efc + md5: 94b13d05122e301de02842d021eea5fb + depends: + - libblas 3.9.0 34_h10e41b3_openblas + constrains: + - libcblas 3.9.0 34*_openblas + - blas 2.134 openblas + - liblapacke 3.9.0 34*_openblas + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 19532 + timestamp: 1754678979401 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm20-20.1.4-he9d0ab4_0.conda + sha256: 56a375dc36df1a4e2061e30ebbacbc9599a11277422a9a3145fd228f772bab53 + md5: 96c33bbd084ef2b2463503fb7f1482ae + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - libxml2 >=2.13.7,<2.14.0a0 + - 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: 43004201 + timestamp: 1746052658083 - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.6.4-hb9d3cd8_0.conda sha256: cad52e10319ca4585bc37f0bc7cce99ec7c15dc9168e42ccb96b741b0a27db3f md5: 42d5b6a0f30d3c10cd88cb8584fda1cb @@ -1211,8 +3676,19 @@ packages: - libgcc >=13 license: 0BSD purls: [] - size: 111357 - timestamp: 1738525339684 + size: 111357 + timestamp: 1738525339684 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.conda + sha256: 0cb92a9e026e7bd4842f410a5c5c665c89b2eb97794ffddba519a626b8ce7285 + md5: d6df911d4564d77c4374b02552cb17d1 + depends: + - __osx >=11.0 + constrains: + - xz 5.8.1.* + license: 0BSD + purls: [] + size: 92286 + timestamp: 1749230283517 - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.64.0-h161d5f1_0.conda sha256: b0f2b3695b13a989f75d8fd7f4778e1c7aabe3b36db83f0fe80b2cd812c0e975 md5: 19e57602824042dfd0446292ef90488b @@ -1230,6 +3706,22 @@ packages: purls: [] size: 647599 timestamp: 1729571887612 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.64.0-h6d7220d_0.conda + sha256: 00cc685824f39f51be5233b54e19f45abd60de5d8847f1a56906f8936648b72f + md5: 3408c02539cee5f1141f9f11450b6a51 + depends: + - __osx >=11.0 + - c-ares >=1.34.2,<2.0a0 + - libcxx >=17 + - libev >=4.33,<4.34.0a0 + - libev >=4.33,<5.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 566719 + timestamp: 1729572385640 - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda sha256: 26d77a3bb4dceeedc2a41bd688564fe71bf2d149fdcf117049970bc02ff1add6 md5: 30fd6e37fe21f86f4bd26d6ee73eeec7 @@ -1240,6 +3732,102 @@ packages: purls: [] size: 33408 timestamp: 1697359010159 +- 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/linux-64/libopenblas-0.3.29-pthreads_h94d23a6_0.conda + sha256: cc5389ea254f111ef17a53df75e8e5209ef2ea6117e3f8aced88b5a8e51f11c4 + md5: 0a4d0252248ef9a0f88f2ba8b8a08e12 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libgfortran + - libgfortran5 >=14.2.0 + constrains: + - openblas >=0.3.29,<0.3.30.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 5919288 + timestamp: 1739825731827 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.30-openmp_h60d53f8_1.conda + sha256: dfa2e506dcbd2b8e5656333021dbd422d2c1655dcfecbd7a50cac9d223c802b4 + md5: 165b15df4e15aba3a2b63897d6e4c539 + 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: 4282228 + timestamp: 1753404509306 +- 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.50-h421ea60_1.conda + sha256: e75a2723000ce3a4b9fd9b9b9ce77553556c93e475a4657db6ed01abc02ea347 + md5: 7af8e91b0deb5f8e25d1a595dea79614 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libzlib >=1.3.1,<2.0a0 + license: zlib-acknowledgement + purls: [] + size: 317390 + timestamp: 1753879899951 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.50-h280e0eb_1.conda + sha256: a2e0240fb0c79668047b528976872307ea80cb330baf8bf6624ac2c6443449df + md5: 4d0f5ce02033286551a32208a5519884 + depends: + - __osx >=11.0 + - libzlib >=1.3.1,<2.0a0 + license: zlib-acknowledgement + purls: [] + size: 287056 + timestamp: 1753879907258 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-17.4-h27ae623_1.conda + sha256: ba2fd74be9d8c38489b9c6c18fa2fa87437dac76dfe285f86425c1b815e59fa2 + md5: 37fba334855ef3b51549308e61ed7a3d + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=75.1,<76.0a0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - openldap >=2.6.9,<2.7.0a0 + - openssl >=3.4.1,<4.0a0 + license: PostgreSQL + purls: [] + size: 2736307 + timestamp: 1743504522214 - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.49.1-hee588c1_2.conda sha256: a086289bf75c33adc1daed3f1422024504ffb5c3c8b3285c49f025c29708ed16 md5: 962d6ac93c30b1dfc54c9cccafd1003e @@ -1251,6 +3839,17 @@ packages: purls: [] size: 918664 timestamp: 1742083674731 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.50.4-h4237e3c_0.conda + sha256: 802ebe62e6bc59fc26b26276b793e0542cfff2d03c086440aeaf72fb8bbcec44 + md5: 1dcb0468f5146e38fae99aef9656034b + depends: + - __osx >=11.0 + - icu >=75.1,<76.0a0 + - libzlib >=1.3.1,<2.0a0 + license: blessing + purls: [] + size: 902645 + timestamp: 1753948599139 - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hf672d98_0.conda sha256: 0407ac9fda2bb67e11e357066eff144c845801d00b5f664efbc48813af1e7bb9 md5: be2de152d8073ef1c01b7728475f2fe7 @@ -1264,6 +3863,17 @@ packages: purls: [] size: 304278 timestamp: 1732349402869 +- 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-14.2.0-h8f9b012_2.conda sha256: 8f5bd92e4a24e1d35ba015c5252e8f818898478cb3bc50bd8b12ab54707dc4da md5: a78c856b6dc6bf4ea8daeb9beaaa3fb0 @@ -1285,6 +3895,41 @@ packages: purls: [] size: 53830 timestamp: 1740240722530 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-hd9ff511_3.conda + sha256: b224e16b88d76ea95e4af56e2bc638c603bd26a770b98d117d04541d3aafa002 + md5: 0ea6510969e1296cc19966fad481f6de + depends: + - __glibc >=2.17,<3.0.a0 + - lerc >=4.0.0,<5.0a0 + - libdeflate >=1.23,<1.24.0a0 + - libgcc >=13 + - libjpeg-turbo >=3.0.0,<4.0a0 + - liblzma >=5.6.3,<6.0a0 + - libstdcxx >=13 + - libwebp-base >=1.4.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: HPND + purls: [] + size: 428173 + timestamp: 1734398813264 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.0-h025e3ab_6.conda + sha256: d6ed4b307dde5d66b73aa3f155b3ed40ba9394947cfe148e2cd07605ef4b410b + md5: d0862034c2c563ef1f52a3237c133d8d + depends: + - __osx >=11.0 + - lerc >=4.0.0,<5.0a0 + - libcxx >=19 + - libdeflate >=1.24,<1.25.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: 372136 + timestamp: 1755012109767 - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda sha256: 787eb542f055a2b3de553614b25f09eefb0a0931b0c87dbcce6efdfd92f04f18 md5: 40b61aab5c7ba9ff276c41cfffe6b80b @@ -1306,6 +3951,68 @@ packages: purls: [] size: 891272 timestamp: 1737016632446 +- 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/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 @@ -1315,6 +4022,48 @@ packages: purls: [] size: 100393 timestamp: 1702724383534 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.9.2-h65c71a3_0.conda + sha256: 49bbeb112b3242f49e4bb1ac8af2d08c447bf3929b475915d67f02628643ed55 + md5: d045b1d878031eb497cab44e6392b1df + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - libxcb >=1.17.0,<2.0a0 + - libxml2 >=2.13.7,<2.14.0a0 + - xkeyboard-config + - xorg-libxau >=1.0.12,<2.0a0 + license: MIT/X11 Derivative + license_family: MIT + purls: [] + size: 675947 + timestamp: 1746581272970 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.7-h8d12d68_0.conda + sha256: 98f0a11d6b52801daaeefd00bfb38078f439554d64d2e277d92f658faefac366 + md5: 109427e5576d0ce9c42257c2421b1680 + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=75.1,<76.0a0 + - libgcc >=13 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.6.4,<6.0a0 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 691755 + timestamp: 1743091084063 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.39-h76b75d6_0.conda + sha256: 684e9b67ef7b9ca0ca993762eeb39705ec58e2e7f958555c758da7ef416db9f3 + md5: e71f31f8cfb0a91439f2086fc8aa0461 + depends: + - libgcc-ng >=12 + - libxml2 >=2.12.1,<2.14.0a0 + license: MIT + license_family: MIT + purls: [] + size: 254297 + timestamp: 1701628814990 - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda sha256: d4bfe88d7cb447768e31650f06257995601f89076080e76df55e3112d4e47dc4 md5: edb0dca6bc32e4f4789199455a1dbeb8 @@ -1328,6 +4077,31 @@ packages: 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/osx-arm64/llvm-openmp-20.1.8-hbb9b287_1.conda + sha256: e56f46b253dd1a99cc01dde038daba7789fc6ed35b2a93e3fc44b8578a82b3ec + md5: a10bdc3e5d9e4c1ce554c83855dff6c4 + depends: + - __osx >=11.0 + constrains: + - openmp 20.1.8|20.1.8.* + - intel-openmp <0.0a0 + license: Apache-2.0 WITH LLVM-exception + license_family: APACHE + purls: [] + size: 283300 + timestamp: 1753978829840 - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda sha256: 0fbacdfb31e55964152b24d5567e9a9996e1e7902fb08eb7d91b5fd6ce60803a md5: fee3164ac23dfca50cfcc8b85ddefb81 @@ -1340,11 +4114,116 @@ packages: - pkg:pypi/markdown-it-py?source=hash-mapping size: 64430 timestamp: 1733250550053 +- conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda + sha256: 7b1da4b5c40385791dbc3cc85ceea9fad5da680a27d5d3cb8bfaa185e304a89e + md5: 5b5203189eb668f042ac2b0826244964 + depends: + - mdurl >=0.1,<1 + - python >=3.10 + license: MIT + license_family: MIT + purls: + - pkg:pypi/markdown-it-py?source=compressed-mapping + size: 64736 + timestamp: 1754951288511 +- pypi: https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl + name: markupsafe + version: 3.0.2 + sha256: 846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225 + requires_python: '>=3.9' - pypi: https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl name: markupsafe version: 3.0.2 sha256: e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8 requires_python: '>=3.9' +- conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.5-py312h7900ff3_0.conda + sha256: 34202064bc5a358ebbf561306dd259fd220ee22b14958f62d4990886f26db44a + md5: 32511cef24b61a6e955417060d3812c5 + depends: + - matplotlib-base >=3.10.5,<3.10.6.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: + - pkg:pypi/matplotlib?source=compressed-mapping + size: 17348 + timestamp: 1754005897072 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.10.5-py312h1f38498_0.conda + sha256: e75ed12886976f48e938ccd3afcc41165904589133b03e4e0f1c1ddd6ff3a071 + md5: 92933847a00ad390bc9fe99c50c73b3f + depends: + - matplotlib-base >=3.10.5,<3.10.6.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - tornado >=5 + license: PSF-2.0 + license_family: PSF + purls: + - pkg:pypi/matplotlib?source=compressed-mapping + size: 17451 + timestamp: 1754005874746 +- conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.5-py312he3d6523_0.conda + sha256: 66e94e6226fd3dd04bb89d04079e2d8e2c74d923c0bbf255e483f127aee621ff + md5: 9246288e5ef2a944f7c9c648f9f331c7 + 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.13.3 + - libfreetype6 >=2.13.3 + - 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=compressed-mapping + size: 8071030 + timestamp: 1754005868258 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.10.5-py312h05635fa_0.conda + sha256: bc44413a9f1984e6ab39bd0b805430a4e11e41e1d0389254c4d2d056be610512 + md5: 96e5de8c96b4557430f6af0d6693d4c9 + depends: + - __osx >=11.0 + - contourpy >=1.0.1 + - cycler >=0.10 + - fonttools >=4.22.0 + - freetype + - kiwisolver >=1.3.1 + - libcxx >=19 + - libfreetype >=2.13.3 + - libfreetype6 >=2.13.3 + - 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=compressed-mapping + size: 8031746 + timestamp: 1754005848626 - pypi: https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl name: matplotlib-inline version: 0.1.7 @@ -1374,6 +4253,17 @@ packages: - pkg:pypi/more-itertools?source=hash-mapping size: 58739 timestamp: 1736883940984 +- conda: https://conda.anaconda.org/conda-forge/noarch/more-itertools-10.7.0-pyhd8ed1ab_0.conda + sha256: d0c2253dcb1da6c235797b57d29de688dabc2e48cc49645b1cff2b52b7907428 + md5: 7c65a443d58beb0518c35b26c70e201d + depends: + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/more-itertools?source=hash-mapping + size: 61359 + timestamp: 1745349566387 - pypi: https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl name: mpmath version: 1.3.0 @@ -1392,6 +4282,11 @@ packages: version: 1.1.0 sha256: 17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39 requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/ab/65/7d1de38c8a22cf8b1551469159d4b6cf49be2126adc2482de50976084d78/msgpack-1.1.1-cp312-cp312-macosx_11_0_arm64.whl + name: msgpack + version: 1.1.1 + sha256: 33be9ab121df9b6b461ff91baac6f2731f83d9b27ed948c5b9d1978ae28bf157 + requires_python: '>=3.8' - pypi: https://files.pythonhosted.org/packages/9b/5d/f25ac7d4fb77cbd53ddc6d05d833c6bf52b12770a44fa9a447eed470ca9a/msgpack_numpy-0.4.8-py2.py3-none-any.whl name: msgpack-numpy version: 0.4.8 @@ -1399,6 +4294,46 @@ packages: requires_dist: - numpy>=1.9.0 - msgpack>=0.5.2 +- 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/mysql-common-9.0.1-h266115a_6.conda + sha256: 9c2e3f9e9883e4b8d7e9e6abf7b235dc00bdcd5ef66640a360464a9f5756294d + md5: 94116b69829e90b72d566e64421e1bff + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.4.1,<4.0a0 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 616215 + timestamp: 1744124836761 +- conda: https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-9.0.1-he0572af_6.conda + sha256: 274467a602944d12722f757f660ad034de6f5f5d7d2ea1b913ef6fd836c1b8ce + md5: 9802ae6d20982f42c0f5d69008988763 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - mysql-common 9.0.1 h266115a_6 + - openssl >=3.4.1,<4.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 1369369 + timestamp: 1744124916632 - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda sha256: 3fde293232fa3fca98635e1167de6b7c7fda83caf24b9d6c91ec9eefb4f4d586 md5: 47e340acb35de30501a76c7c799c41d7 @@ -1409,6 +4344,15 @@ packages: 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 - pypi: https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl name: networkx version: 3.4.2 @@ -1444,6 +4388,42 @@ packages: - pytest>=7.2 ; extra == 'test' - pytest-cov>=4.0 ; extra == 'test' requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/eb/8d/776adee7bbf76365fdd7f2552710282c79a4ead5d2a46408c9043a2b70ba/networkx-3.5-py3-none-any.whl + name: networkx + version: '3.5' + sha256: 0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec + requires_dist: + - numpy>=1.25 ; extra == 'default' + - scipy>=1.11.2 ; extra == 'default' + - matplotlib>=3.8 ; extra == 'default' + - pandas>=2.0 ; extra == 'default' + - pre-commit>=4.1 ; extra == 'developer' + - mypy>=1.15 ; extra == 'developer' + - sphinx>=8.0 ; extra == 'doc' + - pydata-sphinx-theme>=0.16 ; extra == 'doc' + - sphinx-gallery>=0.18 ; extra == 'doc' + - numpydoc>=1.8.0 ; extra == 'doc' + - pillow>=10 ; extra == 'doc' + - texext>=0.6.7 ; extra == 'doc' + - myst-nb>=1.1 ; extra == 'doc' + - intersphinx-registry ; extra == 'doc' + - osmnx>=2.0.0 ; extra == 'example' + - momepy>=0.7.2 ; extra == 'example' + - contextily>=1.6 ; extra == 'example' + - seaborn>=0.13 ; extra == 'example' + - cairocffi>=1.7 ; extra == 'example' + - igraph>=0.11 ; extra == 'example' + - scikit-learn>=1.5 ; extra == 'example' + - lxml>=4.6 ; extra == 'extra' + - pygraphviz>=1.14 ; extra == 'extra' + - pydot>=3.0.1 ; extra == 'extra' + - sympy>=1.10 ; extra == 'extra' + - pytest>=7.2 ; extra == 'test' + - pytest-cov>=4.0 ; extra == 'test' + - pytest-xdist>=3.0 ; extra == 'test' + - pytest-mpl ; extra == 'test-extras' + - pytest-randomly ; extra == 'test-extras' + requires_python: '>=3.11' - conda: https://conda.anaconda.org/conda-forge/linux-64/nh3-0.2.21-py39h77e2912_1.conda noarch: python sha256: 05b2fcbc831ea2936108ba1ebdb249d310d710c7880a98a25817510cf8a41d2a @@ -1462,11 +4442,108 @@ packages: - pkg:pypi/nh3?source=hash-mapping size: 621078 timestamp: 1741652643562 -- pypi: https://files.pythonhosted.org/packages/02/e2/e2cbb8d634151aab9528ef7b8bab52ee4ab10e076509285602c2a3a686e0/numpy-2.2.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - name: numpy - version: 2.2.4 - sha256: 4f92084defa704deadd4e0a5ab1dc52d8ac9e8a8ef617f3fbb853e79b0ea3592 - requires_python: '>=3.10' +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/nh3-0.3.0-py39h24c5d98_0.conda + noarch: python + sha256: bc135c9abf5b38a63abc082712af64942663f2e5373f535df52ba6fec7988a24 + md5: 12896e493335f2f37257ed070d8a4080 + depends: + - python + - __osx >=11.0 + - _python_abi3_support 1.* + - cpython >=3.9 + constrains: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/nh3?source=hash-mapping + size: 624089 + timestamp: 1752853325963 +- conda: https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.9.1-pyhd8ed1ab_1.conda + sha256: 3636eec0e60466a00069b47ce94b6d88b01419b6577d8e393da44bb5bc8d3468 + md5: 7ba3f09fceae6a120d664217e58fe686 + depends: + - python >=3.9 + - setuptools + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/nodeenv?source=hash-mapping + size: 34574 + timestamp: 1734112236147 +- conda: https://conda.anaconda.org/conda-forge/linux-64/nodejs-22.13.0-hf235a45_0.conda + sha256: 925ea8839d6f26d0eb4204675b98a862803a9a9657fd36a4a22c4c29a479a911 + md5: 1f9efd96347aa008bd2c735d7d88fc75 + depends: + - __glibc >=2.28,<3.0.a0 + - icu >=75.1,<76.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libuv >=1.50.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.1,<4.0a0 + - zlib + license: MIT + license_family: MIT + purls: [] + size: 21691794 + timestamp: 1741809786920 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/nodejs-24.4.1-hab9d20b_0.conda + sha256: c79d2c81f80a9adedc77362f2e8b10879ed0f9806deb6ba2464c1287a05f0b9b + md5: 463a537de602f8558604f27395b323d0 + depends: + - libcxx >=19 + - __osx >=11.0 + - openssl >=3.5.1,<4.0a0 + - libuv >=1.51.0,<2.0a0 + - icu >=75.1,<76.0a0 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 17949155 + timestamp: 1752839389217 +- conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.2-py312h33ff503_0.conda + sha256: d54e52df67e0be7e5faa9e6f0efccea3d72f635a3159cc151c4668e5159f6ef3 + md5: 3f6efbc40eb13f019c856c410fa921d2 + depends: + - python + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + - libblas >=3.9.0,<4.0a0 + - python_abi 3.12.* *_cp312 + - libcblas >=3.9.0,<4.0a0 + - liblapack >=3.9.0,<4.0a0 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/numpy?source=compressed-mapping + size: 8785045 + timestamp: 1753401550884 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.3.2-py312h2f38b44_0.conda + sha256: 581039072c18b2abd8dfcf7fe5c16a8fbb72e14821bad4817ca00dbb16f3bad3 + md5: c58a6fa1ee8edb9de10d0f5c91806193 + depends: + - python + - libcxx >=19 + - python 3.12.* *_cpython + - __osx >=11.0 + - liblapack >=3.9.0,<4.0a0 + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/numpy?source=hash-mapping + size: 6657726 + timestamp: 1753401542508 - pypi: https://files.pythonhosted.org/packages/ae/71/1c91302526c45ab494c23f61c7a84aa568b8c1f9d196efa5993957faf906/nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_x86_64.whl name: nvidia-cublas-cu12 version: 12.4.5.8 @@ -1539,6 +4616,50 @@ packages: version: 12.4.127 sha256: 781e950d9b9f60d8241ccea575b32f5105a5baf4c2351cab5256a24869f12a1a requires_python: '>=3' +- conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.3-h55fea9a_1.conda + sha256: 0b7396dacf988f0b859798711b26b6bc9c6161dca21bacfd778473da58730afa + md5: 01243c4aaf71bde0297966125aea4706 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libpng >=1.6.50,<1.7.0a0 + - libstdcxx >=14 + - libtiff >=4.7.0,<4.8.0a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 357828 + timestamp: 1754297886899 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.3-h889cd5d_1.conda + sha256: 6013916893fcd9bc97c479279cfe4616de7735ec566bad0ee41bc729e14d31b2 + md5: ab581998c77c512d455a13befcddaac3 + depends: + - __osx >=11.0 + - libcxx >=19 + - libpng >=1.6.50,<1.7.0a0 + - libtiff >=4.7.0,<4.8.0a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 320198 + timestamp: 1754297986425 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.9-he970967_0.conda + sha256: 224f458848f792fe9e3587ee6b626d4eaad63aead0e5e6c25cbe29aba7b05c53 + md5: ca2de8bbdc871bce41dbf59e51324165 + depends: + - __glibc >=2.17,<3.0.a0 + - cyrus-sasl >=2.1.27,<3.0a0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.4.0,<4.0a0 + license: OLDAP-2.8 + license_family: BSD + purls: [] + size: 784483 + timestamp: 1732674189726 - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.4.1-h7b32b05_0.conda sha256: cbf62df3c79a5c2d113247ddea5658e9ff3697b6e741c210656e239ecaf1768f md5: 41adf927e746dc75ecf0ef841c454e48 @@ -1551,6 +4672,17 @@ packages: purls: [] size: 2939306 timestamp: 1739301879343 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.5.2-he92f556_0.conda + sha256: f6d1c87dbcf7b39fad24347570166dade1c533ae2d53c60a70fa4dc874ef0056 + md5: bcb0d87dfbc199d0a461d2c7ca30b3d8 + depends: + - __osx >=11.0 + - ca-certificates + license: Apache-2.0 + license_family: Apache + purls: [] + size: 3074848 + timestamp: 1754465710470 - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda sha256: da157b19bcd398b9804c5c52fc000fcb8ab0525bdb9c70f95beaa0bb42f85af1 md5: 3bfed7e6228ebf2f7b9eaa47f1b4e2aa @@ -1562,97 +4694,122 @@ packages: - pkg:pypi/packaging?source=hash-mapping size: 60164 timestamp: 1733203368787 -- pypi: https://files.pythonhosted.org/packages/38/f8/d8fddee9ed0d0c0f4a2132c1dfcf0e3e53265055da8df952a53e7eaf178c/pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - name: pandas - version: 2.2.3 - sha256: fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319 - requires_dist: - - numpy>=1.22.4 ; python_full_version < '3.11' - - numpy>=1.23.2 ; python_full_version == '3.11.*' - - numpy>=1.26.0 ; python_full_version >= '3.12' - - python-dateutil>=2.8.2 - - pytz>=2020.1 - - tzdata>=2022.7 - - hypothesis>=6.46.1 ; extra == 'test' - - pytest>=7.3.2 ; extra == 'test' - - pytest-xdist>=2.2.0 ; extra == 'test' - - pyarrow>=10.0.1 ; extra == 'pyarrow' - - bottleneck>=1.3.6 ; extra == 'performance' - - numba>=0.56.4 ; extra == 'performance' - - numexpr>=2.8.4 ; extra == 'performance' - - scipy>=1.10.0 ; extra == 'computation' - - xarray>=2022.12.0 ; extra == 'computation' - - fsspec>=2022.11.0 ; extra == 'fss' - - s3fs>=2022.11.0 ; extra == 'aws' - - gcsfs>=2022.11.0 ; extra == 'gcp' - - pandas-gbq>=0.19.0 ; extra == 'gcp' - - odfpy>=1.4.1 ; extra == 'excel' - - openpyxl>=3.1.0 ; extra == 'excel' - - python-calamine>=0.1.7 ; extra == 'excel' - - pyxlsb>=1.0.10 ; extra == 'excel' - - xlrd>=2.0.1 ; extra == 'excel' - - xlsxwriter>=3.0.5 ; extra == 'excel' - - pyarrow>=10.0.1 ; extra == 'parquet' - - pyarrow>=10.0.1 ; extra == 'feather' - - tables>=3.8.0 ; extra == 'hdf5' - - pyreadstat>=1.2.0 ; extra == 'spss' - - sqlalchemy>=2.0.0 ; extra == 'postgresql' - - psycopg2>=2.9.6 ; extra == 'postgresql' - - adbc-driver-postgresql>=0.8.0 ; extra == 'postgresql' - - sqlalchemy>=2.0.0 ; extra == 'mysql' - - pymysql>=1.0.2 ; extra == 'mysql' - - sqlalchemy>=2.0.0 ; extra == 'sql-other' - - adbc-driver-postgresql>=0.8.0 ; extra == 'sql-other' - - adbc-driver-sqlite>=0.8.0 ; extra == 'sql-other' - - beautifulsoup4>=4.11.2 ; extra == 'html' - - html5lib>=1.1 ; extra == 'html' - - lxml>=4.9.2 ; extra == 'html' - - lxml>=4.9.2 ; extra == 'xml' - - matplotlib>=3.6.3 ; extra == 'plot' - - jinja2>=3.1.2 ; extra == 'output-formatting' - - tabulate>=0.9.0 ; extra == 'output-formatting' - - pyqt5>=5.15.9 ; extra == 'clipboard' - - qtpy>=2.3.0 ; extra == 'clipboard' - - zstandard>=0.19.0 ; extra == 'compression' - - dataframe-api-compat>=0.1.7 ; extra == 'consortium-standard' - - adbc-driver-postgresql>=0.8.0 ; extra == 'all' - - adbc-driver-sqlite>=0.8.0 ; extra == 'all' - - beautifulsoup4>=4.11.2 ; extra == 'all' - - bottleneck>=1.3.6 ; extra == 'all' - - dataframe-api-compat>=0.1.7 ; extra == 'all' - - fastparquet>=2022.12.0 ; extra == 'all' - - fsspec>=2022.11.0 ; extra == 'all' - - gcsfs>=2022.11.0 ; extra == 'all' - - html5lib>=1.1 ; extra == 'all' - - hypothesis>=6.46.1 ; extra == 'all' - - jinja2>=3.1.2 ; extra == 'all' - - lxml>=4.9.2 ; extra == 'all' - - matplotlib>=3.6.3 ; extra == 'all' - - numba>=0.56.4 ; extra == 'all' - - numexpr>=2.8.4 ; extra == 'all' - - odfpy>=1.4.1 ; extra == 'all' - - openpyxl>=3.1.0 ; extra == 'all' - - pandas-gbq>=0.19.0 ; extra == 'all' - - psycopg2>=2.9.6 ; extra == 'all' - - pyarrow>=10.0.1 ; extra == 'all' - - pymysql>=1.0.2 ; extra == 'all' - - pyqt5>=5.15.9 ; extra == 'all' - - pyreadstat>=1.2.0 ; extra == 'all' - - pytest>=7.3.2 ; extra == 'all' - - pytest-xdist>=2.2.0 ; extra == 'all' - - python-calamine>=0.1.7 ; extra == 'all' - - pyxlsb>=1.0.10 ; extra == 'all' - - qtpy>=2.3.0 ; extra == 'all' - - scipy>=1.10.0 ; extra == 'all' - - s3fs>=2022.11.0 ; extra == 'all' - - sqlalchemy>=2.0.0 ; extra == 'all' - - tables>=3.8.0 ; extra == 'all' - - tabulate>=0.9.0 ; extra == 'all' - - xarray>=2022.12.0 ; extra == 'all' - - xlrd>=2.0.1 ; extra == 'all' - - xlsxwriter>=3.0.5 ; extra == 'all' - - zstandard>=0.19.0 ; extra == 'all' - requires_python: '>=3.9' +- conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda + sha256: 289861ed0c13a15d7bbb408796af4de72c2fe67e2bcb0de98f4c3fce259d7991 + md5: 58335b26c38bf4a20f399384c33cbcf9 + depends: + - python >=3.8 + - python + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/packaging?source=hash-mapping + size: 62477 + timestamp: 1745345660407 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.3.1-py312hf79963d_0.conda + sha256: 6ec86b1da8432059707114270b9a45d767dac97c4910ba82b1f4fa6f74e077c8 + md5: 7c73e62e62e5864b8418440e2a2cc246 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - numpy >=1.22.4 + - numpy >=1.23,<3 + - python >=3.12,<3.13.0a0 + - python-dateutil >=2.8.2 + - python-tzdata >=2022.7 + - python_abi 3.12.* *_cp312 + - pytz >=2020.1 + constrains: + - html5lib >=1.1 + - fastparquet >=2022.12.0 + - xarray >=2022.12.0 + - pyqt5 >=5.15.9 + - pyxlsb >=1.0.10 + - matplotlib >=3.6.3 + - numba >=0.56.4 + - odfpy >=1.4.1 + - bottleneck >=1.3.6 + - tabulate >=0.9.0 + - scipy >=1.10.0 + - pyreadstat >=1.2.0 + - pandas-gbq >=0.19.0 + - openpyxl >=3.1.0 + - xlrd >=2.0.1 + - pyarrow >=10.0.1 + - xlsxwriter >=3.0.5 + - python-calamine >=0.1.7 + - gcsfs >=2022.11.0 + - zstandard >=0.19.0 + - fsspec >=2022.11.0 + - lxml >=4.9.2 + - s3fs >=2022.11.0 + - numexpr >=2.8.4 + - psycopg2 >=2.9.6 + - qtpy >=2.3.0 + - pytables >=3.8.0 + - tzdata >=2022.7 + - sqlalchemy >=2.0.0 + - beautifulsoup4 >=4.11.2 + - blosc >=1.21.3 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/pandas?source=hash-mapping + size: 15092371 + timestamp: 1752082221274 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-2.3.1-py312h98f7732_0.conda + sha256: f4f98436dde01309935102de2ded045bb5500b42fb30a3bf8751b15affee4242 + md5: d3775e9b27579a0e96150ce28a2542bd + depends: + - __osx >=11.0 + - libcxx >=19 + - numpy >=1.22.4 + - numpy >=1.23,<3 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python-dateutil >=2.8.2 + - python-tzdata >=2022.7 + - python_abi 3.12.* *_cp312 + - pytz >=2020.1 + constrains: + - openpyxl >=3.1.0 + - pyarrow >=10.0.1 + - s3fs >=2022.11.0 + - zstandard >=0.19.0 + - psycopg2 >=2.9.6 + - fastparquet >=2022.12.0 + - fsspec >=2022.11.0 + - qtpy >=2.3.0 + - blosc >=1.21.3 + - xlsxwriter >=3.0.5 + - xarray >=2022.12.0 + - python-calamine >=0.1.7 + - tabulate >=0.9.0 + - odfpy >=1.4.1 + - numexpr >=2.8.4 + - tzdata >=2022.7 + - scipy >=1.10.0 + - pyreadstat >=1.2.0 + - beautifulsoup4 >=4.11.2 + - numba >=0.56.4 + - pyqt5 >=5.15.9 + - pytables >=3.8.0 + - lxml >=4.9.2 + - xlrd >=2.0.1 + - matplotlib >=3.6.3 + - bottleneck >=1.3.6 + - pandas-gbq >=0.19.0 + - html5lib >=1.1 + - pyxlsb >=1.0.10 + - sqlalchemy >=2.0.0 + - gcsfs >=2022.11.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/pandas?source=hash-mapping + size: 13991815 + timestamp: 1752082557265 - pypi: https://files.pythonhosted.org/packages/c6/ac/dac4a63f978e4dcb3c6d3a78c4d8e0192a113d288502a1216950c41b1027/parso-0.8.4-py2.py3-none-any.whl name: parso version: 0.8.4 @@ -1664,6 +4821,18 @@ packages: - docopt ; extra == 'testing' - pytest ; extra == 'testing' requires_python: '>=3.6' +- conda: https://conda.anaconda.org/conda-forge/noarch/patsy-1.0.1-pyhd8ed1ab_1.conda + sha256: ab52916f056b435757d46d4ce0a93fd73af47df9c11fd72b74cc4b7e1caca563 + md5: ee23fabfd0a8c6b8d6f3729b47b2859d + depends: + - numpy >=1.4.0 + - python >=3.9 + license: BSD-2-Clause AND PSF-2.0 + license_family: BSD + purls: + - pkg:pypi/patsy?source=hash-mapping + size: 186594 + timestamp: 1733792482894 - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.44-hba22ea6_2.conda sha256: 1087716b399dab91cc9511d6499036ccdc53eb29a288bebcb19cf465c51d7c0d md5: df359c09c41cd186fffb93a2d87aa6f5 @@ -1677,39 +4846,70 @@ packages: purls: [] size: 952308 timestamp: 1723488734144 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.45-ha881caa_0.conda + sha256: e9ecb706b58b5a2047c077b3a1470e8554f3aad02e9c3c00cfa35d537420fea3 + md5: a52385b93558d8e6bbaeec5d61a21cd7 + depends: + - __osx >=11.0 + - bzip2 >=1.0.8,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 837826 + timestamp: 1745955207242 - pypi: https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl name: pexpect version: 4.9.0 sha256: 7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523 requires_dist: - ptyprocess>=0.5 -- pypi: https://files.pythonhosted.org/packages/38/0d/84200ed6a871ce386ddc82904bfadc0c6b28b0c0ec78176871a4679e40b3/pillow-11.1.0-cp312-cp312-manylinux_2_28_x86_64.whl - name: pillow - version: 11.1.0 - sha256: 9aa9aeddeed452b2f616ff5507459e7bab436916ccb10961c4a382cd3e03f47f - requires_dist: - - furo ; extra == 'docs' - - olefile ; extra == 'docs' - - sphinx>=8.1 ; extra == 'docs' - - sphinx-copybutton ; extra == 'docs' - - sphinx-inline-tabs ; extra == 'docs' - - sphinxext-opengraph ; extra == 'docs' - - olefile ; extra == 'fpx' - - olefile ; extra == 'mic' - - check-manifest ; extra == 'tests' - - coverage>=7.4.2 ; extra == 'tests' - - defusedxml ; extra == 'tests' - - markdown2 ; extra == 'tests' - - olefile ; extra == 'tests' - - packaging ; extra == 'tests' - - pyroma ; extra == 'tests' - - pytest ; extra == 'tests' - - pytest-cov ; extra == 'tests' - - pytest-timeout ; extra == 'tests' - - trove-classifiers>=2024.10.12 ; extra == 'tests' - - typing-extensions ; python_full_version < '3.10' and extra == 'typing' - - defusedxml ; extra == 'xmp' - requires_python: '>=3.9' +- conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-11.3.0-py312h80c1187_0.conda + sha256: 7c9a8f65a200587bf7a0135ca476f9c472348177338ed8b825ddcc08773fde68 + md5: 7911e727a6c24db662193a960b81b6b2 + depends: + - __glibc >=2.17,<3.0.a0 + - lcms2 >=2.17,<3.0a0 + - libfreetype >=2.13.3 + - libfreetype6 >=2.13.3 + - libgcc >=13 + - libjpeg-turbo >=3.1.0,<4.0a0 + - libtiff >=4.7.0,<4.8.0a0 + - libwebp-base >=1.5.0,<2.0a0 + - libxcb >=1.17.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openjpeg >=2.5.3,<3.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - tk >=8.6.13,<8.7.0a0 + license: HPND + purls: + - pkg:pypi/pillow?source=hash-mapping + size: 42964111 + timestamp: 1751482158083 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-11.3.0-py312h50aef2c_0.conda + sha256: 3d60288e8cfd42e4548c9e5192a285e73f81df2869f69b9d3905849b45d9bd2a + md5: dddff48655b5cd24a5170a6df979943a + depends: + - __osx >=11.0 + - lcms2 >=2.17,<3.0a0 + - libfreetype >=2.13.3 + - libfreetype6 >=2.13.3 + - libjpeg-turbo >=3.1.0,<4.0a0 + - libtiff >=4.7.0,<4.8.0a0 + - libwebp-base >=1.5.0,<2.0a0 + - libxcb >=1.17.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openjpeg >=2.5.3,<3.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - tk >=8.6.13,<8.7.0a0 + license: HPND + purls: + - pkg:pypi/pillow?source=hash-mapping + size: 42514714 + timestamp: 1751482419501 - conda: https://conda.anaconda.org/conda-forge/noarch/pip-25.0.1-pyh8b19718_0.conda sha256: 585940f09d87787f79f73ff5dff8eb2af8a67e5bec5eebf2f553cd26c840ba69 md5: 79b5c1440aedc5010f687048d9103628 @@ -1723,6 +4923,31 @@ packages: - pkg:pypi/pip?source=hash-mapping size: 1256460 timestamp: 1739142857253 +- conda: https://conda.anaconda.org/conda-forge/noarch/pip-25.2-pyh8b19718_0.conda + sha256: ec9ed3cef137679f3e3a68e286c6efd52144684e1be0b05004d9699882dadcdd + md5: dfce4b2af4bfe90cdcaf56ca0b28ddf5 + depends: + - python >=3.9,<3.13.0a0 + - setuptools + - wheel + license: MIT + license_family: MIT + purls: + - pkg:pypi/pip?source=compressed-mapping + size: 1177168 + timestamp: 1753924973872 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h537e5f6_0.conda + sha256: f1a4bed536f8860b4e67fcd17662884dfa364e515c195c6d2e41dbf70f19263b + md5: b0674781beef9e302a17c330213ec41a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + purls: [] + size: 410140 + timestamp: 1753105399719 - conda: https://conda.anaconda.org/conda-forge/linux-64/pkg-config-0.29.2-h4bc722e_1009.conda sha256: c9601efb1af5391317e04eca77c6fe4d716bf1ca1ad8da2a05d15cb7c28d7d4e md5: 1bee70681f504ea424fb07cdb090c001 @@ -1734,6 +4959,73 @@ packages: purls: [] size: 115175 timestamp: 1720805894943 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pkg-config-0.29.2-hde07d2e_1009.conda + sha256: d82f4655b2d67fe12eefe1a3eea4cd27d33fa41dbc5e9aeab5fd6d3d2c26f18a + md5: b4f41e19a8c20184eec3aaf0f0953293 + depends: + - __osx >=11.0 + - libglib >=2.80.3,<3.0a0 + - libiconv >=1.17,<2.0a0 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 49724 + timestamp: 1720806128118 +- conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.8-pyhe01879c_0.conda + sha256: 0f48999a28019c329cd3f6fd2f01f09fc32cc832f7d6bbe38087ddac858feaa3 + md5: 424844562f5d337077b445ec6b1398a7 + depends: + - python >=3.9 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/platformdirs?source=hash-mapping + size: 23531 + timestamp: 1746710438805 +- conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhd8ed1ab_0.conda + sha256: a8eb555eef5063bbb7ba06a379fa7ea714f57d9741fe0efdb9442dbbc2cccbcc + md5: 7da7ccd349dbf6487a7778579d2bb971 + depends: + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pluggy?source=hash-mapping + size: 24246 + timestamp: 1747339794916 +- conda: https://conda.anaconda.org/conda-forge/noarch/pre-commit-4.2.0-pyha770c72_0.conda + sha256: d0bd8cce5f31ae940934feedec107480c00f67e881bf7db9d50c6fc0216a2ee0 + md5: 17e487cc8b5507cd3abc09398cf27949 + depends: + - cfgv >=2.0.0 + - identify >=1.0.0 + - nodeenv >=0.11.1 + - python >=3.9 + - pyyaml >=5.1 + - virtualenv >=20.10.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pre-commit?source=hash-mapping + size: 195854 + timestamp: 1742475656293 +- conda: https://conda.anaconda.org/conda-forge/noarch/pre-commit-4.3.0-pyha770c72_0.conda + sha256: 66b6d429ab2201abaa7282af06b17f7631dcaafbc5aff112922b48544514b80a + md5: bc6c44af2a9e6067dd7e949ef10cdfba + depends: + - cfgv >=2.0.0 + - identify >=1.0.0 + - nodeenv >=0.11.1 + - python >=3.9 + - pyyaml >=5.1 + - virtualenv >=20.10.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pre-commit?source=hash-mapping + size: 195839 + timestamp: 1754831350570 - pypi: https://files.pythonhosted.org/packages/e4/ea/d836f008d33151c7a1f62caf3d8dd782e4d15f6a43897f64480c2b8de2ad/prompt_toolkit-3.0.50-py3-none-any.whl name: prompt-toolkit version: 3.0.50 @@ -1741,6 +5033,34 @@ packages: requires_dist: - wcwidth requires_python: '>=3.8.0' +- pypi: https://files.pythonhosted.org/packages/ce/4f/5249960887b1fbe561d9ff265496d170b55a735b76724f10ef19f9e40716/prompt_toolkit-3.0.51-py3-none-any.whl + name: prompt-toolkit + version: 3.0.51 + sha256: 52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07 + requires_dist: + - wcwidth + requires_python: '>=3.8' +- 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 - pypi: https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl name: ptyprocess version: 0.7.0 @@ -1751,6 +5071,12 @@ packages: sha256: 1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0 requires_dist: - pytest ; extra == 'tests' +- pypi: https://files.pythonhosted.org/packages/a3/17/c476487ba903c7d793db633b4e8ca4a420ae272890302189d9402ba8ff85/py3dmol-2.5.2-py2.py3-none-any.whl + name: py3dmol + version: 2.5.2 + sha256: b921940ff046cf7ca008a249cbd5debec561dcf337f1e5f3df7ac5d4a1954e8e + requires_dist: + - ipython ; extra == 'ipython' - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda sha256: 79db7928d13fab2d892592223d7570f5061c192f27b9febd1a418427b719acc6 md5: 12c566707c80111f9799308d9e265aef @@ -1759,9 +5085,19 @@ packages: - python license: BSD-3-Clause license_family: BSD - purls: [] + purls: + - pkg:pypi/pycparser?source=hash-mapping size: 110100 timestamp: 1733195786147 +- pypi: https://files.pythonhosted.org/packages/00/78/9cbcc1c073b9d4918e925af1a059762265dc65004e020511b2a06fbfd020/pydssp-0.9.1-py3-none-any.whl + name: pydssp + version: 0.9.1 + sha256: 74fb8129c07c1625bb687b80f7e94ae7ebf1277725258d7fc75fc1f3d12a67dc + requires_dist: + - numpy + - torch + - einops + - tqdm - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.1-pyhd8ed1ab_0.conda sha256: 28a3e3161390a9d23bc02b4419448f8d27679d9e2c250e29849e37749c8de86b md5: 232fb4577b6687b2d503ef8e254270c9 @@ -1773,6 +5109,91 @@ packages: - pkg:pypi/pygments?source=hash-mapping size: 888600 timestamp: 1736243563082 +- conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda + sha256: 5577623b9f6685ece2697c6eb7511b4c9ac5fb607c9babc2646c811b428fd46a + md5: 6b6ece66ebcae2d5f326c77ef2c5a066 + depends: + - python >=3.9 + license: BSD-2-Clause + license_family: BSD + purls: + - pkg:pypi/pygments?source=hash-mapping + size: 889287 + timestamp: 1750615908735 +- pypi: https://files.pythonhosted.org/packages/ec/cd/bd196b2cf014afb1009de8b0f05ecd54011d881944e62763f3c1b1e8ef37/pygtrie-2.5.0-py3-none-any.whl + name: pygtrie + version: 2.5.0 + sha256: 8795cda8105493d5ae159a5bef313ff13156c5d4d72feddefacaad59f8c8ce16 +- conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.3-pyhe01879c_2.conda + sha256: afe32182b1090911b64ac0f29eb47e03a015d142833d8a917defd65d91c99b74 + md5: aa0028616c0750c773698fdc254b2b8d + depends: + - python >=3.9 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/pyparsing?source=compressed-mapping + size: 102292 + timestamp: 1753873557076 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyright-1.1.399-py312h66e93f0_0.conda + sha256: 9857f51927cbe196ce7c0b4258504283a3492025a54dd7396282e718133eada2 + md5: 2fcde1e79f9d16a21acc7a1391c80216 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - nodeenv >=1.6.0 + - nodejs + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - typing_extensions >=4.1 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pyright?source=hash-mapping + size: 3543823 + timestamp: 1744274982054 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyright-1.1.399-py312hea69d52_0.conda + sha256: 97a022cd83aaad61b347ba2d420f40f206c92c201b612544f5a16d82542900d8 + md5: 2ec7ce1f1637de2984b9a6d0362d07ec + depends: + - __osx >=11.0 + - nodeenv >=1.6.0 + - nodejs + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - typing_extensions >=4.1 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pyright?source=hash-mapping + size: 3572220 + timestamp: 1744275049046 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.9.0-py312h91f0f75_0.conda + sha256: 4db931dccd8347140e79236378096d9a1b97b98bbd206d54cebd42491ad12535 + md5: e3a335c7530a1d0c4db621914f00f9f7 + depends: + - __glibc >=2.17,<3.0.a0 + - libclang13 >=20.1.2 + - libegl >=1.7.0,<2.0a0 + - libgcc >=13 + - libgl >=1.7.0,<2.0a0 + - libopengl >=1.7.0,<2.0a0 + - libstdcxx >=13 + - libxml2 >=2.13.7,<2.14.0a0 + - libxslt >=1.1.39,<2.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - qt6-main 6.9.0.* + - qt6-main >=6.9.0,<6.10.0a0 + license: LGPL-3.0-only + license_family: LGPL + purls: + - pkg:pypi/pyside6?source=hash-mapping + - pkg:pypi/shiboken6?source=hash-mapping + size: 10119296 + timestamp: 1743760712824 - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda sha256: ba3b032fa52709ce0d9fd388f63d330a026754587a2f461117cac9ab73d8d0d8 md5: 461219d1a5bd61342293efa2c0c90eac @@ -1785,6 +5206,55 @@ packages: - pkg:pypi/pysocks?source=hash-mapping size: 21085 timestamp: 1733217331982 +- conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.1-pyhd8ed1ab_0.conda + sha256: 93e267e4ec35353e81df707938a6527d5eb55c97bf54c3b87229b69523afb59d + md5: a49c2283f24696a7b30367b7346a0144 + depends: + - colorama >=0.4 + - exceptiongroup >=1 + - iniconfig >=1 + - packaging >=20 + - pluggy >=1.5,<2 + - pygments >=2.7.2 + - python >=3.9 + - tomli >=1 + constrains: + - pytest-faulthandler >=2 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pytest?source=hash-mapping + size: 276562 + timestamp: 1750239526127 +- conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.2.1-pyhd8ed1ab_0.conda + sha256: 3a9fc07be76bc67aef355b78816b5117bfe686e7d8c6f28b45a1f89afe104761 + md5: ce978e1b9ed8b8d49164e90a5cdc94cd + depends: + - coverage >=7.5 + - pytest >=4.6 + - python >=3.9 + - toml + license: MIT + license_family: MIT + purls: + - pkg:pypi/pytest-cov?source=hash-mapping + size: 28216 + timestamp: 1749778064293 +- conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda + sha256: b7b58a5be090883198411337b99afb6404127809c3d1c9f96e99b59f36177a96 + md5: 8375cfbda7c57fbceeda18229be10417 + depends: + - execnet >=2.1 + - pytest >=7.0.0 + - python >=3.9 + constrains: + - psutil >=3.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pytest-xdist?source=hash-mapping + size: 39300 + timestamp: 1751452761594 - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.9-h9e4cc4f_1_cpython.conda build_number: 1 sha256: 77f2073889d4c91a57bc0da73a0466d9164dbcf6191ea9c3a7be6872f784d625 @@ -1811,45 +5281,220 @@ packages: - python_abi 3.12.* *_cp312 license: Python-2.0 purls: [] - size: 31670716 - timestamp: 1741130026152 -- pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl - name: python-dateutil - version: 2.9.0.post0 - sha256: a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427 - requires_dist: - - six>=1.5 - requires_python: '>=2.7,!=3.0.*,!=3.1.*,!=3.2.*' -- conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.12.9-hd8ed1ab_1.conda - sha256: d45a5a99ec3ad65d390590905c0d79b6223468d75425d988106473056ddc35e7 - md5: a1a3aa64397603a81615400388409e10 + size: 31670716 + timestamp: 1741130026152 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.11-hc22306f_0_cpython.conda + sha256: cde8b944c2dc378a5afbc48028d0843583fd215493d5885a80f1b41de085552f + md5: 9207ebad7cfbe2a4af0702c92fd031c4 + depends: + - __osx >=11.0 + - bzip2 >=1.0.8,<2.0a0 + - libexpat >=2.7.0,<3.0a0 + - libffi >=3.4.6,<3.5.0a0 + - liblzma >=5.8.1,<6.0a0 + - libsqlite >=3.50.0,<4.0a0 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.5.0,<4.0a0 + - readline >=8.2,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + constrains: + - python_abi 3.12.* *_cp312 + license: Python-2.0 + purls: [] + size: 13009234 + timestamp: 1749048134449 +- 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-gil-3.12.11-hd8ed1ab_0.conda + sha256: b8afeaefe409d61fa4b68513b25a66bb17f3ca430d67cfea51083c7bfbe098ef + md5: 859c6bec94cd74119f12b961aba965a8 + depends: + - cpython 3.12.11.* + - python_abi * *_cp312 + license: Python-2.0 + purls: [] + size: 45836 + timestamp: 1749047798827 +- conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.12.9-hd8ed1ab_1.conda + sha256: d45a5a99ec3ad65d390590905c0d79b6223468d75425d988106473056ddc35e7 + md5: a1a3aa64397603a81615400388409e10 + depends: + - cpython 3.12.9.* + - python_abi * *_cp312 + license: Python-2.0 + purls: [] + size: 45697 + timestamp: 1741128092145 +- conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.2-pyhd8ed1ab_0.conda + sha256: e8392a8044d56ad017c08fec2b0eb10ae3d1235ac967d0aab8bd7b41c4a5eaf0 + md5: 88476ae6ebd24f39261e0854ac244f33 + depends: + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/tzdata?source=hash-mapping + size: 144160 + timestamp: 1742745254292 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.12-6_cp312.conda + build_number: 6 + sha256: 09aff7ca31d1dbee63a504dba89aefa079b7c13a50dae18e1fe40a40ea71063e + md5: 95bd67b1113859774c30418e8481f9d8 + constrains: + - python 3.12.* *_cpython + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 6872 + timestamp: 1743483197238 +- 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/noarch/pytz-2025.2-pyhd8ed1ab_0.conda + sha256: 8d2a8bf110cc1fc3df6904091dead158ba3e614d8402a83e51ed3a8aa93cdeb0 + md5: bc8e3267d44011051f2eb14d22fb0960 + depends: + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pytz?source=hash-mapping + size: 189015 + timestamp: 1742920947249 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py312h178313f_2.conda + sha256: 159cba13a93b3fe084a1eb9bda0a07afc9148147647f0d437c3c3da60980503b + md5: cf2485f39740de96e2a7f2bb18ed2fee + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - yaml >=0.2.5,<0.3.0a0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pyyaml?source=hash-mapping + size: 206903 + timestamp: 1737454910324 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.2-py312h998013c_2.conda + sha256: ad225ad24bfd60f7719709791345042c3cb32da1692e62bd463b084cf140e00d + md5: 68149ed4d4e9e1c42d2ba1f27f08ca96 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - yaml >=0.2.5,<0.3.0a0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pyyaml?source=hash-mapping + size: 192148 + timestamp: 1737454886351 +- 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: - - cpython 3.12.9.* - - python_abi * *_cp312 - license: Python-2.0 + - __osx >=11.0 + - libcxx >=16 + license: LicenseRef-Qhull purls: [] - size: 45697 - timestamp: 1741128092145 -- conda: https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.12-6_cp312.conda - build_number: 6 - sha256: 09aff7ca31d1dbee63a504dba89aefa079b7c13a50dae18e1fe40a40ea71063e - md5: 95bd67b1113859774c30418e8481f9d8 + size: 516376 + timestamp: 1720814307311 +- conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.9.0-h6441bc3_0.conda + sha256: 5f04a118f6f4124bf0f3a7b7ca2510954860c764db8c25a62bcfa91f51693073 + md5: d3df16592e15a3f833cfc4d19ae58677 + depends: + - __glibc >=2.17,<3.0.a0 + - alsa-lib >=1.2.13,<1.3.0a0 + - dbus >=1.13.6,<2.0a0 + - double-conversion >=3.3.1,<3.4.0a0 + - fontconfig >=2.15.0,<3.0a0 + - fonts-conda-ecosystem + - freetype >=2.13.3,<3.0a0 + - harfbuzz >=11.0.0,<12.0a0 + - icu >=75.1,<76.0a0 + - krb5 >=1.21.3,<1.22.0a0 + - libclang-cpp20.1 >=20.1.1,<20.2.0a0 + - libclang13 >=20.1.1 + - libcups >=2.3.3,<2.4.0a0 + - libdrm >=2.4.124,<2.5.0a0 + - libegl >=1.7.0,<2.0a0 + - libgcc >=13 + - libgl >=1.7.0,<2.0a0 + - libglib >=2.84.0,<3.0a0 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libllvm20 >=20.1.1,<20.2.0a0 + - libpng >=1.6.47,<1.7.0a0 + - libpq >=17.4,<18.0a0 + - libsqlite >=3.49.1,<4.0a0 + - libstdcxx >=13 + - libtiff >=4.7.0,<4.8.0a0 + - libwebp-base >=1.5.0,<2.0a0 + - libxcb >=1.17.0,<2.0a0 + - libxkbcommon >=1.8.1,<2.0a0 + - libxml2 >=2.13.7,<2.14.0a0 + - libzlib >=1.3.1,<2.0a0 + - mysql-libs >=9.0.1,<9.1.0a0 + - openssl >=3.4.1,<4.0a0 + - pcre2 >=10.44,<10.45.0a0 + - wayland >=1.23.1,<2.0a0 + - xcb-util >=0.4.1,<0.5.0a0 + - xcb-util-cursor >=0.1.5,<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.12,<2.0a0 + - xorg-libxcomposite >=0.4.6,<1.0a0 + - xorg-libxcursor >=1.2.3,<2.0a0 + - xorg-libxdamage >=1.1.6,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxrandr >=1.5.4,<2.0a0 + - xorg-libxtst >=1.2.5,<2.0a0 + - xorg-libxxf86vm >=1.1.6,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 constrains: - - python 3.12.* *_cpython - license: BSD-3-Clause - license_family: BSD + - qt 6.9.0 + license: LGPL-3.0-only + license_family: LGPL purls: [] - size: 6872 - timestamp: 1743483197238 -- pypi: https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl - name: pytz - version: '2025.2' - sha256: 5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00 -- pypi: https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - name: pyyaml - version: 6.0.2 - sha256: 80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476 - requires_python: '>=3.8' + size: 51884819 + timestamp: 1743632133306 - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda sha256: 2d6d0c026902561ed77cd646b5021aef2d4db22e57a5b0178dfc669231e06d2c md5: 283b96675859b20a825f8fa30f311446 @@ -1861,6 +5506,16 @@ packages: purls: [] size: 282480 timestamp: 1740379431762 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.2-h1d1bf99_2.conda + sha256: 7db04684d3904f6151eff8673270922d31da1eea7fa73254d01c437f49702e34 + md5: 63ef3f6e6d6d5c589e64f11263dc5676 + depends: + - ncurses >=6.5,<7.0a0 + license: GPL-3.0-only + license_family: GPL + purls: [] + size: 252359 + timestamp: 1740379663071 - conda: https://conda.anaconda.org/conda-forge/noarch/readme_renderer-44.0-pyhd8ed1ab_1.conda sha256: 66f3adf6aaabf977cfcc22cb65607002b1de4a22bc9fac7be6bb774bc6f85a3a md5: c58dd5d147492671866464405364c0f1 @@ -1881,6 +5536,11 @@ packages: version: 2024.11.6 sha256: 70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/cd/70/69506d53397b4bd6954061bae75677ad34deb7f6ca3ba199660d6f728ff5/regex-2025.7.34-cp312-cp312-macosx_11_0_arm64.whl + name: regex + version: 2025.7.34 + sha256: 0200a5150c4cf61e407038f4b4d5cdad13e86345dac29ff9dab3d75d905cf130 + requires_python: '>=3.9' - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda sha256: d701ca1136197aa121bbbe0e8c18db6b5c94acbd041c2b43c70e5ae104e1d8ad md5: a9b9368f3701a417eac9edbcae7cb737 @@ -1898,6 +5558,23 @@ packages: - pkg:pypi/requests?source=hash-mapping size: 58723 timestamp: 1733217126197 +- conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.4-pyhd8ed1ab_0.conda + sha256: 9866aaf7a13c6cfbe665ec7b330647a0fb10a81e6f9b8fee33642232a1920e18 + md5: f6082eae112814f1447b56a5e1f6ed05 + depends: + - certifi >=2017.4.17 + - charset-normalizer >=2,<4 + - idna >=2.5,<4 + - python >=3.9 + - urllib3 >=1.21.1,<3 + constrains: + - chardet >=3.0.2,<6 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/requests?source=hash-mapping + size: 59407 + timestamp: 1749498221996 - conda: https://conda.anaconda.org/conda-forge/noarch/requests-toolbelt-1.0.0-pyhd8ed1ab_1.conda sha256: c0b815e72bb3f08b67d60d5e02251bbb0164905b5f72942ff5b6d2a339640630 md5: 66de8645e324fda0ea6ef28c2f99a2ab @@ -1932,6 +5609,16 @@ packages: purls: [] size: 186921 timestamp: 1728886721623 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/rhash-1.4.6-h5505292_1.conda + sha256: f4957c05f4fbcd99577de8838ca4b5b1ae4b400a44be647a0159c14f85b9bfc0 + md5: 029e812c8ae4e0d4cf6ff4f7d8dc9366 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 185448 + timestamp: 1748645057503 - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.0.0-pyh29332c3_0.conda sha256: d10e2b66a557ec6296844e04686db87818b0df87d73c06388f2332fda3f7d2d5 md5: 202f08242192ce3ed8bdb439ba40c0fe @@ -1947,6 +5634,29 @@ packages: - pkg:pypi/rich?source=hash-mapping size: 200323 timestamp: 1743371105291 +- conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.1.0-pyhe01879c_0.conda + sha256: 3bda3cd6aa2ca8f266aeb8db1ec63683b4a7252d7832e8ec95788fb176d0e434 + md5: c41e49bd1f1479bed6c6300038c5466e + depends: + - markdown-it-py >=2.2.0 + - pygments >=2.13.0,<3.0.0 + - python >=3.9 + - typing_extensions >=4.0.0,<5.0.0 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/rich?source=hash-mapping + size: 201098 + timestamp: 1753436991345 +- pypi: https://files.pythonhosted.org/packages/6d/4f/d073e09df851cfa251ef7840007d04db3293a0482ce607d2b993926089be/s3transfer-0.13.1-py3-none-any.whl + name: s3transfer + version: 0.13.1 + sha256: a981aa7429be23fe6dfc13e80e4020057cbab622b08c0315288758d67cabc724 + requires_dist: + - botocore>=1.37.4,<2.0a0 + - botocore[crt]>=1.37.4,<2.0a0 ; extra == 'crt' + requires_python: '>=3.9' - pypi: https://files.pythonhosted.org/packages/a6/f8/dae3421624fcc87a89d42e1898a798bc7ff72c61f38973a65d60df8f124c/safetensors-0.5.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl name: safetensors version: 0.5.3 @@ -1986,6 +5696,48 @@ packages: - safetensors[testing] ; extra == 'all' - safetensors[all] ; extra == 'dev' requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/8c/c9/bb114c158540ee17907ec470d01980957fdaf87b4aa07914c24eba87b9c6/safetensors-0.6.2-cp38-abi3-macosx_11_0_arm64.whl + name: safetensors + version: 0.6.2 + sha256: d6675cf4b39c98dbd7d940598028f3742e0375a6b4d4277e76beb0c35f4b843b + requires_dist: + - numpy>=1.21.6 ; extra == 'numpy' + - safetensors[numpy] ; extra == 'torch' + - torch>=1.10 ; extra == 'torch' + - safetensors[numpy] ; extra == 'tensorflow' + - tensorflow>=2.11.0 ; extra == 'tensorflow' + - safetensors[numpy] ; extra == 'pinned-tf' + - tensorflow==2.18.0 ; extra == 'pinned-tf' + - safetensors[numpy] ; extra == 'jax' + - flax>=0.6.3 ; extra == 'jax' + - jax>=0.3.25 ; extra == 'jax' + - jaxlib>=0.3.25 ; extra == 'jax' + - mlx>=0.0.9 ; extra == 'mlx' + - safetensors[numpy] ; extra == 'paddlepaddle' + - paddlepaddle>=2.4.1 ; extra == 'paddlepaddle' + - ruff ; extra == 'quality' + - safetensors[numpy] ; extra == 'testing' + - h5py>=3.7.0 ; extra == 'testing' + - huggingface-hub>=0.12.1 ; extra == 'testing' + - setuptools-rust>=1.5.2 ; extra == 'testing' + - pytest>=7.2.0 ; extra == 'testing' + - pytest-benchmark>=4.0.0 ; extra == 'testing' + - hypothesis>=6.70.2 ; extra == 'testing' + - safetensors[numpy] ; extra == 'testingfree' + - huggingface-hub>=0.12.1 ; extra == 'testingfree' + - setuptools-rust>=1.5.2 ; extra == 'testingfree' + - pytest>=7.2.0 ; extra == 'testingfree' + - pytest-benchmark>=4.0.0 ; extra == 'testingfree' + - hypothesis>=6.70.2 ; extra == 'testingfree' + - safetensors[torch] ; extra == 'all' + - safetensors[numpy] ; extra == 'all' + - safetensors[pinned-tf] ; extra == 'all' + - safetensors[jax] ; extra == 'all' + - safetensors[paddlepaddle] ; extra == 'all' + - safetensors[quality] ; extra == 'all' + - safetensors[testing] ; extra == 'all' + - safetensors[all] ; extra == 'dev' + requires_python: '>=3.9' - pypi: https://files.pythonhosted.org/packages/29/7a/8bce8968883e9465de20be15542f4c7e221952441727c4dad24d534c6d99/scikit_learn-1.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl name: scikit-learn version: 1.6.1 @@ -2048,49 +5800,143 @@ packages: - pooch>=1.6.0 ; extra == 'tests' - conda-lock==2.5.6 ; extra == 'maintenance' requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/c0/53/eaada1a414c026673eb983f8b4a55fe5eb172725d33d62c1b21f63ff6ca4/scipy-1.15.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - name: scipy - version: 1.15.2 - sha256: 2b871df1fe1a3ba85d90e22742b93584f8d2b8e6124f8372ab15c71b73e428b8 +- pypi: https://files.pythonhosted.org/packages/67/4e/899317092f5efcab0e9bc929e3391341cec8fb0e816c4789686770024580/scikit_learn-1.7.1-cp312-cp312-macosx_12_0_arm64.whl + name: scikit-learn + version: 1.7.1 + sha256: 2fd8b8d35817b0d9ebf0b576f7d5ffbbabdb55536b0655a8aaae629d7ffd2e1f requires_dist: - - numpy>=1.23.5,<2.5 - - pytest ; extra == 'test' - - pytest-cov ; extra == 'test' - - pytest-timeout ; extra == 'test' - - pytest-xdist ; extra == 'test' - - asv ; extra == 'test' - - mpmath ; extra == 'test' - - gmpy2 ; extra == 'test' - - threadpoolctl ; extra == 'test' - - scikit-umfpack ; extra == 'test' - - pooch ; extra == 'test' - - hypothesis>=6.30 ; extra == 'test' - - array-api-strict>=2.0,<2.1.1 ; extra == 'test' - - cython ; extra == 'test' - - meson ; extra == 'test' - - ninja ; sys_platform != 'emscripten' and extra == 'test' - - sphinx>=5.0.0,<8.0.0 ; extra == 'doc' - - intersphinx-registry ; extra == 'doc' - - pydata-sphinx-theme>=0.15.2 ; extra == 'doc' - - sphinx-copybutton ; extra == 'doc' - - sphinx-design>=0.4.0 ; extra == 'doc' - - matplotlib>=3.5 ; extra == 'doc' - - numpydoc ; extra == 'doc' - - jupytext ; extra == 'doc' - - myst-nb ; extra == 'doc' - - pooch ; extra == 'doc' - - jupyterlite-sphinx>=0.16.5 ; extra == 'doc' - - jupyterlite-pyodide-kernel ; extra == 'doc' - - mypy==1.10.0 ; extra == 'dev' - - typing-extensions ; extra == 'dev' - - types-psutil ; extra == 'dev' - - pycodestyle ; extra == 'dev' - - ruff>=0.0.292 ; extra == 'dev' - - cython-lint>=0.12.2 ; extra == 'dev' - - rich-click ; extra == 'dev' - - doit>=0.36.0 ; extra == 'dev' - - pydevtool ; extra == 'dev' + - numpy>=1.22.0 + - scipy>=1.8.0 + - joblib>=1.2.0 + - threadpoolctl>=3.1.0 + - numpy>=1.22.0 ; extra == 'build' + - scipy>=1.8.0 ; extra == 'build' + - cython>=3.0.10 ; extra == 'build' + - meson-python>=0.17.1 ; extra == 'build' + - numpy>=1.22.0 ; extra == 'install' + - scipy>=1.8.0 ; extra == 'install' + - joblib>=1.2.0 ; extra == 'install' + - threadpoolctl>=3.1.0 ; extra == 'install' + - matplotlib>=3.5.0 ; extra == 'benchmark' + - pandas>=1.4.0 ; extra == 'benchmark' + - memory-profiler>=0.57.0 ; extra == 'benchmark' + - matplotlib>=3.5.0 ; extra == 'docs' + - scikit-image>=0.19.0 ; extra == 'docs' + - pandas>=1.4.0 ; extra == 'docs' + - seaborn>=0.9.0 ; extra == 'docs' + - memory-profiler>=0.57.0 ; extra == 'docs' + - sphinx>=7.3.7 ; extra == 'docs' + - sphinx-copybutton>=0.5.2 ; extra == 'docs' + - sphinx-gallery>=0.17.1 ; extra == 'docs' + - numpydoc>=1.2.0 ; extra == 'docs' + - pillow>=8.4.0 ; extra == 'docs' + - pooch>=1.6.0 ; extra == 'docs' + - sphinx-prompt>=1.4.0 ; extra == 'docs' + - sphinxext-opengraph>=0.9.1 ; extra == 'docs' + - plotly>=5.14.0 ; extra == 'docs' + - polars>=0.20.30 ; extra == 'docs' + - sphinx-design>=0.5.0 ; extra == 'docs' + - sphinx-design>=0.6.0 ; extra == 'docs' + - sphinxcontrib-sass>=0.3.4 ; extra == 'docs' + - pydata-sphinx-theme>=0.15.3 ; extra == 'docs' + - sphinx-remove-toctrees>=1.0.0.post1 ; extra == 'docs' + - towncrier>=24.8.0 ; extra == 'docs' + - matplotlib>=3.5.0 ; extra == 'examples' + - scikit-image>=0.19.0 ; extra == 'examples' + - pandas>=1.4.0 ; extra == 'examples' + - seaborn>=0.9.0 ; extra == 'examples' + - pooch>=1.6.0 ; extra == 'examples' + - plotly>=5.14.0 ; extra == 'examples' + - matplotlib>=3.5.0 ; extra == 'tests' + - scikit-image>=0.19.0 ; extra == 'tests' + - pandas>=1.4.0 ; extra == 'tests' + - pytest>=7.1.2 ; extra == 'tests' + - pytest-cov>=2.9.0 ; extra == 'tests' + - ruff>=0.11.7 ; extra == 'tests' + - mypy>=1.15 ; extra == 'tests' + - pyamg>=4.2.1 ; extra == 'tests' + - polars>=0.20.30 ; extra == 'tests' + - pyarrow>=12.0.0 ; extra == 'tests' + - numpydoc>=1.2.0 ; extra == 'tests' + - pooch>=1.6.0 ; extra == 'tests' + - conda-lock==3.0.1 ; extra == 'maintenance' requires_python: '>=3.10' +- conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.16.0-py312hf734454_0.conda + sha256: 8406e26bf853e699b1ea97792f63987808783ff4ab6ddeff9cf1ec0b9d1aa342 + md5: 7513ac56209d27a85ffa1582033f10a8 + depends: + - __glibc >=2.17,<3.0.a0 + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libgcc >=13 + - libgfortran + - libgfortran5 >=13.3.0 + - liblapack >=3.9.0,<4.0a0 + - libstdcxx >=13 + - numpy <2.6 + - 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=hash-mapping + size: 16847456 + timestamp: 1751148548291 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.16.1-py312h286a95b_0.conda + sha256: 2d9d0173b58010c2ee09280b7e4fa185d191380a4f042698263b4ffa2671818b + md5: 9841d229c34dbca6fd039e76cfca307b + depends: + - __osx >=11.0 + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libcxx >=19 + - libgfortran + - libgfortran5 >=14.3.0 + - libgfortran5 >=15.1.0 + - liblapack >=3.9.0,<4.0a0 + - numpy <2.6 + - 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=hash-mapping + size: 13840981 + timestamp: 1754970654942 +- conda: https://conda.anaconda.org/conda-forge/noarch/seaborn-0.13.2-hd8ed1ab_3.conda + noarch: python + sha256: ea29a69b14dd6be5cdeeaa551bf50d78cafeaf0351e271e358f9b820fcab4cb0 + md5: 62afb877ca2c2b4b6f9ecb37320085b6 + depends: + - seaborn-base 0.13.2 pyhd8ed1ab_3 + - statsmodels >=0.12 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 6876 + timestamp: 1733730113224 +- conda: https://conda.anaconda.org/conda-forge/noarch/seaborn-base-0.13.2-pyhd8ed1ab_3.conda + sha256: f209c9c18187570b85ec06283c72d64b8738f825b1b82178f194f4866877f8aa + md5: fd96da444e81f9e6fcaac38590f3dd42 + depends: + - matplotlib-base >=3.4,!=3.6.1 + - numpy >=1.20,!=1.24.0 + - pandas >=1.2 + - python >=3.9 + - scipy >=1.7 + constrains: + - seaborn =0.13.2=*_3 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/seaborn?source=hash-mapping + size: 227843 + timestamp: 1733730112409 - conda: https://conda.anaconda.org/conda-forge/linux-64/secretstorage-3.3.3-py312h7900ff3_3.conda sha256: c6d5d0bc7fb6cbfa3b8be8f2399a3c1308b3392a4e20bd1a0f29a828fda5ab20 md5: 4840da9db2808db946a0d979603c6de4 @@ -2117,11 +5963,34 @@ packages: - pkg:pypi/setuptools?source=compressed-mapping size: 777736 timestamp: 1740654030775 -- pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl - name: six - version: 1.17.0 - sha256: 4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 - requires_python: '>=2.7,!=3.0.*,!=3.1.*,!=3.2.*' +- conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda + sha256: 972560fcf9657058e3e1f97186cc94389144b46dbdf58c807ce62e83f977e863 + md5: 4de79c071274a53dcaf2a8c749d1499e + depends: + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/setuptools?source=hash-mapping + size: 748788 + timestamp: 1748804951958 +- 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 +- pypi: https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl + name: sniffio + version: 1.3.1 + sha256: 2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 + requires_python: '>=3.7' - pypi: https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl name: stack-data version: 0.6.3 @@ -2135,6 +6004,46 @@ packages: - pygments ; extra == 'tests' - littleutils ; extra == 'tests' - cython ; extra == 'tests' +- conda: https://conda.anaconda.org/conda-forge/linux-64/statsmodels-0.14.5-py312h8b63200_0.conda + sha256: 71af2d8efae963c83f9cd49f4648087d0acd41a58972a5bd7b097273b895ed54 + md5: d3588408248f78db333a5b019a4ca696 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - numpy <3,>=1.22.3 + - numpy >=1.23,<3 + - packaging >=21.3 + - pandas !=2.1.0,>=1.4 + - patsy >=0.5.6 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - scipy !=1.9.2,>=1.8 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/statsmodels?source=hash-mapping + size: 12062670 + timestamp: 1751917720541 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/statsmodels-0.14.5-py312hcde60ef_0.conda + sha256: 911a6fcfa2c04de64d03378e705a0678bf511f25aa0f5f9c8e4570ccb071b241 + md5: 79208bf3a11cedbe7dd53a6798cb88d2 + depends: + - __osx >=11.0 + - numpy <3,>=1.22.3 + - numpy >=1.23,<3 + - packaging >=21.3 + - pandas !=2.1.0,>=1.4 + - patsy >=0.5.6 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - scipy !=1.9.2,>=1.8 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/statsmodels?source=hash-mapping + size: 11703160 + timestamp: 1751918258652 - pypi: https://files.pythonhosted.org/packages/b2/fe/81695a1aa331a842b582453b605175f419fe8540355886031328089d840a/sympy-1.13.1-py3-none-any.whl name: sympy version: 1.13.1 @@ -2144,6 +6053,15 @@ packages: - pytest>=7.1.0 ; extra == 'dev' - hypothesis>=6.70.0 ; extra == 'dev' requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl + name: sympy + version: 1.14.0 + sha256: e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5 + requires_dist: + - mpmath>=1.1.0,<1.4 + - pytest>=7.1.0 ; extra == 'dev' + - hypothesis>=6.70.0 ; extra == 'dev' + requires_python: '>=3.9' - pypi: https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl name: tenacity version: 9.1.2 @@ -2171,6 +6089,17 @@ packages: purls: [] size: 3318875 timestamp: 1699202167581 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h892fb3f_2.conda + sha256: cb86c522576fa95c6db4c878849af0bccfd3264daf0cc40dd18e7f4a7bfced0e + md5: 7362396c170252e7b7b0c8fb37fe9c78 + depends: + - __osx >=11.0 + - libzlib >=1.3.1,<2.0a0 + license: TCL + license_family: BSD + purls: [] + size: 3125538 + timestamp: 1748388189063 - pypi: https://files.pythonhosted.org/packages/8a/63/38be071b0c8e06840bc6046991636bcb30c27f6bb1e670f4f4bc87cf49cc/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl name: tokenizers version: 0.21.1 @@ -2188,6 +6117,46 @@ packages: - setuptools-rust ; extra == 'docs' - tokenizers[testing] ; extra == 'dev' requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/8d/a6/28975479e35ddc751dc1ddc97b9b69bf7fcf074db31548aab37f8116674c/tokenizers-0.21.4-cp39-abi3-macosx_11_0_arm64.whl + name: tokenizers + version: 0.21.4 + sha256: 5e2f601a8e0cd5be5cc7506b20a79112370b9b3e9cb5f13f68ab11acd6ca7d60 + requires_dist: + - huggingface-hub>=0.16.4,<1.0 + - pytest ; extra == 'testing' + - requests ; extra == 'testing' + - numpy ; extra == 'testing' + - datasets ; extra == 'testing' + - black==22.3 ; extra == 'testing' + - ruff ; extra == 'testing' + - sphinx ; extra == 'docs' + - sphinx-rtd-theme ; extra == 'docs' + - setuptools-rust ; extra == 'docs' + - tokenizers[testing] ; extra == 'dev' + requires_python: '>=3.9' +- conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_1.conda + sha256: 34f3a83384ac3ac30aefd1309e69498d8a4aa0bf2d1f21c645f79b180e378938 + md5: b0dd904de08b7db706167240bf37b164 + depends: + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/toml?source=hash-mapping + size: 22132 + timestamp: 1734091907682 +- conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.2.1-pyhe01879c_2.conda + sha256: 040a5a05c487647c089ad5e05ad5aff5942830db2a4e656f1e300d73436436f1 + md5: 30a0a26c8abccf4b7991d590fe17c699 + depends: + - python >=3.9 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/tomli?source=compressed-mapping + size: 21238 + timestamp: 1753796677376 - pypi: https://files.pythonhosted.org/packages/e5/35/0c52d708144c2deb595cd22819a609f78fdd699b95ff6f0ebcd456e3c7c1/torch-2.6.0-cp312-cp312-manylinux1_x86_64.whl name: torch version: 2.6.0 @@ -2217,6 +6186,37 @@ packages: - opt-einsum>=3.3 ; extra == 'opt-einsum' - optree>=0.13.0 ; extra == 'optree' requires_python: '>=3.9.0' +- pypi: https://files.pythonhosted.org/packages/be/66/5c9a321b325aaecb92d4d1855421e3a055abd77903b7dab6575ca07796db/torch-2.8.0-cp312-none-macosx_11_0_arm64.whl + name: torch + version: 2.8.0 + sha256: 619c2869db3ada2c0105487ba21b5008defcc472d23f8b80ed91ac4a380283b0 + requires_dist: + - filelock + - typing-extensions>=4.10.0 + - setuptools ; python_full_version >= '3.12' + - sympy>=1.13.3 + - networkx + - jinja2 + - fsspec + - nvidia-cuda-nvrtc-cu12==12.8.93 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-cuda-runtime-cu12==12.8.90 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-cuda-cupti-cu12==12.8.90 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-cudnn-cu12==9.10.2.21 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-cublas-cu12==12.8.4.1 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-cufft-cu12==11.3.3.83 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-curand-cu12==10.3.9.90 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-cusolver-cu12==11.7.3.90 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-cusparse-cu12==12.5.8.93 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-cusparselt-cu12==0.7.1 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-nccl-cu12==2.27.3 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-nvtx-cu12==12.8.90 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-nvjitlink-cu12==12.8.93 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-cufile-cu12==1.13.1.3 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - triton==3.4.0 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - optree>=0.13.0 ; extra == 'optree' + - opt-einsum>=3.3 ; extra == 'opt-einsum' + - pyyaml ; extra == 'pyyaml' + requires_python: '>=3.9.0' - pypi: https://files.pythonhosted.org/packages/65/13/26c37c5433658d3f1eb30be07a4b42b29893bc42ff7cb7261ef6e474fc3c/torchtext-0.18.0-cp312-cp312-manylinux1_x86_64.whl name: torchtext version: 0.18.0 @@ -2227,17 +6227,66 @@ packages: - torch>=2.3.0 - numpy requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/8b/80/c78f88844e576c21580e64bd93de9df9bca223fef3e8195345680eb6f87f/torchtext-0.18.0-cp312-cp312-macosx_11_0_arm64.whl + name: torchtext + version: 0.18.0 + sha256: fec43696fb6fa7573e740a8175fd69681106574fd1fc840211182d941b88a2ba + requires_dist: + - tqdm + - requests + - torch>=2.3.0 + - numpy + requires_python: '>=3.8' - pypi: https://files.pythonhosted.org/packages/36/63/0722e153fd27d64d5b0af45b5c8cb0e80b35a68cf0130303bc9a8bb095c7/torchvision-0.21.0-cp312-cp312-manylinux1_x86_64.whl name: torchvision - version: 0.21.0 - sha256: b578bcad8a4083b40d34f689b19ca9f7c63e511758d806510ea03c29ac568f7b + version: 0.21.0 + sha256: b578bcad8a4083b40d34f689b19ca9f7c63e511758d806510ea03c29ac568f7b + requires_dist: + - numpy + - torch==2.6.0 + - pillow>=5.3.0,!=8.3.* + - gdown>=4.7.3 ; extra == 'gdown' + - scipy ; extra == 'scipy' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/df/1d/0ea0b34bde92a86d42620f29baa6dcbb5c2fc85990316df5cb8f7abb8ea2/torchvision-0.23.0-cp312-cp312-macosx_11_0_arm64.whl + name: torchvision + version: 0.23.0 + sha256: e0e2c04a91403e8dd3af9756c6a024a1d9c0ed9c0d592a8314ded8f4fe30d440 requires_dist: - numpy - - torch==2.6.0 + - torch==2.8.0 - pillow>=5.3.0,!=8.3.* - gdown>=4.7.3 ; extra == 'gdown' - scipy ; extra == 'scipy' requires_python: '>=3.9' +- conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.1-py312h66e93f0_0.conda + sha256: c96be4c8bca2431d7ad7379bad94ed6d4d25cd725ae345540a531d9e26e148c9 + md5: c532a6ee766bed75c4fa0c39e959d132 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/tornado?source=hash-mapping + size: 850902 + timestamp: 1748003427956 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.2-py312h163523d_0.conda + sha256: 82ceea2527ac484f5c8d7dee95033935b7fecb0b42afb2d9538f7397404aa6d8 + md5: 181a5ca410bad66be792da0e11038016 + 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: 853490 + timestamp: 1754732280524 - pypi: https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl name: tqdm version: 4.67.1 @@ -2689,11 +6738,18 @@ packages: - pkg:pypi/typing-extensions?source=hash-mapping size: 52077 timestamp: 1743201626659 -- pypi: https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl - name: tzdata - version: '2025.2' - sha256: 1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8 - requires_python: '>=2' +- conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.14.1-pyhe01879c_0.conda + sha256: 4f52390e331ea8b9019b87effaebc4f80c6466d09f68453f52d5cdc2a3e1194f + md5: e523f4f1e980ed7a4240d7e27e9ec81f + depends: + - python >=3.9 + - python + license: PSF-2.0 + license_family: PSF + purls: + - pkg:pypi/typing-extensions?source=hash-mapping + size: 51065 + timestamp: 1751643513473 - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda sha256: 5aaa366385d716557e365f0a4e9c3fca43ba196872abbbe3d56bb610d131e192 md5: 4222072737ccff51314b5ece9c7d6f5a @@ -2701,6 +6757,66 @@ packages: purls: [] size: 122968 timestamp: 1742727099393 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.0.1-py312h68727a3_5.conda + sha256: 9fb020083a7f4fee41f6ece0f4840f59739b3e249f157c8a407bb374ffb733b5 + md5: f9664ee31aed96c85b7319ab0a693341 + depends: + - __glibc >=2.17,<3.0.a0 + - cffi + - libgcc >=13 + - libstdcxx >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + purls: + - pkg:pypi/ukkonen?source=hash-mapping + size: 13904 + timestamp: 1725784191021 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ukkonen-1.0.1-py312h6142ec9_5.conda + sha256: 1e4452b4a12d8a69c237f14b876fbf0cdc456914170b49ba805779c749c31eca + md5: 2b485a809d1572cbe7f0ad9ee107e4b0 + depends: + - __osx >=11.0 + - cffi + - libcxx >=17 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + purls: + - pkg:pypi/ukkonen?source=hash-mapping + size: 13605 + timestamp: 1725784243533 +- conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-16.0.0-py312h66e93f0_0.conda + sha256: 638916105a836973593547ba5cf4891d1f2cb82d1cf14354fcef93fd5b941cdc + md5: 617f5d608ff8c28ad546e5d9671cbb95 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/unicodedata2?source=hash-mapping + size: 404401 + timestamp: 1736692621599 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-16.0.0-py312hea69d52_0.conda + sha256: c6ca9ea11eecc650df4bce4b3daa843821def6d753eeab6d81de35bb43f9d984 + md5: 9a835052506b91ea8f0d8e352cd12246 + 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=hash-mapping + size: 409745 + timestamp: 1736692768349 - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.3.0-pyhd8ed1ab_0.conda sha256: 114919ffa80c328127dab9c8e7a38f9d563c617691fb81fccb11c1e86763727e md5: 32674f8dbfb7b26410ed580dd3c10a29 @@ -2716,6 +6832,64 @@ packages: - pkg:pypi/urllib3?source=hash-mapping size: 100102 timestamp: 1734859520452 +- conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda + sha256: 4fb9789154bd666ca74e428d973df81087a697dbb987775bc3198d2215f240f8 + md5: 436c165519e140cb08d246a4472a9d6a + depends: + - brotli-python >=1.0.9 + - h2 >=4,<5 + - pysocks >=1.5.6,<2.0,!=1.5.7 + - python >=3.9 + - zstandard >=0.18.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/urllib3?source=hash-mapping + size: 101735 + timestamp: 1750271478254 +- conda: https://conda.anaconda.org/conda-forge/noarch/virtualenv-20.33.1-pyhd8ed1ab_0.conda + sha256: 7b8328d79cb7ec19929ec955fe4dbb938d35da391a74a3974c47ca2622c64b04 + md5: 3f6ee060b1462c29b3442df71939a358 + depends: + - distlib >=0.3.7,<1 + - filelock >=3.12.2,<4 + - platformdirs >=3.9.1,<5 + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/virtualenv?source=compressed-mapping + size: 4138678 + timestamp: 1754419391413 +- conda: https://conda.anaconda.org/conda-forge/noarch/virtualenv-20.34.0-pyhd8ed1ab_0.conda + sha256: 398f40090e80ec5084483bb798555d0c5be3d1bb30f8bb5e4702cd67cdb595ee + md5: 2bd6c0c96cfc4dbe9bde604a122e3e55 + depends: + - distlib >=0.3.7,<1 + - filelock >=3.12.2,<4 + - platformdirs >=3.9.1,<5 + - python >=3.9 + - typing_extensions >=4.13.2 + license: MIT + license_family: MIT + purls: + - pkg:pypi/virtualenv?source=compressed-mapping + size: 4381624 + timestamp: 1755111905876 +- conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.24.0-h3e06ad9_0.conda + sha256: ba673427dcd480cfa9bbc262fd04a9b1ad2ed59a159bd8f7e750d4c52282f34c + md5: 0f2ca7906bf166247d1d760c3422cb8a + depends: + - __glibc >=2.17,<3.0.a0 + - libexpat >=2.7.0,<3.0a0 + - libffi >=3.4.6,<3.5.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: MIT + license_family: MIT + purls: [] + size: 330474 + timestamp: 1751817998141 - pypi: https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl name: wcwidth version: 0.2.13 @@ -2733,6 +6907,326 @@ packages: - pkg:pypi/wheel?source=hash-mapping size: 62931 timestamp: 1733130309598 +- pypi: https://files.pythonhosted.org/packages/ca/51/5447876806d1088a0f8f71e16542bf350918128d0a69437df26047c8e46f/widgetsnbextension-4.0.14-py3-none-any.whl + name: widgetsnbextension + version: 4.0.14 + sha256: 4875a9eaf72fbf5079dc372a51a9f268fc38d46f767cbf85c43a36da5cb9b575 + requires_python: '>=3.7' +- 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.5-hb9d3cd8_0.conda + sha256: c7b35db96f6e32a9e5346f97adc968ef2f33948e3d7084295baebc0e33abdd5b + md5: eb44b3b6deb1cab08d72cb61686fe64c + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libxcb >=1.13 + - libxcb >=1.16,<2.0.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: 20296 + timestamp: 1726125844850 +- 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.45-hb9d3cd8_0.conda + sha256: a5d4af601f71805ec67403406e147c48d6bad7aaeae92b0622b7e2396842d3fe + md5: 397a013c2dc5145a70737871aaa87e98 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.12,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 392406 + timestamp: 1749375847832 +- 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.12-h4f16b4b_0.conda + sha256: 51909270b1a6c5474ed3978628b341b4d4472cd22610e5f22b506855a5e20f67 + md5: db038ce880f100acc74dba10302b5630 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libxcb >=1.17.0,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 835896 + timestamp: 1741901112627 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb9d3cd8_0.conda + sha256: ed10c9283974d311855ae08a16dfd7e56241fac632aec3b92e3cfe73cff31038 + md5: f6ebe2cb3f82ba6c057dde5d9debe4f7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + purls: [] + size: 14780 + timestamp: 1734229004433 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-h5505292_0.conda + sha256: f33e6f013fc36ebc200f09ddead83468544cb5c353a3b50499b07b8c34e28a8d + md5: 50901e0764b7701d8ed7343496f4f301 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 13593 + timestamp: 1734229104321 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.6-hb9d3cd8_2.conda + sha256: 753f73e990c33366a91fd42cc17a3d19bb9444b9ca5ff983605fa9e953baf57f + md5: d3c295b50f092ab525ffe3c2aa4b7413 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxfixes >=6.0.1,<7.0a0 + license: MIT + license_family: MIT + purls: [] + size: 13603 + timestamp: 1727884600744 +- 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-hb9d3cd8_0.conda + sha256: 6b250f3e59db07c2514057944a3ea2044d6a8cdde8a47b6497c254520fade1ee + md5: 8035c64cb77ed555e3f150b7b3972480 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + purls: [] + size: 19901 + timestamp: 1727794976192 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hd74edd7_0.conda + sha256: 9939a166d780700d81023546759102b33fdc2c5f11ef09f5f66c77210fd334c8 + md5: 77c447f48cab5d3a15ac224edb86a968 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 18487 + timestamp: 1727795205022 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.6-hb9d3cd8_0.conda + sha256: da5dc921c017c05f38a38bd75245017463104457b63a1ce633ed41f214159c14 + md5: febbab7d15033c913d53c7a2c102309d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 50060 + timestamp: 1727752228921 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.1-hb9d3cd8_0.conda + sha256: 2fef37e660985794617716eb915865ce157004a4d567ed35ec16514960ae9271 + md5: 4bdb303603e9821baf5fe5fdff1dc8f8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 19575 + timestamp: 1727794961233 +- 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.4-hb9d3cd8_0.conda + sha256: ac0f037e0791a620a69980914a77cb6bb40308e26db11698029d6708f5aa8e0d + md5: 2de7f99d6581a4a7adbff607b5c278ca + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxrender >=0.9.11,<0.10.0a0 + license: MIT + license_family: MIT + purls: [] + size: 29599 + timestamp: 1727794874300 +- 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.6-hb9d3cd8_0.conda + sha256: 8a4e2ee642f884e6b78c20c0892b85dd9b2a6e64a6044e903297e616be6ca35b + md5: 5efa5fa6243a622445fdfd72aee15efa + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 17819 + timestamp: 1734214575628 +- conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda + sha256: 6d9ea2f731e284e9316d95fa61869fe7bbba33df7929f82693c121022810f4ad + md5: a77f85f77be52ff59391544bfe73390a + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + license: MIT + license_family: MIT + purls: [] + size: 85189 + timestamp: 1753484064210 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h925e9cb_3.conda + sha256: b03433b13d89f5567e828ea9f1a7d5c5d697bf374c28a4168d71e9464f5dafac + md5: 78a0fe9e9c50d2c381e8ee47e3ea437d + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 83386 + timestamp: 1753484079473 - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda sha256: 567c04f124525c97a096b65769834b7acb047db24b15a56888a322bf3966c3e1 md5: 0c3cc595284c5e8f0f9900a9b228a332 @@ -2744,6 +7238,29 @@ packages: - pkg:pypi/zipp?source=hash-mapping size: 21809 timestamp: 1732827613585 +- conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda + sha256: 7560d21e1b021fd40b65bfb72f67945a3fcb83d78ad7ccf37b8b3165ec3b68ad + md5: df5e78d904988eb55042c0c97446079f + depends: + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/zipp?source=hash-mapping + size: 22963 + timestamp: 1749421737203 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-hb9d3cd8_2.conda + sha256: 5d7c0e5f0005f74112a34a7425179f4eb6e73c92f5d109e6af4ddeca407c92ab + md5: c9f075ab2f33b3bbee9e62d4ad0a6cd8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib 1.3.1 hb9d3cd8_2 + license: Zlib + license_family: Other + purls: [] + size: 92286 + timestamp: 1727963153079 - conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py312h66e93f0_1.conda sha256: b4fd6bd1cb87a183a8bbe85b4e87a1e7c51473309d0d82cd88d38fb021bcf41e md5: d28b82fcc8d1b462b595af4b15a6cdcf @@ -2759,10 +7276,29 @@ packages: - pkg:pypi/zstandard?source=hash-mapping size: 731658 timestamp: 1741853415477 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.23.0-py312hea69d52_2.conda + sha256: c499a2639c2981ac2fd33bae2d86c15d896bc7524f1c5651a7d3b088263f7810 + md5: ba0eb639914e4033e090b46f53bec31c + depends: + - __osx >=11.0 + - cffi >=1.11 + - 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/zstandard?source=hash-mapping + size: 532173 + timestamp: 1745870087418 - pypi: https://files.pythonhosted.org/packages/92/a7/5f4c6c2a62dd88f58133e333af1c251dd08f98d48445f650889d85e43b7b/zstd-1.5.6.7.tar.gz name: zstd version: 1.5.6.7 sha256: b3fa8f6bfb5e116b950ad633edccae4f65055a213ab4c00c1d4397f4b257b697 +- pypi: https://files.pythonhosted.org/packages/08/b8/2bc2590a34c733ea0570f366e6ad7d889d05c7825bd3ccab01f36ece71c6/zstd-1.5.7.2-cp312-cp312-macosx_11_0_arm64.whl + name: zstd + version: 1.5.7.2 + sha256: ef201b6f7d3a6751d85cc52f9e6198d4d870e83d490172016b64a6dd654a9583 - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda sha256: a4166e3d8ff4e35932510aaff7aa90772f84b4d07e9f6f83c614cba7ceefe0eb md5: 6432cb5d4ac0046c3ac0a8a0f95842f9 @@ -2776,3 +7312,14 @@ packages: purls: [] size: 567578 timestamp: 1742433379869 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-h6491c7d_2.conda + sha256: 0d02046f57f7a1a3feae3e9d1aa2113788311f3cf37a3244c71e61a93177ba67 + md5: e6f69c7bcccdefa417f056fa593b40f0 + depends: + - __osx >=11.0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 399979 + timestamp: 1742433432699 diff --git a/pyproject.toml b/pyproject.toml index 8ab5b091..943a06c0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ name = "esm" version = "3.2.1" description = "EvolutionaryScale open model repository" readme = "README.md" -requires-python = ">=3.10" +requires-python = ">=3.12,<3.13" license = {file = "LICENSE.txt"} authors = [ @@ -17,7 +17,7 @@ maintainers = [ classifiers = [ "Development Status :: 3 - Alpha", "Topic :: Scientific/Engineering :: Bio-Informatics", - "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.12", ] dependencies = [ @@ -35,9 +35,25 @@ dependencies = [ "attrs", "pandas", "cloudpathlib", + "httpx", "tenacity", - "zstd" + "zstd", + "ipywidgets", + "py3dmol", + "pydssp", + "boto3", + "pygtrie", + "dna_features_viewer", + "flash-attn @ https://github.com/Dao-AILab/flash-attention/releases/download/v2.8.1/flash_attn-2.8.1+cu12torch2.7cxx11abiFALSE-cp312-cp312-linux_x86_64.whl" ] +# Pytest +[tool.pytest.ini_options] +addopts = """ + --cov=esm + --cov-report term-missing:skip-covered + -n auto + --ignore=tests/oss_pytests/test_oss_client.py +""" [tool.setuptools] package-dir = {"" = "."} @@ -52,7 +68,7 @@ esm = ["data/*"] [tool.pixi.project] channels = ["conda-forge"] -platforms = ["linux-64"] +platforms = ["linux-64", "osx-arm64"] # These are build dependencies, to ensure pip support, keep run-time deps above in `dependencies` [tool.pixi.dependencies] @@ -60,6 +76,7 @@ pkg-config = "*" cmake = "*" pip = "*" twine = "*" +python = "3.12.*" [tool.pixi.pypi-dependencies] esm = { path = ".", editable = true } @@ -67,3 +84,61 @@ esm = { path = ".", editable = true } [tool.pixi.tasks] build-wheel = "python -m pip wheel --no-deps -w dist ." upload-wheel = "python -m twine upload --repository pypi" + +[tool.pixi.feature.dev.dependencies] +matplotlib = "*" +pre-commit = "*" +pytest = "*" +pytest-cov = "*" +pytest-xdist = "*" +seaborn = "*" +pyright = "==1.1.399" + +[tool.pixi.feature.dev.tasks] +lint-all = "pre-commit run --all-files --show-diff-on-failure" +cov-test = "pytest -v --junitxml=pytest.xml --cov=esm" + +[tool.pixi.environments] +default = {features = [], solve-group = "default"} +dev = {features = ["dev"], solve-group = "default"} + +[tool.ruff] +extend-include = ["*.ipynb"] + +[tool.ruff.lint] +# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`), sort imports ('I') +select = ["E4", "E7", "E9", "F", "I"] +ignore = [ + # allow variable == False (tensors should do this) + "E712", + # allow assigning of lambdas + "E731", + # Allow ambiguous variables, e.g. we use O for oxygen + "E741", + # Ignore errors from jaxtyping hints + # https://docs.kidger.site/jaxtyping/faq/#flake8-or-ruff-are-throwing-an-error + "F722", + # TODO: Fix the few offenders in a follow up PR + "E721", +] + +[tool.ruff.lint.isort] +split-on-trailing-comma = false +known-third-party = ["wandb"] + +[tool.ruff.format] +skip-magic-trailing-comma = true +docstring-code-format = true +docstring-code-line-length = "dynamic" + +[tool.isort] +known_third_party = ["wandb"] + +[tool.pyright] +root = ['.'] +useLibraryCodeForTypes = true +reportPrivateImportUsage = false +typeCheckingMode = "basic" + +[tool.importlinter] +root_package = "esm" diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 00000000..5af442ea --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,15 @@ +# OSS-specific variables and commands +DOCKER_TAG ?= dev +DOCKER_IMAGE_OSS=oss_pytests:${DOCKER_TAG} + +build-oss-ci: + docker build -f oss_pytests/Dockerfile oss_pytests -t $(DOCKER_IMAGE_OSS) + +start-docker-oss: + docker run \ + --rm \ + -e URL=${URL} \ + -e ESM3_FORGE_TOKEN=${ESM3_FORGE_TOKEN} \ + --name=$(USER)-oss_pytests \ + --network=host \ + ${DOCKER_IMAGE_OSS} diff --git a/tests/oss_pytests/Dockerfile b/tests/oss_pytests/Dockerfile new file mode 100644 index 00000000..f10b2023 --- /dev/null +++ b/tests/oss_pytests/Dockerfile @@ -0,0 +1,19 @@ +# Dockerfile.sdktest +FROM python:3.12-slim + +# Install pip and basic dependencies +RUN apt-get update && apt-get install -y curl build-essential && rm -rf /var/lib/apt/lists/* + +# Set working directory +WORKDIR /tests + +# Copy requirements and install them (assumes esm-oss is one of them) +COPY requirements.txt . + +RUN pip install --no-cache-dir -r requirements.txt + +# Copy in the tests +COPY . . + +# Default command (can be overridden in docker run) +CMD ["pytest", "-v", "test_oss_client.py"] diff --git a/tests/oss_pytests/requirements.txt b/tests/oss_pytests/requirements.txt new file mode 100644 index 00000000..d120ce35 --- /dev/null +++ b/tests/oss_pytests/requirements.txt @@ -0,0 +1,3 @@ +esm +pytest +httpx # TODO(williamxi): Remove this after the esm repo is fixed diff --git a/tests/oss_pytests/test_oss_client.py b/tests/oss_pytests/test_oss_client.py new file mode 100644 index 00000000..6fa4a30d --- /dev/null +++ b/tests/oss_pytests/test_oss_client.py @@ -0,0 +1,85 @@ +import os + +import pytest + +from esm.sdk import client # pyright: ignore +from esm.sdk.api import ( # pyright: ignore + ESMProtein, + ESMProteinTensor, + ForwardAndSampleOutput, + GenerationConfig, + LogitsConfig, + LogitsOutput, + SamplingConfig, + SamplingTrackConfig, +) +from esm.sdk.forge import SequenceStructureForgeInferenceClient # pyright: ignore + +API_TOKEN = os.environ.get("ESM3_FORGE_TOKEN", "") +URL = os.environ.get("URL") + + +@pytest.mark.sdk +def test_oss_esm3_client(): + assert URL is not None + + sequence = "MALWMRLLPLLALLAL___PDPAAA" + model = "esm3-small-2024-03" + esm3_client = client(model=model, url=URL, token=API_TOKEN) + protein = ESMProtein(sequence) + + encoded_protein = esm3_client.encode(input=protein) + assert isinstance(encoded_protein, ESMProteinTensor) + + decoded_protein = esm3_client.decode(input=encoded_protein) + assert isinstance(decoded_protein, ESMProtein) + + logits_config = LogitsConfig(sequence=True, return_embeddings=True) + result = esm3_client.logits(input=encoded_protein, config=logits_config) + assert isinstance(result, LogitsOutput) + + sampling_config = SamplingConfig(sequence=SamplingTrackConfig(temperature=0.1)) + result = esm3_client.forward_and_sample( + input=encoded_protein, sampling_configuration=sampling_config + ) + assert isinstance(result, ForwardAndSampleOutput) + + generation_config = GenerationConfig(track="sequence", num_steps=4) + result = esm3_client.generate(input=protein, config=generation_config) + assert isinstance(result, ESMProtein) + + +@pytest.mark.sdk +def test_oss_esmc_client(): + assert URL is not None + + sequence = "MALWMRLLPLLALLALAVUUPDPAAA" + model = "esmc-300m-2024-12" + esmc_client = client(model=model, url=URL, token=API_TOKEN) + + protein = ESMProtein(sequence) + encoded_protein = esmc_client.encode(input=protein) + assert isinstance(encoded_protein, ESMProteinTensor) + + decoded_protein = esmc_client.decode(input=encoded_protein) + assert isinstance(decoded_protein, ESMProtein) + + logits_config = LogitsConfig( + sequence=True, return_embeddings=True, return_hidden_states=True + ) + result = esmc_client.logits(input=encoded_protein, config=logits_config) + assert isinstance(result, LogitsOutput) + + +@pytest.mark.sdk +def test_oss_sequence_structure_forge_inference_client(): + assert URL is not None + + sequence = "MALWMRLLPLLALLALAVUUPDPAAA" + model = "esm3-small-2024-03" + client = SequenceStructureForgeInferenceClient( + model=model, url=URL, token=API_TOKEN + ) + + encoded_protein = client.fold(sequence=sequence) + assert isinstance(encoded_protein, ESMProtein) diff --git a/tests/oss_pytests/test_placeholder.py b/tests/oss_pytests/test_placeholder.py new file mode 100644 index 00000000..a3b975ad --- /dev/null +++ b/tests/oss_pytests/test_placeholder.py @@ -0,0 +1,6 @@ +import pytest + + +@pytest.mark.skip(reason="no other tests in this suite") +def test_placeholder(): + pass