PLASMA is a tiny, pluggable head that turns any frozen protein-language-model
(PLM) into a residue-level local aligner. It reformulates protein substructure
alignment as a regularised optimal transport problem and runs ~50× faster than
structure-based aligners (TM-Align, Foldseek) by operating on pre-computed
embeddings.
This repository hosts the trained PLASMA heads for every (task, backbone)
combination from the paper, plus instructions for the parameter-free
PLASMA-PF baseline (which has no learned weights).
All heads share the same architecture: a small LRL non-linearity
(LazyLinear → ReLU → Linear → LayerNorm, hidden dim 512) followed by a
parameter-free Sinkhorn iteration (temperature=0.1, n_iters=20). The
checkpoint files are ~3 MB each.
How to use
PLASMA is a head: it consumes per-residue embeddings from a frozen protein
language model and returns a soft alignment matrix between two
sub-structures. The end-to-end pipeline is therefore three steps:
Embed each protein with the backbone the head was trained on (one of the
seven listed above).
Run the PLASMA head on the (residue × residue) embeddings to get a soft
alignment matrix M ∈ [0, 1]^{n_q × n_c}.
Optionally reduce M to a scalar similarity score with
utils.alignment_score.
The Alignment class and the load_plasma helper live in the alignment
package shipped by that repo.
2. Load a trained head
python
1from alignment import load_plasma
23# task ∈ {"active_site", "binding_site", "motif"}4# backbone is the PLM whose embeddings the head was trained on5model = load_plasma(task="active_site", backbone="esm2_t33_650M_UR50D")6model.eval()
load_plasma downloads the matching config.json + model.safetensors from
this repo via huggingface_hub and rebuilds the Alignment module.
3. Compute embeddings with the matching backbone
PLASMA does not embed sequences itself. The example below shows how to do it
with ESM-2 via transformers; the same pattern works for any other
backbone (Ankh, ProstT5, ProtBERT, ProtT5, TM-Vec, ProtSSN —
their loaders are documented in embed.py in the GitHub repo).
python
1import torch
2from transformers import AutoTokenizer, AutoModel
34device ="cuda"if torch.cuda.is_available()else"cpu"56tokenizer = AutoTokenizer.from_pretrained("facebook/esm2_t33_650M_UR50D")7backbone = AutoModel.from_pretrained("facebook/esm2_t33_650M_UR50D").to(device).eval()89@torch.no_grad()10defembed(sequence:str)-> torch.Tensor:11"""Return per-residue embeddings of shape (L, 1280) — no special tokens."""12 tokens = tokenizer(sequence, return_tensors="pt", add_special_tokens=True).to(device)13 h = backbone(**tokens).last_hidden_state[0]# (L+2, 1280): <cls> ... <eos>14return h[1:-1].cpu()# drop <cls> and <eos>1516seq_q ="MKTAYIAKQRQISFVKSHFSRQDILDLWIYHTQGYFP"17seq_c ="MKTVRQERLKSIVRILERSKEPVSGAQLAEELSVSRQVIVQDIAYLRSLGYNI"1819H_q = embed(seq_q)# (n_q, 1280)20H_c = embed(seq_c)# (n_c, 1280)
4. Run PLASMA and read the alignment matrix
python
1# `batch_q` / `batch_c` assign each residue to a sample. Use zeros for a2# single pair; use [0, 0, ..., 1, 1, ...] to score multiple pairs in one batch.3batch_q = torch.zeros(H_q.size(0), dtype=torch.long)4batch_c = torch.zeros(H_c.size(0), dtype=torch.long)56with torch.no_grad():7 M = model(H_q, H_c, batch_q, batch_c)# (n_q, n_c) in [0, 1]89# Hard residue-residue assignment (top of column / row in the transport plan)10q_to_c = M.argmax(dim=1)# for each query residue, the best candidate residue11c_to_q = M.argmax(dim=0)# for each candidate residue, the best query residue
M is a (near-)doubly-stochastic transport plan: rows and columns each sum
to ~1, so M[i, j] is the soft probability that query residue i aligns to
candidate residue j. Thresholding at 0.5 gives a sparse local alignment;
plotting M as a heatmap gives the canonical PLASMA visualisation (the
diagonal stripe in the visual abstract above).
5. Reduce to a similarity score
To collapse the alignment matrix into a single number per protein pair (the
quantity used to compute ROC-AUC / F1-Max in the tables above), use
utils.alignment_score from the GitHub repo. It applies the diagonal
convolution + thresholding described in the paper:
python
1from utils.alignment_utils import alignment_score
23score = alignment_score(4 H_q, H_c, M, batch_c,5 threshold=0.5,# gating on max-row / max-col residues6 K=10,# diagonal-convolution window7)# -> shape (num_pairs_in_batch,), here (1,)8print(float(score))
PLASMA-PF (parameter-free)
PLASMA-PF is a hinge / Sinkhorn baseline with no learned weights. Use it
when you want a strong zero-training baseline on top of any backbone — there
is nothing to download:
It accepts the same forward signature as the trained heads above and pairs
with any of the seven supported backbones.
Available variants & evaluation results
Numbers below are 3-seed averages reported in the paper. The seven backbone
columns correspond to the seven subfolders under each task. Bold marks the
best backbone for each row.
Interpolation (in-distribution test split)
Task
Metric
Ankh
ESM-2
ProstT5
ProtBERT
ProtSSN
ProtT5
TM-Vec
Motif
ROC-AUC
.925
.933
.954
.854
.922
.972
.910
F1-Max
.885
.877
.885
.784
.866
.918
.853
PR-AUC
.921
.931
.953
.872
.920
.971
.914
Label Match Score
.921
.890
.929
.746
.767
.937
.792
Binding Site
ROC-AUC
.995
.992
.993
.981
.992
.993
.980
F1-Max
.987
.986
.983
.948
.982
.988
.970
PR-AUC
.996
.994
.995
.985
.993
.995
.984
Label Match Score
.951
.950
.951
.880
.872
.951
.900
Active Site
ROC-AUC
.994
.991
.993
.986
.992
.994
.991
F1-Max
.989
.985
.987
.967
.987
.987
.982
PR-AUC
.994
.992
.994
.988
.994
.994
.992
Label Match Score
.975
.969
.975
.904
.885
.972
.938
Extrapolation (held-out hard test split)
Task
Metric
Ankh
ESM-2
ProstT5
ProtBERT
ProtSSN
ProtT5
TM-Vec
Motif
ROC-AUC
.960
.972
.975
.870
.949
.968
.954
F1-Max
.915
.931
.926
.799
.896
.922
.903
PR-AUC
.948
.970
.969
.873
.940
.962
.944
Label Match Score
.842
.786
.801
.541
.537
.738
.704
Binding Site
ROC-AUC
.995
.999
.993
.951
.999
.999
.990
F1-Max
.992
.991
.985
.896
.988
.996
.983
PR-AUC
.997
.999
.995
.958
.998
.999
.992
Label Match Score
.894
.851
.891
.603
.753
.902
.824
Active Site
ROC-AUC
.995
.996
.996
.980
.997
.999
.995
F1-Max
.992
.986
.991
.950
.991
.991
.985
PR-AUC
.995
.997
.997
.984
.998
.999
.996
Label Match Score
.938
.882
.931
.697
.737
.893
.880
Each subfolder also contains a metadata.json with the full hyperparameter
config in machine-readable form.
Score head:K=10, threshold=0.5 (used by
utils.alignment_score to reduce the transport plan to a scalar).
Optimiser / loss: Adam (lr=1e-4), BCEWithLogitsLoss on the alignment
score plus a label-match auxiliary loss (target_loss_weight=1.0).
Data: the InterPro-derived motif / binding-site / active-site datasets
shipped under data/raw/ in the GitHub repo, split into train / validation /
test / test-hard with dataset_fraction=0.1 (default sweep) and
dataset_fraction=1.0 (full sweep — checkpoints here are from the full
sweep).
Selection metric: validation loss (early stopping, patience=3).
Citation
If you use these weights, please cite the PLASMA paper:
bibtex
1@inproceedings{wang2026plasma,
2 title = {Fast and Interpretable Protein Substructure Alignment via Optimal Transport},
3 author = {Wang, Zhiyu and Zhou, Bingxin and Wang, Jing and Tan, Yang and Zhao, Weishu and Li{\`o}, Pietro and Hong, Liang},
4 booktitle = {International Conference on Learning Representations (ICLR)},
5 year = {2026},
6 url = {https://arxiv.org/abs/2510.11752},
7}