Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ jobs:
tests/test_pepsickle.py \
tests/test_bigmhc.py \
tests/test_unparseable_alleles.py \
tests/test_tulip.py \
tests/test_random.py

integration-netmhc:
Expand Down Expand Up @@ -109,3 +110,42 @@ jobs:
mkdir -p "$NETMHC_BUNDLE_TMPDIR"
export PATH="$PATH:$NETMHC_BUNDLE_HOME/bin"
./test.sh

integration-tulip:
# Exercises the TULIP wrapper end-to-end: mhctools runs in one interpreter
# and shells out to an ISOLATED sidecar env (torch + transformers==4.32.1)
# holding a public TULIP-TCR checkout. TULIP is GPLv3; we clone (not vendor)
# it here, same as running any user-provided external tool.
runs-on: ubuntu-22.04
steps:
- name: Checkout repository
uses: actions/checkout@v4

# Python 3.11 so transformers==4.32.1's tokenizers install from a
# prebuilt wheel (no cp312 wheel exists; a Rust build would be needed).
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: pip

- name: Install mhctools
run: |
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"

- name: Build isolated TULIP sidecar env + clone TULIP-TCR
env:
# This job's `python` is already 3.11; use it for the sidecar env.
TULIP_SETUP_PYTHON: python
run: |
python -m pip install uv
scripts/setup_tulip_env.sh "${{ github.workspace }}/tulip-env" \
"${{ github.workspace }}/TULIP-TCR"

- name: Run TULIP wrapper tests (incl. end-to-end)
env:
TULIP_HOME: ${{ github.workspace }}/TULIP-TCR
TULIP_PYTHON: ${{ github.workspace }}/tulip-env/bin/python
run: |
python -m pytest tests/test_tulip.py -ra
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,36 @@ Examples:
| `NetCleave_I` | `proteasome_cleavage` | `none` | `I` |
| `NetCleave_II` | `endolysosomal_cleavage` | `none` | `II` |
| `NetTCR` | `pMHC_TCR_binding` | `none` | `I` |
| `Tulip` | `pMHC_TCR_binding` | `single_allele` | `I` |

### TCR predictors (`NetTCR`, `Tulip`)

`NetTCR` and `Tulip` predict pMHC:TCR binding — whether a paired αβ T-cell
receptor (an `mhctools.TCR`, described by its CDR loops) recognises a peptide.
Both take `(peptide, TCR)` inputs; `Tulip` additionally takes the presenting
MHC allele.

```python
from mhctools import Tulip, TCR

tcr = TCR(cdr3a="CAGASGNTGKLIF", cdr3b="CASSIRASYEQYF", name="clone1")
predictor = Tulip() # needs TULIP_HOME + TULIP_PYTHON
results = predictor.predict(["GILGFVFTL"], [tcr], mhc="HLA-A*02:01")
results[0].preds[0].score # higher = more likely binding
```

[TULIP-TCR](https://github.com/barthelemymp/TULIP-TCR) is **GPLv3** and pinned to
`transformers==4.32.1`; mhctools is Apache-2.0 and depends on neither torch nor
transformers. The `Tulip` wrapper therefore vendors none of TULIP — it runs a
user-provided checkout out-of-process, in an isolated interpreter, via TULIP's
own `predict.py`. Set two things up first (see `scripts/setup_tulip_env.sh`,
which does both):

- `TULIP_HOME` — a clone of TULIP-TCR (provides `predict.py`, `src/`, tokenizers,
and the released `model_weights/`);
- `TULIP_PYTHON` — an isolated **Python 3.11** interpreter with `torch` and
`transformers==4.32.1` (3.11 so `tokenizers` installs from a prebuilt wheel and
needs no Rust toolchain).

For MHCflurry presentation, `presentation_allele_mode="haplotype"` treats the
requested alleles as one sample genotype and emits one `pMHC_presentation`
Expand Down
4 changes: 3 additions & 1 deletion mhctools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
"MHCflurry": (".mhcflurry", "MHCflurry"),
"MHCflurry_Affinity": (".mhcflurry", "MHCflurry_Affinity"),
"NetTCR": (".nettcr", "NetTCR"),
"Tulip": (".tulip", "Tulip"),
}


Expand All @@ -78,7 +79,7 @@ def __getattr__(name):
raise AttributeError(
"module %r has no attribute %r" % (__name__, name))

__version__ = "3.19.0"
__version__ = "3.20.0"

__all__ = [
"Prediction",
Expand Down Expand Up @@ -148,6 +149,7 @@ def __getattr__(name):
"BigMHC_EL",
"BigMHC_IM",
"NetTCR",
"Tulip",
"RandomBindingPredictor",
"UnsupportedAllele",
]
Loading
Loading