Skip to content

test: proline Boman edge cases + ZwitterAMP trap end-to-end scorer divergence - #29

Merged
cschanhniem merged 2 commits into
mainfrom
feat/scorer-divergence-proline-tests
Jun 28, 2026
Merged

cschanhniem merged 2 commits into
mainfrom
feat/scorer-divergence-proline-tests

Conversation

@cschanhniem

Copy link
Copy Markdown
Collaborator

Summary

Two gaps closed before synthesis ordering:

1. Proline edge case (3 tests in test_boman_scorer.py)

Proline (P) has Boman potential 0.0 — identical to glycine (G). This means:

  • boman_index("PPPPPPPP") = 0.0
  • boman_activity("PPPPPPPP") ≈ 0.5 (neutral midpoint)

No test previously covered this. A future edit to the _BOMAN_POTENTIALS table that accidentally gave proline a non-zero value would corrupt scores on proline-containing candidates (common in hairpin-forming AMP variants) without any test catching it.

2. ZwitterAMP trap — end-to-end scorer divergence (3 tests in test_pipeline_filters.py)

KDKDKDKD is a sequence designed to fool the Boman scorer in the worst possible way:

  • K and D each have the same Boman potential (+2.465) → boman_index = 2.465 → boman_activity ≈ 0.92
  • But K (+1) and D (-1) cancel → net charge = 0, no hydrophobicity
  • activity_likeness ≈ 0.19 (correctly identifies this as non-AMP)
  • disagreement = 0.73 — more than 2× the pipeline gate threshold (0.40)

Without the disagreement gate, this sequence could pass individual scorer thresholds and be nominated for a $10k synthesis slot. The three new tests verify:

  1. Both scorer values are computed correctly (activity < 0.25, boman_activity > 0.85)
  2. The full run_ranking_pipeline() call marks it selected: false with the default config
  3. PPPPPPPP correctly straddles the two config gates (passes pipeline 0.40, fails phase3 0.30)

Test plan

  • pytest tests/test_boman_scorer.py tests/test_pipeline_filters.py — 61 passed
  • Full suite: 821 passed
  • No regressions

…ce tests

Proline edge cases (test_boman_scorer.py):
- test_all_proline_returns_zero: P has Boman potential 0.0 → boman_index("PPPPPPPP")=0.0;
  matches G (both are conformational residues with no side-chain charge contribution)
- test_proline_same_as_glycine_in_boman: G and P share the same Boman table entry (0.0)
- test_all_proline_boman_activity_is_midpoint: boman_index=0 → tanh(0)=0 → score=0.5

ZwitterAMP trap (test_pipeline_filters.py):
- KDKDKDKD is designed to fool the Boman scorer: K and D each have Boman potential +2.465
  (highest table value), so boman_index=2.465 → boman_activity≈0.92. But net charge=0
  (K and D cancel) and no hydrophobicity, so activity_likeness≈0.19. Disagreement≈0.73.
- test_zwitteramp_trap_scorer_divergence: pins actual computed values (act<0.25, bom>0.85,
  dis>0.60) to verify the two scoring systems diverge as expected on this false positive
- test_zwitteramp_trap_blocked_by_pipeline: full run_ranking_pipeline integration test
  confirms selected=False for KDKDKDKD against the default pipeline.yaml gate (max_dis=0.40)
- test_all_proline_pipeline_scores_and_disagreement: PPPPPPPP has dis≈0.33, which passes
  pipeline.yaml gate (0.40) but would fail phase3.yaml gate (0.30); both config thresholds
  tested explicitly against live computed values

Why this matters for synthesis: these tests document that the two independent scorers serve
as cross-check, not just redundancy — a sequence that is systematically biased in one scoring
paradigm (all-polar, no-net-charge) still gets caught by the other scorer and by disagreement.
…ertions

- test_pipeline_filters.py: move compute_features, activity_likeness_score,
  boman_activity_score, model_disagreement to module-level imports (good practice even
  though PLC0415 is not enabled in this project's ruff config); remove redundant
  dis>0.40 / dis>0.30 assertions that were logically implied by dis>0.60
- test_boman_scorer.py: replace test_proline_same_as_glycine_in_boman (compared two
  computed values against each other — would survive an equal-but-wrong implementation)
  with test_proline_boman_index_is_zero_like_glycine asserting both against pytest.approx(0.0)
@cschanhniem
cschanhniem merged commit 9f2afb8 into main Jun 28, 2026
1 check passed
@cschanhniem
cschanhniem deleted the feat/scorer-divergence-proline-tests branch June 28, 2026 02:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant