# Reverse Screen: user manual Covers using the service, using the published index on your own machine, and reproducing every number in this archive. Written against the archived snapshot 2026-09-08. --- ## 1. What the index is, and the rule that built it Every co-crystal ligand in the RCSB Protein Data Bank that passes one admission rule, carrying a PharmCast pharmacophore fingerprint and a Morgan fingerprint, mapped to the UniProt accessions and PDB entries it was solved against. Screening a molecule means comparing its fingerprints against all of them, taking the nearest, and collecting the proteins those ligands were crystallized with. Those proteins are the candidate targets, and each row names the entry the pair was solved in, which is the structure you can dock into. A component is admitted when all of the following hold: - the entry is **X-ray diffraction** at a resolution of 2.5 Angstrom or better **and** a free R factor at or below 0.28, **or** it is **electron microscopy** at 4.0 Angstrom or better, for which no free R factor condition is applied - the component is **non-polymer**, not a polymer residue - its formula weight is between **250 and 900 daltons** - the entry carries at least one polymer entity mapped to a UniProt accession Both resolution ceilings and the R factor cut are real predicates, and a rule quoted without them admits structures the index does not hold. The live service returns this rule at `https://reversescreen.ai/api/index-info`. A separate and stricter rule, X-ray at 1.8 Angstrom with a free R factor at or below 0.23, governs the pocket description work and the selection of the docking panel. It has nothing to do with index admission. Three crystallization additives sit inside the mass window and were indexed as though they were ligands until 6 October 2026: `2PE` and `P6G`, which are polyethylene glycols, and `TCE`, which is TCEP. They are excluded now. This archived snapshot predates that correction and still contains them. A promiscuous cofactor is capped: ATP is deposited against thousands of proteins, so no single indexed ligand contributes more than 50 sites, and the ones kept are the best resolved entries. When a query is itself in the archive its own deposited entries come back marked exact, at rank 0. They are shown, not withheld. For a benchmark they must be held out, and by the **InChIKey connectivity layer rather than the SMILES string**; section 4 says why. ## 2. Screening one molecule ### In the browser . Paste or draw a structure and press screen. No registration, no key, academic and commercial use alike. The default search has **two arms**. The pharmacophore arm compares a PharmCast pharmacophore fingerprint predicted from your 2D structure; the Morgan arm compares a Morgan fingerprint, radius 2 folded to 2,048 bits. A protein is admitted when **either** arm keeps the co-crystal ligand it was solved with, so the candidate list is the union of the two and each arm carries its own cutoff. Three operating points are offered. Panel is the share of the 28,579 target sites a query's candidate list reaches, and recall is the share of queries whose recorded target is in that list: | Preset | Pharmacophore | Morgan | Panel | Recall | |---|---|---|---|---| | strict | 0.65 | 0.50 | 1.25% | 0.59 | | balanced | 0.65 | 0.35 | 2.58% | 0.71 | | broad | 0.50 | 0.20 | 8.58% | 0.86 | Each result row names the PDB entry the ligand to protein pair was solved in, which is the structure you can dock into. Drill down on a row to dock your molecule into that site. ### Through the API ```bash curl -s https://reversescreen.ai/api/screen \ -H 'content-type: application/json' \ -d '{"smiles":"Cc1ccc(NC(=O)c2ccc(CN3CCN(C)CC3)cc2)cc1Nc1nccc(-c2cccnc2)n1", "pharm_min":0.65,"morgan_min":0.35}' ``` Supplying both cutoffs selects the two arm cutoff union and the response reports the admission of each candidate as `both`, `morgan_only` or `pharmacophore_only`. Supplying neither returns the single arm neighbor search. Cutoffs below 0.10 are refused: at 0.0 every indexed ligand is kept and the candidate list is every protein in the index. `https://reversescreen.ai/api/index-info` returns the admission rule, the index version and the archive snapshot it was built from. --- ## 3. Running the index on your own machine Take the weekly bundle from . It carries `screen_index.py`, which uses the Python standard library alone and contacts no server: ```bash python3 screen_index.py --pfp examples.pfp --top 5 ``` Imatinib should come back with ABL1 first, through component `STI` in entry `2HYY`. The bundle `README.md` covers the file formats and how to fingerprint your own structures. `screen_index.py` runs the pharmacophore arm only. The Morgan bits ship beside it as `rs_morgan_.tsv` so the second arm reproduces exactly; they are recomputable from the SMILES in the ligands table with RDKit. **The two score columns are not interchangeable.** On the orforglipron retrieval of the glucagon-like peptide-1 receptor through neighbor `UK1` the arms score 0.901 and 0.632, and a two dimensional cutoff of 0.70 would have missed the target entirely. --- ## 4. Reproducing the archived numbers ### What you need ```bash python3 -m venv env && ./env/bin/pip install "rdkit==2023.09.6" \ "numpy==1.26.4" "torch==2.9.1" gunzip -k retrieval_index_2026-09-08.jsonl.gz shasum -a 256 retrieval_index_2026-09-08.jsonl # must match manifest.json ``` The scripts expect the index at `work/retrieval_index.jsonl` relative to the directory above `scripts/`, which is what `--index` overrides. Anything that predicts a pharmacophore fingerprint also needs the PharmCast checkpoint `pharmcast_scp_v10.pt`, whose sha256 is in `manifest.json`. Point `PHARMCAST_CHECKPOINT` at it. The weights with checksums are at , the tool at , and a single structure can be fingerprinted in the browser at . The checkpoint has to be that one: a fingerprint from another checkpoint is not comparable with these vectors, every similarity comes out wrong, and nothing reports an error. ### Recall and cost ```bash ./env/bin/python scripts/bench_retrieval.py \ --index retrieval_index_2026-09-08.jsonl \ --sample 3000 --pools 1,5,10,25,50 ``` The 3,000 queries are drawn by `numpy.random.default_rng(20260906).choice(n, 3000, replace=False)` over index line order, which is deterministic, so the sample is the same one in `queries.json`. Hold each query out by its **InChIKey connectivity block, not by its SMILES string**: both case molecules are themselves deposited, orforglipron as `V6G` in `6XOX` and daraxonrasib as `A1AHB` in `9AX6`, and a SMILES comparison does not match them because the index SMILES comes from the component dictionary and the query's does not. The scaffold holdout removes, in addition, every indexed ligand sharing the query's Bemis-Murcko generic scaffold. The groups are in `scaffold_groups.json`, 10,588 of them, generated by `MakeScaffoldGeneric(GetScaffoldForMol(mol))`. ### Throughput ```bash ./env/bin/python scripts/throughput.py # one arm ./env/bin/python scripts/throughput_two_arm.py # both arms ``` Both measure in batches, because a virtual library is screened in batches and a single query figure understates the rate. Report the batch size with the number. On one core of an Apple M3 Ultra, in batches of 100, over the 27,797 ligand snapshot: the pharmacophore arm alone is 37.6 ms per molecule and about 95,700 molecules per hour, and both arms together are 45.7 ms per molecule and about 78,700 molecules per hour. The second arm is one extra Tanimoto scan over a matrix a fifth as wide; it is not an extra fingerprinting step, because PharmCast consumes those same 2,048 Morgan bits as its input. Timings from the hosted service are a different measurement of a different machine, a shared Cloud Run vCPU, and the two conventions are not interchangeable. ### The contact verified pairing ```bash ./env/bin/python scripts/contact_pairing.py \ --root \ --index retrieval_index_2026-09-08.jsonl \ --outdir out ``` `rs_contacts_2026-09-08.tsv` is the result already computed, and `pairing_audit.json` the coverage and shrinkage it reports. Joining the published map to this one gives the unverified pairings: ```bash tail -n +2 reverse_screen_sites_2026-10-08.tsv | cut -f1,2 | sort -u > published.pairs tail -n +2 rs_contacts_2026-09-08.tsv | sort -u > verified.pairs comm -23 published.pairs verified.pairs | wc -l ``` Use the snapshot's own sites table for an exact count; the weekly table is a later index and the two differ by a month of growth and by the crystallization additive filter added on 6 October 2026. ### The docking panel ```bash ./env/bin/python scripts/redock_test.py --pdb 5MME --comp 8Q6 ``` Needs AutoDock Vina 1.2.7 and Meeko 0.8.0, and fetches the entry from the PDB. `docking_panel_2026-09-06.tsv` holds the panel as run, and `docking_panel_2026-09-06.tar.gz` the raw records: each run's Vina log with the search box and every pose score, the Meeko preparation logs, the fresh input conformer and the docked poses. The four headline figures recompute from the TSV alone. A pose counts as correct at 2.0 Angstrom from the deposited coordinates: ```bash awk -F'\t' 'NR>1 && $3!="" {n++; if($16!="" && $16<=2.0){g++; if($17==1)f++; if($17<=3)t++}} END{printf "panel %d correct %d first %d within three %d\n",n,g,f,t}' \ docking_panel_2026-09-06.tsv ``` which prints `panel 38 correct 27 first 14 within three 23`. --- ## 5. Things that cost a run **Hold the query out by the InChIKey connectivity layer, not by the SMILES string.** Covered above; it is the single most expensive mistake in this benchmark. **The served RDKit must equal the RDKit a fingerprint was computed under.** The difference between RDKit 2023.09.6 and 2025.09.5 touches exactly three indexed components, `GCR`, `GIX` and `VPC`, all dative bond gallium complexes that the older build cannot parse at all. None is a benchmark query, so the archive's `rdkit 2023.09.6` compromises no published number, but check your own build on `GCR` before trusting a count. **The pharmacophore fingerprint is 10,549 bits wide, not 10,560.** Bits 10,549 to 10,559 are zero in every record and truncating at any other width deletes real bits. **Both resolution ceilings are real.** Electron microscopy is admitted to 4.0 Angstrom, not without a ceiling, and X-ray carries a free R factor cut at 0.28 as well as the 2.5 Angstrom limit. A rule quoted without them admits structures the index does not hold. **A positive Vina score is not a binding affinity.** Scores are reported as the scoring function returns them. **Redocking is not pose accuracy at a retrieved site.** It establishes that the search and scoring can place a ligand in a pocket that ligand is known to occupy. Cross-docking is the measurement that would establish the other thing, and it has not been done.