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
16 changes: 16 additions & 0 deletions tests/test_boman_scorer.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
"""
from __future__ import annotations

import math

import pytest

from openamp_foundry.scoring.boman import (
Expand Down Expand Up @@ -71,6 +73,11 @@ def test_mixed_canonical_and_unknown(self):
# "KB" → K(2.465) + B(0.0) → mean 1.2325
assert boman_index("KB") == pytest.approx(1.2325, abs=1e-4)

def test_all_non_canonical_returns_zero(self):
# Every residue maps to 0.0 via .get(aa, 0.0) → mean = 0.0
assert boman_index("XXX") == pytest.approx(0.0, abs=1e-4)
assert boman_index("BBBBB") == pytest.approx(0.0, abs=1e-4)


class TestBomanActivityScore:
def test_returns_between_zero_and_one(self):
Expand Down Expand Up @@ -104,6 +111,15 @@ def test_rounding(self):
score = boman_activity_score("KWK")
assert score == round(score, 4)

def test_tanh_formula_pinned_against_scorer(self):
# Pin the formula 0.5*(1+tanh(bi/2)) against the actual scorer output.
# K → boman_index = 2.465 (verified in TestBomanIndex::test_single_lysine)
# expected = 0.5 * (1 + tanh(2.465/2)) ≈ 0.9845
# If the divisor in the scorer were changed from 2.0 to 3.0 this test fails.
k_bi = boman_index("K")
expected = round(0.5 * (1.0 + math.tanh(k_bi / 2.0)), 4)
assert boman_activity_score("K") == pytest.approx(expected, abs=1e-4)


class TestGravyScore:
def test_empty_returns_zero(self):
Expand Down
1 change: 0 additions & 1 deletion tests/test_diversity.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"""Tests for src/openamp_foundry/analysis/diversity.py"""
import pytest
from openamp_foundry.analysis.diversity import (
cluster_panel,
diversity_stats,
Expand Down
32 changes: 31 additions & 1 deletion tests/test_pipeline_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
from __future__ import annotations

import json
from pathlib import Path


from openamp_foundry.config import load_config
from openamp_foundry.pipeline import _passes_length_filter, run_ranking_pipeline, score_candidates


Expand Down Expand Up @@ -127,3 +128,32 @@ def test_novelty_filter_excludes_near_duplicates(tmp_path):
# But should still be scored and appear in output
assert len(rows) == 1
assert rows[0]["scores"]["novelty"] == 0.0


def test_phase3_config_has_stricter_safety_filter_than_pipeline():
"""phase3.yaml max_safety_risk=0.40 (safety>=0.60) vs pipeline.yaml 0.70 (safety>=0.30).

A candidate with safety=0.50 passes pipeline.yaml but is blocked by phase3.yaml.
"""
repo_root = Path(__file__).parents[1]
pipeline_cfg = load_config(repo_root / "configs" / "pipeline.yaml")
phase3_cfg = load_config(repo_root / "configs" / "phase3.yaml")

pipeline_min_safety = 1.0 - float(pipeline_cfg["selection"]["max_safety_risk"])
phase3_min_safety = 1.0 - float(phase3_cfg["selection"]["max_safety_risk"])

# Phase3 must be stricter
assert phase3_min_safety > pipeline_min_safety

# Verify the expected concrete thresholds (max_safety_risk 0.70 → 0.30, 0.40 → 0.60)
assert abs(pipeline_min_safety - 0.30) < 0.01, (
f"pipeline.yaml min_safety expected ~0.30, got {pipeline_min_safety}"
)
assert abs(phase3_min_safety - 0.60) < 0.01, (
f"phase3.yaml min_safety expected ~0.60, got {phase3_min_safety}"
)

# A candidate with safety=0.50 straddles the two configs
candidate_safety = 0.50
assert candidate_safety >= pipeline_min_safety, "Should pass pipeline filter"
assert candidate_safety < phase3_min_safety, "Should fail phase3 filter"
Loading