Feature Extraction
Transformers
Safetensors
esmfold2
biology
protein-structure
multimodal-protein-model
custom_code
Instructions to use Synthyra/ESMFold2-Fast with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use Synthyra/ESMFold2-Fast with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("feature-extraction", model="Synthyra/ESMFold2-Fast", trust_remote_code=True)# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("Synthyra/ESMFold2-Fast", trust_remote_code=True, dtype="auto") - Notebooks
- Google Colab
- Kaggle
| """Taxonomy-paired MSA construction for ESMFold2 inference. | |
| Taxonomy IDs are read from FASTA headers as ``key=N`` tokens. Rows | |
| where any chain has ``key=-1`` (or no ``key=`` at all) are treated as | |
| unpaired and assigned to that chain's block-diagonal section after | |
| the paired rows. | |
| """ | |
| import re | |
| import numpy as np | |
| from .esmfold2_constants import ( | |
| MSA_GAP_TOKEN_ID, | |
| PROTEIN_3TO1, | |
| PROTEIN_RESIDUE_TO_RES_TYPE, | |
| PROTEIN_UNK_RES_TYPE, | |
| ) | |
| from .esmfold2_msa import MSA | |
| _KEY_RE = re.compile(r"key=(-?\d+)") | |
| def protein_letter_to_res_type() -> dict[str, int]: | |
| """Return the protein 1-letter → res_type mapping used by the MSA encoder.""" | |
| mapping: dict[str, int] = {} | |
| for three, one in PROTEIN_3TO1.items(): | |
| if three in PROTEIN_RESIDUE_TO_RES_TYPE: | |
| mapping[one] = PROTEIN_RESIDUE_TO_RES_TYPE[three] | |
| mapping["-"] = MSA_GAP_TOKEN_ID | |
| mapping["X"] = PROTEIN_UNK_RES_TYPE | |
| return mapping | |
| def _taxonomy_from_header(header: str) -> int: | |
| if not header: | |
| return -1 | |
| m = _KEY_RE.search(header) | |
| return int(m.group(1)) if m else -1 | |
| def msa_to_res_type_and_deletions( | |
| msa: MSA, letter_to_res_type: dict[str, int] | |
| ) -> tuple[np.ndarray, np.ndarray]: | |
| """Convert an :class:`MSA` to ``(res_type[M, L], deletion_count[M, L])``. | |
| Handles a3m insertion convention: lowercase letters and ``.`` are | |
| insertions and are not emitted; their count is accumulated into the | |
| next non-insertion position's deletion value. ``L`` is the query | |
| length after stripping insertions from row 0. | |
| """ | |
| query = msa.entries[0].sequence | |
| L = sum(1 for ch in query if not (ch.islower() or ch == ".")) | |
| M = msa.depth | |
| res_type = np.full((M, L), MSA_GAP_TOKEN_ID, dtype=np.int64) | |
| deletions = np.zeros((M, L), dtype=np.float32) | |
| for r, entry in enumerate(msa.entries): | |
| col = 0 | |
| ins = 0 | |
| for ch in entry.sequence: | |
| if ch == "." or (ch.islower() and ch != "-"): | |
| ins += 1 | |
| continue | |
| if col >= L: | |
| break | |
| if ch == "-": | |
| res_type[r, col] = MSA_GAP_TOKEN_ID | |
| else: | |
| res_type[r, col] = letter_to_res_type.get( | |
| ch.upper(), PROTEIN_UNK_RES_TYPE | |
| ) | |
| if ins > 0: | |
| deletions[r, col] = float(ins) | |
| ins = 0 | |
| col += 1 | |
| return res_type, deletions | |
| def _dummy_msa_residues(query_res_types: np.ndarray) -> np.ndarray: | |
| """Single-row 'MSA' for chains without one — just the query.""" | |
| return query_res_types[None, :] # [1, L] | |
| def construct_paired_msa( | |
| chain_msas: dict[int, MSA | None], | |
| chain_query_res_types: dict[int, np.ndarray], | |
| token_asym_ids: np.ndarray, | |
| token_res_ids: np.ndarray, | |
| letter_to_res_type: dict[str, int] | None = None, | |
| *, | |
| max_pairs: int = 8192, | |
| max_total: int = 16384, | |
| max_seqs: int = 16384, | |
| ) -> tuple[np.ndarray, np.ndarray, np.ndarray]: | |
| """Build paired MSA features. | |
| Parameters | |
| ---------- | |
| chain_msas | |
| ``asym_id -> MSA`` (or ``None`` for chains without an MSA). | |
| chain_query_res_types | |
| ``asym_id -> np.ndarray[L_c]`` of res-type ids for the chain's | |
| query. Used to build dummy MSAs when a chain has no MSA. | |
| token_asym_ids | |
| Per-token asym_id, length ``T``. Must be non-decreasing. | |
| token_res_ids | |
| Per-token residue index within chain, length ``T``. | |
| letter_to_res_type | |
| 1-letter → res-type mapping. Defaults to | |
| :func:`protein_letter_to_res_type`. | |
| Returns | |
| ------- | |
| msa_residues : ``np.ndarray[M, T]`` int64 | |
| deletion_value : ``np.ndarray[M, T]`` float32 (raw deletion counts; the | |
| ``arctan(/3) * pi/2`` transform is applied by the caller) | |
| is_paired : ``np.ndarray[M, T]`` float32 broadcast of per-row, | |
| per-chain paired flags. | |
| """ | |
| if letter_to_res_type is None: | |
| letter_to_res_type = protein_letter_to_res_type() | |
| chain_ids: list[int] = sorted(chain_msas.keys()) | |
| # Build per-chain (res_type, deletions, taxonomy) tables. | |
| chain_res_type: dict[int, np.ndarray] = {} | |
| chain_deletions: dict[int, np.ndarray] = {} | |
| chain_taxonomies: dict[int, list[int]] = {} | |
| for c in chain_ids: | |
| m = chain_msas.get(c) | |
| if m is None or m.depth == 0: | |
| qres = chain_query_res_types[c] | |
| chain_res_type[c] = _dummy_msa_residues(qres) | |
| chain_deletions[c] = np.zeros((1, qres.shape[0]), dtype=np.float32) | |
| chain_taxonomies[c] = [-1] | |
| continue | |
| rt, dl = msa_to_res_type_and_deletions(m, letter_to_res_type) | |
| chain_res_type[c] = rt | |
| chain_deletions[c] = dl | |
| chain_taxonomies[c] = [_taxonomy_from_header(e.header) for e in m.entries] | |
| # Group by taxonomy, skip query row and unpaired (-1) entries. | |
| taxonomy_map: dict[int, list[tuple[int, int]]] = {} | |
| for c in chain_ids: | |
| for seq_idx, taxon in enumerate(chain_taxonomies[c]): | |
| if seq_idx == 0 or taxon == -1: | |
| continue | |
| taxonomy_map.setdefault(taxon, []).append((c, seq_idx)) | |
| taxonomy_map = {k: v for k, v in taxonomy_map.items() if len(v) > 1} | |
| # Order taxonomies by number of distinct chains, descending. | |
| sorted_taxa = sorted( | |
| taxonomy_map.items(), key=lambda kv: len({c for c, _ in kv[1]}), reverse=True | |
| ) | |
| visited = {s for _, items in taxonomy_map.items() for s in items} | |
| available: dict[int, list[int]] = { | |
| c: [i for i in range(1, len(chain_taxonomies[c])) if (c, i) not in visited] | |
| for c in chain_ids | |
| } | |
| pairing: list[dict[int, int]] = [{c: 0 for c in chain_ids}] | |
| is_paired: list[dict[int, int]] = [{c: 1 for c in chain_ids}] | |
| for _, pairs in sorted_taxa: | |
| per_chain: dict[int, list[int]] = {} | |
| for c, seq_idx in pairs: | |
| per_chain.setdefault(c, []).append(seq_idx) | |
| max_occ = max(len(v) for v in per_chain.values()) | |
| for i in range(max_occ): | |
| row_pairing: dict[int, int] = {} | |
| row_is_paired: dict[int, int] = {} | |
| for c, seq_idxs in per_chain.items(): | |
| row_pairing[c] = seq_idxs[i % len(seq_idxs)] | |
| row_is_paired[c] = 1 | |
| for c in chain_ids: | |
| if c in row_pairing: | |
| continue | |
| row_is_paired[c] = 0 | |
| if available[c]: | |
| row_pairing[c] = available[c].pop(0) | |
| else: | |
| row_pairing[c] = -1 | |
| pairing.append(row_pairing) | |
| is_paired.append(row_is_paired) | |
| if len(pairing) >= max_pairs: | |
| break | |
| if len(pairing) >= max_pairs: | |
| break | |
| max_left = max((len(v) for v in available.values()), default=0) | |
| for _ in range(min(max_total - len(pairing), max_left)): | |
| row_pairing = {} | |
| row_is_paired = {} | |
| for c in chain_ids: | |
| row_is_paired[c] = 0 | |
| if available[c]: | |
| row_pairing[c] = available[c].pop(0) | |
| else: | |
| row_pairing[c] = -1 | |
| pairing.append(row_pairing) | |
| is_paired.append(row_is_paired) | |
| if len(pairing) >= max_total: | |
| break | |
| pairing = pairing[:max_seqs] | |
| is_paired = is_paired[:max_seqs] | |
| M = len(pairing) | |
| T = len(token_asym_ids) | |
| msa_residues = np.full((M, T), MSA_GAP_TOKEN_ID, dtype=np.int64) | |
| deletion_value = np.zeros((M, T), dtype=np.float32) | |
| paired_mask = np.zeros((M, T), dtype=np.float32) | |
| # Vectorize per chain: gather chain rows according to pairing[c], then | |
| # index into them by the chain's token residue ids. | |
| for c in chain_ids: | |
| rt = chain_res_type[c] | |
| dl = chain_deletions[c] | |
| Lc = rt.shape[1] | |
| chain_pairing = np.array([row[c] for row in pairing], dtype=np.int64) | |
| chain_paired = np.array([row[c] for row in is_paired], dtype=np.float32) | |
| token_mask = token_asym_ids == c | |
| if not token_mask.any(): | |
| continue | |
| token_res_in_chain = token_res_ids[token_mask] | |
| # Clamp residue indices to the MSA's column range. Modified-residue | |
| # tokens that exceed the query length fall back to the last column. | |
| cols = np.minimum(token_res_in_chain, Lc - 1) | |
| # Rows where pairing == -1 fall back to gap (already initialized). | |
| valid_rows = chain_pairing >= 0 | |
| if valid_rows.any(): | |
| gathered_rt = rt[chain_pairing[valid_rows]][:, cols] | |
| gathered_dl = dl[chain_pairing[valid_rows]][:, cols] | |
| valid_idx = np.where(valid_rows)[0] | |
| token_idx = np.where(token_mask)[0] | |
| msa_residues[np.ix_(valid_idx, token_idx)] = gathered_rt | |
| deletion_value[np.ix_(valid_idx, token_idx)] = gathered_dl | |
| paired_mask[:, token_mask] = chain_paired[:, None] | |
| return msa_residues, deletion_value, paired_mask | |