Metadata-Version: 2.4
Name: shellde
Version: 0.2.0
Summary: Low-N, acquisition-driven protein mutation campaign planner: additive default, opt-in signals, honest abstention
Author: gyuminlee-repo
Keywords: directed-evolution,protein-engineering,active-learning,bayesian-optimization,epistasis,mutation-recommendation,uncertainty-calibration,active-site
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
Classifier: Operating System :: OS Independent
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: numpy
Requires-Dist: scipy
Requires-Dist: scikit-learn
Requires-Dist: pandas
Provides-Extra: plm
Requires-Dist: torch; extra == "plm"
Requires-Dist: esm; extra == "plm"
Provides-Extra: plm2
Requires-Dist: fair-esm; extra == "plm2"
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Requires-Dist: pyright; extra == "dev"
Requires-Dist: build; extra == "dev"
Requires-Dist: twine; extra == "dev"

# ShellDE

Low-N, acquisition-driven protein mutation recommendation. The spine is one measurement campaign:

```text
R0 seed to measure -> fit surrogate on all measured data -> acquire next plate -> measure -> repeat
```

ShellDE is not a magic zero-shot "better protein" predictor. Zero-shot, MSA, inverse-folding,
structure, and ddG signals are used as priors, constraints, or features. The function-aligned signal
is still measurement. The tool's job is to spend a small measurement budget better.

> **Naming.** `ShellDE` in prose, `shellde` as the PyPI distribution, the import, and the command. Never
> `Shellde`, `shellDE`, or `SHELLDE`.
>
> **Renamed.** This project was `epiquire` through v0.1.46.00 and is `shellde` from v0.2.00.00. The name
> is the active-site contact SHELL that `resolve-site` computes (and the 2nd-shell ring that
> `--exclude-catalytic` designs over), plus DE for directed evolution.
>
> **PyPI status (checked 2026-08-28).** `shellde` is NOT on PyPI: the PyPI JSON API returns 404 for it.
> `epiquire` IS on PyPI at 0.1.21, and that wheel still installs the OLD pre-rename package, unchanged,
> which is why a pinned Colab install naming it keeps working. No deprecation shim exists, in this tree
> or on PyPI. Publishing `shellde`, and only then turning `epiquire` into a shim that points at it, are
> PLANNED owner actions tracked in `docs/RENAME_PLAN.md`, not things already done.

## Install

`shellde` is not on PyPI yet, so install it from a source checkout of this repository. Run this from
the repository root, the directory holding `pyproject.toml`:

```bash
pip install .                  # core deps: numpy/scipy/scikit-learn/pandas
pip install ".[plm]"           # optional: adds torch + ESM-C (PLM re-rank / naturalness)
pip install -e .               # editable, when working on the code itself
```

Verified 2026-08-28 in a throwaway venv: `pip install .` builds `shellde-0.2.0` and the installed
`shellde` console script renders `usage: shellde`.

The short PyPI form stays here because it becomes the recommended path once the distribution is
published. It returns a 404 today, so do not run it yet:

```bash
pip install shellde            # ONLY AFTER shellde is published to PyPI (not yet; 404 today)
pip install "shellde[plm]"     # ONLY AFTER shellde is published to PyPI (not yet; 404 today)
```

Or run it in your browser with **no install** via the Colab notebook below.

## Easiest entry point: `shellde round`

One command per round. It auto-resolves the active-site design region (the `resolve-site` evidence
ladder: UniProt -> linked PDB cocrystal -> AlphaFold) and then runs the campaign round, so there is
no position bookkeeping by hand.

**Run it in your browser, no install:** open the notebook in Google Colab via its public gist (works
without repo access): [open in Colab](https://colab.research.google.com/gist/gyuminlee-repo/1a7a0b5dbf3919d652a6ba2dbdb3f49a/epiquire_round_colab.ipynb).
The notebook is a 3-line bootstrap over a `plate(...)` call: all logic lives in the pip package, so the
notebook file never needs re-uploading. That link opens the PRE-RENAME notebook. The published gist
still installs `epiquire` and imports `epiquire.colab.plate`, and that is exactly why it still runs
today. `notebooks/COLAB_GIST.md` records the resync order, which waits on the
PyPI publish.

Once `shellde` is published and the gist is resynced, any notebook can skip the form and call it
directly with the three lines below. They do not work yet, because the first one 404s:

```python
%pip install -q -U shellde
from shellde.colab import plate
df = plate("...WT sequence...", uniprot="Q50L36", auto_msa=True)   # R0 seed plate, MSA built for you
```

The core loop is numpy/scikit-learn (seconds); only the optional PLM path is heavy (Colab's free GPU).

```bash
# R0: no --measured -> the funclib seed plate to MEASURE. The seed needs a tolerance signal;
# supply --msa/--ddg/--if-logprobs, OR --auto-msa to build the MSA from the WT (ColabFold MMseqs2).
shellde round WT.fasta --uniprot Q50L36 --auto-msa --plate 95 --outdir outputs/round

# Rk: add each round's measured results -> the next AL plate over the full saturation universe
shellde round WT.fasta --uniprot Q50L36 --measured round0_measured.csv --plate 95 --outdir outputs/round
```

Point at the design region with whatever you have: `--uniprot ACC` (or `--uniprot auto` to find the accession by
an EXACT match of your WT sequence against UniProt), `--query`/`--pdb` (auto ladder), a local
`--holo` structure, or explicit `--positions`. Give more than one and the most-specific wins,
resolved by a fixed priority `positions > holo > pdb+uniprot > pdb > uniprot/query` -- so you can just
pass whatever you have. Two special combinations: giving **both** `--pdb` and `--uniprot` designs over
the PDB contact shell while flagging (QC) the UniProt catalytic core that falls inside it, and adding
`--exclude-catalytic` then designs only the tunable 2nd-shell ring (shell minus the usually-lethal
catalytic core). If the ladder cannot map evidence onto your WT it abstains (supply `--positions` or
`--holo`) rather than guessing. `round` is a thin wrapper: `resolve-site` and `campaign` below remain
the explicit, fully-configurable commands it calls.
No cocrystal at all? `--find-holo` (on `round`/`resolve-site`) Foldseek-searches for a ligand-bound
(holo) structural homolog of your WT and, if a SIGNIFICANT one exists (E-value + coverage gated), uses
its ligand pose to define the shell; it abstains when only distant homologs are found so a mismatched
pocket is never silently borrowed. When several evidence types are available (e.g. `--pdb` + `--uniprot`)
ShellDE prints an evidence-consensus map: each position tagged with which independent lines support it
and a confidence tier (high = >=2 agree), so you can measure high-confidence positions first.

## The campaign engine (what `round` calls)

Use `campaign` when starting an active-site campaign. It combines the two validated pieces:

1. **R0 cold-start seed**: FuncLib-style active-site library construction. This proposes variants to
   measure; it does not predict winners.
2. **R1+ active learning**: after measurements arrive, the AL loop searches the full saturation space
   of the design positions, not just the R0 seed.

### R0: generate the seed plate

```bash
shellde campaign WT.fasta \
  --holo holo_structure.pdb --ligand-resnames LIG \
  --msa alignment.a3m \
  --plate 95 \
  --outdir outputs/campaign
```

This writes `outputs/campaign/round0_plate.csv` with `variant,mutations,n_mut,seed_score`. Measure
those variants and save a `variant,fitness` CSV.

You can use explicit design positions instead of a holo structure:

```bash
shellde campaign WT.fasta \
  --positions 183,184,227,228 \
  --msa alignment.a3m \
  --plate 95 \
  --outdir outputs/campaign
```

### R1+: propose the next plate from measured data

```bash
shellde campaign WT.fasta \
  --positions 183,184,227,228 \
  --measured round0_measured.csv \
  --plate 95 \
  --outdir outputs/campaign
```

With multiple rounds, pass all accumulated CSVs:

```bash
shellde campaign WT.fasta \
  --positions 183,184,227,228 \
  --measured round0_measured.csv round1_measured.csv \
  --plate 95 \
  --outdir outputs/campaign
```

On later rounds, add `--plm esmc` to rerank the plate with an ESM-C surrogate refit on YOUR measured
data (EVOLVEpro-style; opt-in, GPU-recommended). Every Rk round also auto-prints an `advise` line --
held-out Spearman + a recommendation (`gather_more` / `model_reliable` / `open_epistasis`) -- so you
know each round whether to trust the surrogate or keep measuring broadly.

### Simulate against a complete landscape

For validation or retrospective benchmarks:

```bash
shellde campaign WT.fasta \
  --simulate data/trpb.csv \
  --positions 183,184,227,228 \
  --msa isps_run/msa_trpb.a3m \
  --strategies funclib singles random \
  --plate 95 --rounds 3 --seeds 20 --jobs 4 \
  --model-class ridge \
  --outdir artifacts/campaign_trpb
```

`--jobs` parallelizes independent seeds with spawn processes; serial output and parallel output are
byte-identical by test.

## Other commands

- `shellde recommend measured.csv`: rank next candidates from accumulated measurements. This is the
  AL round engine exposed directly. If you pass `--if-logprobs`, ShellDE CV-gates that signal by
  default and uses it only if it improves held-out performance on this protein. Use
  `--no-auto-signals` only as an expert override. `--recombine` also adds recombinations of your
  MEASURED beneficials (variants beating WT) to the candidate pool -- the strategy the real-data
  evidence supports (measure broadly, then recombine confirmed wins).
- `shellde resolve-site WT.fasta --uniprot ACC` (or `--query`): resolve active-site/binding positions
  from UniProt experimental curation, sequence-aligned onto YOUR WT numbering (handles transit-peptide
  / species offsets), with provenance + confidence per position. If UniProt features do not map it
  falls back to a UniProt-linked PDB cocrystal (active-site shells); `--pdb ID` forces the structure
  path. If neither maps, it fetches the AlphaFold model (predicted, apo). Optional `--apo-pocket`
  (fpocket) guesses pocket positions but is LOW-confidence -- it missed the catalytic site on ispS,
  so it is warned, not default. `--find-holo` instead Foldseek-finds a ligand-bound homolog (gated on
  significance) to borrow a pose from; giving `--pdb` + `--uniprot` prints an evidence-consensus map
  (per-position support + confidence tier). Abstains on positions rather than guessing -- then provide
  `--holo`/`--positions`. Feed resolved positions to `campaign`.
- `shellde hotspots scan.csv`: rank hotspot positions from a whole-protein single-mutant scan
  (`variant,activity`; CSV or Excel), then hand the top positions to `round --positions` for
  combinatorial AL. Combinatorial means the CANDIDATE POOL spans multi-mutants; the fitted model is
  additive by default (one-hot + bootstrap-ridge ensemble). Explicit pairwise epistasis is an opt-in
  on `recommend`: `--auto-gate-pairwise` opens the block only if cross-validated evidence on your own
  measured data supports it, while `--contacts-pdb` opens a contact-restricted pairwise block
  unconditionally (no CV gate).
  Honest: scan-based position selection is a coarse pre-filter,
  reliable only on additive sites (see `artifacts/hotspots_backtest_evidence.md`).
- `shellde funclib WT.fasta ...`: build only the active-site R0 measurement library. Useful when you
  want the seed plate without the campaign wrapper.
- `shellde advise measured.csv`: readiness check on measured data (held-out Spearman + pairwise gate
  -> gather_more / model_reliable / open_epistasis). Also auto-printed at the end of every `round`/`campaign`
  Rk round, so you are told each round whether to trust the surrogate or keep measuring.
- `shellde bench`: synthetic alpha-spectrum benchmark.
- `shellde report`: summarize a JSON report.

## What the opt-ins mean

Most optional flags are not feature clutter. They are either:

- **resources you must provide**: MSA, PDB, inverse-folding table, ddG table, PLM cache; or
- **expert overrides** for known regimes.

The default stays conservative because low-N model selection overfits. When ShellDE can decide from
this protein's measured data, it should decide itself -- and that is wired per command, not in general.
On `recommend`: a supplied `--if-logprobs` signal is CV-gated by default, and pairwise epistasis has
its own data gate (`--auto-gate-pairwise`). Not wired elsewhere: `--contacts-pdb` opens the pairwise
block with no CV gate, `campaign` and `round` add `--if-logprobs` unconditionally, and `--plm` /
`--naturalness` are never gated on any command. See `docs/OPTIN_AUDIT.md`.

## Honest scope

Evidence so far supports these claims:

- active-site FuncLib-style seeds enrich measurable libraries and improve low-budget discovery;
- the global activity winner can be outside the seed, so AL must search beyond the seed;
- stability/ddG signals are best used as constraints, while activity improvement is measurement-bound;
- high-order activity epistasis makes zero-shot winner prediction unreliable.

Evidence does **not** support: "ShellDE predicts the best protein from sequence/structure alone." It
increases the probability and efficiency of finding a better variant per measured plate, conditional on
the design space and assay.

## When NOT to use ShellDE

The focused active-site campaign helps only when the wins plausibly live in a region you can name
and measure combinatorially. Do NOT reach for it when:

- you do not know where beneficial mutations are (no mechanistic/structural prior on the target site);
- the wins are likely distal/distributed across the protein (use a broad whole-protein single-mutant
  scan instead -- that is a different tool class; the ShellDE additive default cannot rank
  whole-protein singles because one-hot has no cross-position transfer);
- you cannot measure a focused combinatorial library (no assay throughput at the chosen site).

Real-data caution (PtIspS, `artifacts/ispS_wetlab_validation.md`): a campaign run as a broad
single-mutant scan with the active site barely sampled and the global winner distal is exactly the
regime ShellDE is NOT for. Pick broad-vs-focused from your biology; ShellDE does not decide it.

More detail: `ARCHITECTURE.md`, `artifacts/DECISION_EVIDENCE.md`, and the Obsidian decision log named
there.
