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
3 changes: 3 additions & 0 deletions src/openamp_foundry/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from openamp_foundry.scoring.ensemble import ensemble_score, known_failure_modes, selection_reasons
from openamp_foundry.scoring.novelty import novelty_score
from openamp_foundry.scoring.safety import safety_score
from openamp_foundry.scoring.stability import serum_stability_score
from openamp_foundry.scoring.synthesis import synthesis_feasibility_score
from openamp_foundry.selection.diversity import greedy_diverse_select
from openamp_foundry.selection.pareto import rank_candidates
Expand Down Expand Up @@ -53,13 +54,15 @@ def score_candidates(
synth = synthesis_feasibility_score(features, valid_sequence=valid)
nov, nearest = novelty_score(candidate.sequence, references)
boman_act = boman_activity_score(candidate.sequence) if valid else 0.0
stability = serum_stability_score(features) if valid else 0.0
raw_scores = {
"activity": act,
"safety": safe,
"synthesis": synth,
"novelty": nov,
"boman_activity": boman_act,
"disagreement": model_disagreement(act, boman_act),
"serum_stability": stability,
}
raw_scores["ensemble"] = ensemble_score(raw_scores, weights)
item = ScoredCandidate(
Expand Down
11 changes: 7 additions & 4 deletions src/openamp_foundry/reports/pilot_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"safety",
"synthesis",
"novelty",
"serum_stability",
"pilot_priority",
]

Expand Down Expand Up @@ -48,6 +49,7 @@ def _f(key: str, fallback: float = 0.0) -> float:
"safety": round(_f("safety"), 4),
"synthesis": round(_f("synthesis"), 4),
"novelty": round(_f("novelty"), 4),
"serum_stability": round(_f("serum_stability"), 4),
"pilot_priority": round(_f("pilot_priority"), 4),
}

Expand Down Expand Up @@ -86,21 +88,22 @@ def write_pilot_markdown(panel: list[dict], path: str | Path, generated_at: str
"",
"## Selection method",
"",
"Priority score = `ensemble − 0.3 × disagreement` ",
"Priority score = `ensemble − 0.3 × disagreement + 0.05 × serum_stability` ",
"Rules: one representative per seed (highest priority), then remaining slots filled by priority rank.",
"Serum stability acts as a light tiebreaker — favours candidates with fewer interior K/R cleavage sites.",
"",
"## Candidates",
"",
"| # | ID | Sequence | Len | Seed | Ensemble | Activity | Boman | Disagree | Safety | Synth |",
"|--:|---|---|--:|---|---:|---:|---:|---:|---:|---:|",
"| # | ID | Sequence | Len | Seed | Ensemble | Activity | Boman | Disagree | Safety | Synth | SerumStab |",
"|--:|---|---|--:|---|---:|---:|---:|---:|---:|---:|---:|",
]

for c in panel:
r = _row(c)
lines.append(
f"| {r['pilot_rank']} | {r['candidate_id']} | `{r['sequence']}` | {r['length']} "
f"| {r['seed']} | {r['ensemble']} | {r['activity']} | {r['boman_activity']} "
f"| {r['disagreement']} | {r['safety']} | {r['synthesis']} |"
f"| {r['disagreement']} | {r['safety']} | {r['synthesis']} | {r['serum_stability']} |"
)

lines += [
Expand Down
9 changes: 7 additions & 2 deletions src/openamp_foundry/selection/pilot.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,15 @@ def _seed_from_source(source: str) -> str:


def _pilot_priority(scores: dict) -> float:
"""Higher is better: reward ensemble score, penalise scorer disagreement."""
"""Higher is better: reward ensemble, reward serum stability, penalise disagreement.

Serum stability is weighted lightly (0.05) so it acts as a tiebreaker without
overriding the ensemble score. A candidate with stability=1.0 gains at most +0.05.
"""
ensemble = scores.get("ensemble", 0.0)
disagreement = scores.get("disagreement", 0.5)
return round(ensemble - 0.3 * disagreement, 6)
stability = scores.get("serum_stability", 0.5)
return round(ensemble - 0.3 * disagreement + 0.05 * stability, 6)


def select_pilot_panel(
Expand Down
10 changes: 8 additions & 2 deletions tests/test_pilot_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,14 @@ def test_lower_disagreement_higher_priority(self):
assert p1 > p2

def test_missing_disagreement_defaults_to_0_5(self):
# stability defaults to 0.5 when absent; disagreement defaults to 0.5
p = _pilot_priority({"ensemble": 0.70})
assert p == pytest.approx(0.70 - 0.3 * 0.5, abs=1e-6)
assert p == pytest.approx(0.70 - 0.3 * 0.5 + 0.05 * 0.5, abs=1e-6)

def test_higher_stability_higher_priority(self):
p1 = _pilot_priority({"ensemble": 0.70, "disagreement": 0.10, "serum_stability": 1.0})
p2 = _pilot_priority({"ensemble": 0.70, "disagreement": 0.10, "serum_stability": 0.0})
assert p1 > p2


class TestSelectPilotPanel:
Expand Down Expand Up @@ -212,7 +218,7 @@ def test_correct_columns(self, tmp_path):
fields = reader.fieldnames
expected = {"pilot_rank", "candidate_id", "sequence", "length", "seed",
"ensemble", "activity", "boman_activity", "disagreement",
"safety", "synthesis", "novelty", "pilot_priority"}
"safety", "synthesis", "novelty", "serum_stability", "pilot_priority"}
assert set(fields) == expected

def test_row_count_matches_panel(self, tmp_path):
Expand Down
Loading