Skip to content
Merged
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
17 changes: 17 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,20 @@ jobs:
sys.exit(1)
print('PASS: Pipeline outperforms random ranker on hidden-active benchmark')
"

- name: Expanded benchmark (500 AMP + 500 decoy, AUROC > 0.70)
run: |
PYTHONPATH=src python -m openamp_foundry.cli validate-scoring \
--amp-csv examples/validation/known_amps_500.csv \
--decoy-csv examples/validation/random_background_500.csv \
--out outputs/validate_scoring_500_ci.json
PYTHONPATH=src python -c "
import json
data = json.load(open('outputs/validate_scoring_500_ci.json'))
auroc = data['auroc']
print(f'Expanded benchmark AUROC: {auroc:.4f}')
if auroc < 0.70:
print(f'FAIL: Expanded benchmark AUROC {auroc:.4f} < 0.70')
exit(1)
print(f'PASS: Expanded benchmark AUROC {auroc:.4f} >= 0.70')
"
23 changes: 23 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ help:
@echo " make bench-multi-negatives Multi-negative-set benchmark (4 decoy distributions)"
@echo " make bench-baseline Hidden-positive recovery benchmark (demo set)"
@echo " make bench-cluster-split Cluster-aware bootstrap CI (de-inflates near-duplicates)"
@echo " make bench-500 Full benchmark on expanded 500-AMP set (AUROC, AUPRC, recall)"
@echo " make bench-cluster-split-500 Cluster-split on expanded 500-AMP set"
@echo " make bench-expert-ablation Expert composite vs ensemble ablation (honesty check)"
@echo " make bench-selectivity Within-AMP selectivity (hemolytic vs selective)"
@echo " make bench-feature-decomp Per-feature selective_vs_hemolytic decomposition"
Expand Down Expand Up @@ -151,6 +153,27 @@ bench-cluster-split:
--decoy-csv examples/validation/random_background.csv \
--out outputs/cluster_split_report.json

bench-500:
PYTHONPATH=src $(PYTHON) -m openamp_foundry.cli validate-scoring \
--amp-csv examples/validation/known_amps_500.csv \
--decoy-csv examples/validation/random_background_500.csv \
--benchmark-type standard \
--out outputs/validate_scoring_500.json
PYTHONPATH=src $(PYTHON) -m openamp_foundry.cli validate-scoring \
--amp-csv examples/validation/known_amps_500.csv \
--decoy-csv examples/validation/random_background_500.csv \
--config configs/phase3.yaml \
--benchmark-type standard \
--out outputs/validate_scoring_phase3_500.json
@echo "Expanded benchmark (500 AMP + 500 decoy) complete."

bench-cluster-split-500:
PYTHONPATH=src $(PYTHON) -m openamp_foundry.cli bench cluster-split \
--amp-csv examples/validation/known_amps_500.csv \
--decoy-csv examples/validation/random_background_500.csv \
--out outputs/cluster_split_500.json
@echo "Cluster-split on expanded 500-AMP set complete."

bench-expert-ablation:
PYTHONPATH=src $(PYTHON) -m openamp_foundry.cli bench expert-ablation \
--amp-csv examples/validation/known_amps.csv \
Expand Down
7 changes: 4 additions & 3 deletions docs/50_LOOP_PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Strengthen every benchmark against self-deception. Expand datasets. Add honest f
| Loop | Bottleneck | Deliverable | Verification |
|------|-----------|-------------|-------------|
| 9 ✅ | Benchmark gate only checked standard + phase3 AUROC. Cluster-split, selectivity, triage could silently regress | Extended benchmark regression gate (v0.5.27): `scripts/benchmark_gate.py` now checks cluster_split.full_auroc, cluster_split.representative_auroc, rich_selectivity.detection.auroc, gate_triage.triages_correctly, triage.best_scorer. 8 new tests. 1709 total. | `make bench-gate` passes; cluster-split AUROC gated at 0.03 tolerance; selectivity at 0.05; triage booleans enforced |
| 10 | Benchmark expanded to n=191 but ROADMAP flags 500+ target as deferred | **500+ AMP benchmark**: expand to ≥500 public-domain AMPs + 500 composition-matched decoys. Recompute AUROC, CI, AUPRC | Validated on new dataset; CI width reported |
| 10 ✅ | Benchmark expanded to n=191 but ROADMAP flags 500+ target as deferred | **500+ AMP benchmark**: expand to ≥500 public-domain AMPs + 500 composition-matched decoys. Recompute AUROC, CI, AUPRC | Validated on new dataset: AUROC 0.7792 (CI₉₅: 0.7505–0.8065); cluster-aware CI 0.746–0.8102; representative AUROC 0.778 |
| 11 | No time-split benchmark (hardest leakage test). Most public AMP databases lack deposition dates | Time-split: gather dated AMP records, split by year (pre-2015 train, post-2015 test). Report AUROC with CI | Time-split AUROC > 0.60 or honest failure documented |
| 12 | Strict triage: no scorer passes composition-matched decoy test. This is the hardest known gap | Analyze which 1D features survive scrambling. Build an order-dependent feature (dipeptide frequencies, auto-cross-covariance) and test if it recovers scrambled-decoy signal | Strict sel_vs_dec AUROC improves vs baseline 0.572 |
| 13 | Cross-dataset generalisation untested. Current benchmark uses same database for train/test | Cross-dataset benchmark: train on APD6, test on DRAMP (or vice versa). Report transfer AUROC | Transfer AUROC documented (may be weak — publish honestly) |
Expand Down Expand Up @@ -172,7 +172,7 @@ If no data arrives, virtual assay scaffolding continues independently.

```
Phase 0: ✅ Complete (Loops 1–8)
Phase 1: Loop 10 of 13 (next loop: 11)
Phase 1: Loop 11 of 13 (next loop: 12 — time-split benchmark)
Phase 2: Not started
Phase 3: Not started
Phase 4: Not started
Expand Down Expand Up @@ -203,10 +203,11 @@ Phase 4: Not started
|------|-----------|-------------|-------------|
| 9 ✅ | Benchmark gate only checks strict AUROC — no cluster-split, selectivity, or triage metric regression | Extended `benchmark_gate.py` with `_deep_get()` dotted-path resolver; 5 numeric + 2 boolean metrics; separate tolerances | 1709 pass, 8 new gate tests (total 21) |
| 10 ✅ | No multi-negative benchmark; no honest assessment of composition-dependence | `scripts/benchmark_multi_negatives.py` with 4 decoy distributions; Makefile target; CI gate; composition-dependence documented as honest finding | 1723 pass, 14 new tests |
| 11 ✅ | Benchmark expanded to n=191 but ROADMAP flags 500+ target as deferred. Current n=191 gives ±0.07 CI width | Expanded benchmark to 500 + 500 (n=1000). `scripts/curate_500_amp_benchmark.py`: UniProt-reviewed + APD6 natural + existing curated. AUROC 0.7792 (CI₉₅: 0.7505–0.8065). Cluster-aware CI 0.746–0.8102 (width 0.064, ~2.3× tighter). Representative AUROC 0.778 ≈ full AUROC 0.7792. `make bench-500`, `make bench-cluster-split-500`, CI gate | 500 AMPs + 500 decoys; AUROC > 0.70 verified; CI width ±0.028 vs ±0.07 on n=191 |

### Phase 0 exit criteria (archived):
- ✅ `from openamp_foundry.calibration import GateVerdict` works
- ✅ `make ci` passes with benchmark gate
- ✅ A new agent can read README → run demo → understand calibration flow → contribute safely in one session

**Next loop:** Loop 11 — Phase 1 (500+ AMP benchmark expansion).
**Next loop:** Loop 12 — Phase 1 (Time-split benchmark).
82 changes: 74 additions & 8 deletions docs/METRICS_CURRENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ Machine-readable snapshot: `outputs/metrics_snapshot.json` regenerated with `mak
> **Purpose:** One authoritative table of current pipeline metrics. If any doc disagrees
> with this file, this file wins. Updated whenever benchmark/benchmark config changes.
>
> **Last updated:** 2026-07-05 (subpackage public API surface, v0.5.25)
> **New in v0.5.25:** 11 subpackages now publish a curated `__all__` so `from openamp_foundry.benchmark import run_triage_benchmark` works cleanly. `tests/test_public_api_imports.py` (7 tests) locks in the public surface. No benchmark numbers changed.
> **Pipeline version:** v0.5.25
> **Last updated:** 2026-07-05 (expanded 500-AMP benchmark, v0.5.29)
> **New in v0.5.29:** Expanded benchmark to 500 AMPs + 500 composition-matched decoys (n=1000). AUROC 0.7792 (CI₉₅: 0.7505–0.8065) confirms signal generalizes. Cluster-aware CI: 0.746–0.8102. Representative AUROC: 0.778. Standard benchmark (n=191) retained for backward comparison.
> **Pipeline version:** v0.5.29
> **Branch:** main

---
Expand All @@ -31,7 +31,69 @@ Machine-readable snapshot: `outputs/metrics_snapshot.json` regenerated with `mak
| Interpretation | **STRONG** | AUROC > 0.70 gate passed |


### Cluster-Split Benchmark (near-duplicate de-inflation)
### Expanded 500-AMP Benchmark (n=1000)

> Added 2026-07-05. The original benchmark (95 AMPs + 96 decoys, n=191) was
> expanded to 500 AMPs + 500 length-matched decoys (n=1000) using UniProt
> reviewed AMPs (CC BY 4.0) and APD6 natural sequences (academic use).
> This provides a more honest estimate of pipeline discriminative power
> with tighter confidence intervals.
>
> Curated by: `scripts/curate_500_amp_benchmark.py`
>
> Run: `make bench-500`

| Metric | Value | Notes |
|--------|-------|-------|
| Positives | **500** public-domain AMPs | UniProt reviewed + APD6 natural + existing curated |
| Negatives | **500** length-matched random decoys | Swiss-Prot residue frequencies, seed=20260705 |
| Total (n) | **1000** | ~2.3× reduction in CI width over the original 87-seq benchmark |
| **Pipeline AUROC** | **0.7792** | Bootstrap CI₉₅: 0.7505–0.8065 |
| **Phase3 AUROC** | **0.7744** | Synthesis gate config |
| **Pipeline AUPRC** | **0.7705** | Random baseline: 0.5000 |
| **Phase3 AUPRC** | **0.7656** | Random baseline: 0.5000 |
| Recall@10 | 0.020 | 10/500 positives in top 10 |
| Recall@20 | 0.040 | 20/500 positives in top 20 |
| Recall@43 | 0.076 | 38/500 positives in top 43 |
| Interpretation | **STRONG** | AUROC > 0.70 gate passed |

### Expanded Cluster-Split Benchmark (near-duplicate de-inflation)

| Metric | Pipeline (pipeline.yaml) | Phase3 (phase3.yaml) |
|--------|:-----------------------:|:-------------------:|
| Full AUROC | 0.7792 | 0.7744 |
| **Cluster-aware CI₉₅** | **0.746–0.8102** | **same config** |
| Representative AUROC (1/cluster) | 0.778 | 0.7744 |
| Representative CI₉₅ | 0.7455–0.8084 | — |
| Held-out AUROC (195 near-dup AMPs) | 0.7828 | — |
| Independent clusters | 374 / 500 | 374 / 500 |
| AMPs in multi-member clusters | 195 / 500 | 195 / 500 |
| Multi-member clusters | 69 | 69 |

**Key findings:**

1. **Signal generalizes to 10× larger set.** AUROC 0.7792 on n=1000 is essentially
identical to 0.7832 on n=191. The pipeline does not overfit to the 95-sequence
benchmark.

2. **CIs are much tighter.** Cluster-aware CI: 0.746–0.8102 (width 0.064) vs
0.7061–0.8526 (width 0.146) on n=191. The expanded set provides ~2.3× tighter
confidence intervals.

3. **Representative AUROC nearly equals full AUROC.** 0.778 vs 0.7792. On the
original benchmark, representative AUROC (0.7607) was lower than full (0.7832).
The expanded set's 500 sequences are less dominated by near-duplicate inflation.

4. **Cluster-aware CI lower bound (0.746) is well above the 0.65 gate.** The
pipeline's signal is robust to near-duplicate de-inflation.

5. **Honest limitation:** The expanded set uses UniProt-reviewed and APD6 sequences
annotated as antimicrobial. These carry the same annotation bias as the original
set: AMP annotation is an active research field, and some annotated AMPs may not
be genuinely antimicrobial. The set also includes fewer defensins and
cysteine-rich peptides due to the 10–30 AA length constraint.

### Cluster-Split Benchmark (near-duplicate de-inflation, n=191)

> Added 2026-07-01. The standard benchmark treats all 95 AMPs as independent samples.
> 33 of 95 AMPs are in 14 near-duplicate clusters (sim >= 0.70): magainin-1/2/3,
Expand Down Expand Up @@ -72,15 +134,19 @@ Run: `openamp-foundry bench cluster-split`

| Point | Benchmark | AUROC | Phase3 AUROC | Source |
|-------|-----------|-------|--------------|--------|
| Current | 95 AMP + 96 decoy (n=191) | **0.7832** | **0.7448** | PR #110 |
| **Expanded** | **500 AMP + 500 decoy (n=1000)** | **0.7792** | **0.7744** | v0.5.29 (Loop 11) |
| Standard | 95 AMP + 96 decoy (n=191) | 0.7832 | 0.7448 | PR #110 |
| Original demo set | 43 AMP + 44 decoy (n=87) | 0.8420 | 0.8266 | PR #72 |
| Pre-face-bonus | 43 + 44 | 0.8348 | 0.8126 | PR #70 |
| Pre-windowed-mu_h | 43 + 44 | 0.8047 | 0.7846 | PR #66 |
| Pre-Trp-bonus | 43 + 44 | 0.8164 | — | PR #65 (transient) |

**Note:** All historical baselines use the original demo set (43 AMPs + 44 decoys). Direct
comparison with the expanded benchmark is not meaningful — the expanded set is more
representative of diverse AMP classes not covered by the helic-centric scorer.
**Note:** The expanded benchmark (500+500, n=1000) is now the primary benchmark.
The n=191 benchmark is retained for backward comparison. The expanded set is more
representative of diverse AMP classes and provides ~2.3× tighter confidence intervals.
Historical baselines from the demo set (n=87) should not be directly compared with
the expanded benchmark — the helic-centric scorer's strong performance on small
amphipathic-helix sets does not reflect performance on diverse AMP classes.

---

Expand Down
42 changes: 42 additions & 0 deletions docs/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,48 @@ penalizes the AMP-like composition that hemolytic AMPs share with their
scrambled versions. It also retains 3 decoys in top-20 (vs 0 for ensemble).
It must NOT replace the ensemble activity gate — it is a complementary signal.

## v0.5.29 — Expanded 500-AMP Benchmark ✓ (2026-07-05)

- Expanded benchmark from 95 + 96 (n=191) to 500 AMP + 500 decoy (n=1000)
- Curation script: `scripts/curate_500_amp_benchmark.py` — reads UniProt-reviewed
AMPs (CC BY 4.0) and APD6 natural sequences, filters to 10-30 AA, deduplicates
against existing curated set, samples to 500, generates matched Swiss-Prot
frequency decoys
- Pipeline AUROC on expanded set: 0.7792 (CI₉₅: 0.7505–0.8065) — signal
generalizes, essentially identical to 0.7832 on n=191
- Phase3 AUROC: 0.7744 (was 0.7448 on n=191)
- Cluster-aware CI: 0.746–0.8102 (width 0.064 vs 0.146 on n=191) — ~2.3× tighter
- Representative AUROC: 0.778 — nearly equals full AUROC (improvement over
n=191 where representative 0.7607 < full 0.7832)
- Expanded benchmark files: `examples/validation/known_amps_500.csv`,
`examples/validation/random_background_500.csv`
- Makefile targets: `make bench-500`, `make bench-cluster-split-500`
- CI gate: expanded benchmark AUROC > 0.70 enforced in CI after standard gate
- Metrics snapshot: `outputs/metrics_snapshot_500.json`
- METRICS_CURRENT.md updated with expanded benchmark sections

Key honest findings:

1. **Signal generalizes to diverse AMP classes.** AUROC 0.7792 on 500 AMPs from
UniProt and APD6 is nearly identical to 0.7832 on 95 manually curated AMPs.
The pipeline is not overfit to the small curated set.

2. **CIs are dramatically tighter.** Cluster-aware CI width drops from 0.146
(n=191) to 0.064 (n=1000). This is the statistical benefit of a larger
benchmark — the pipeline's true discriminative power is now constrained
to a ±0.03 band rather than ±0.07.

3. **Representative AUROC parity.** On n=191, the representative-only AUROC
(0.7607) was notably lower than the full AUROC (0.7832), indicating
near-duplicate inflation. On n=1000, representative AUROC (0.778) nearly
equals the full AUROC (0.7792) — the expanded set's 500 sequences have
lower redundancy.

4. **Limitation:** The expanded set's lower recall@k (2% vs 10.5% at k=10) is
an honest effect of triage on a larger, harder pool. With 500 decoys
(vs 96), the top-10 threshold is more competitive. The pipeline correctly
ranks many AMPs in the top 20–40% rather than the top 1–2%.

## v0.5.28 — Multi-Negative-Set Benchmark ✓ (2026-07-05)

The pipeline's AUROC (0.78 on AMP-vs-decoy) was measured against a single
Expand Down
Loading
Loading