Files
Reverso/tests/test_scoring.py
Junior B. fd4591949c Week 3: CMap connectivity scoring engine + ranked candidates
Implement the matching engine (PLAN §6 Week 3):
- src/scoring.py: weighted-KS/GSEA enrichment, weighted connectivity
  score (WTCS, Lamb 2006 / Subramanian 2017), signed NCS normalization,
  rank_drugs, and a sickle-pathway mechanistic prior
- tests/test_scoring.py: real reference tests for the scorer (perfect
  reversal<null<mimic, same-sign->0, absent-gene invariance) + prior
- week3_scoring.py: score 300 drugs -> ranked_candidates_v1.csv with a
  raw ranking and a secondary mechanistic-prior-weighted ranking

Preliminary (formal recovery test is Week 4): hydroxyurea raw rank
40/300 (top 13%, just misses pre-registered top-10%), blended rank 7;
L-glutamine WTCS=0 (ambiguous). Notably anti-inflammatory SCD drugs
cluster in the raw top tier — the engine reverses the inflammation axis,
not the erythroid axis, traceable to the 12% landmark-overlap caveat.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 22:34:56 +02:00

127 lines
5.2 KiB
Python

"""Tests for the matching engine and provenance logic.
Connectivity tests (PLAN.md §6, Week 3 task 4) pin the weighted-KS scorer against hand-built
reference profiles. The tier-assignment tests pin the rules from PLAN.md §3 so the most
commercially important design decision can't silently drift.
"""
from __future__ import annotations
import pandas as pd
import pytest
from src.provenance import ConfidenceTier, assign_tier
class TestAssignTier:
"""Tier rules from PLAN.md §3."""
def test_measured_large_n_peer_reviewed_multi_source_is_tier_a(self):
assert (
assign_tier(
is_measured=True,
n_per_group=27,
peer_reviewed=True,
single_source=False,
)
== ConfidenceTier.A
)
def test_inferred_is_always_tier_c(self):
assert (
assign_tier(
is_measured=False,
n_per_group=1000,
peer_reviewed=True,
single_source=False,
)
== ConfidenceTier.C
)
@pytest.mark.parametrize(
"kwargs",
[
dict(is_measured=True, n_per_group=6, peer_reviewed=True, single_source=False),
dict(is_measured=True, n_per_group=27, peer_reviewed=False, single_source=False),
dict(is_measured=True, n_per_group=27, peer_reviewed=True, single_source=True),
dict(is_measured=True, n_per_group=None, peer_reviewed=True, single_source=False),
],
)
def test_measured_but_weak_evidence_is_tier_b(self, kwargs):
assert assign_tier(**kwargs) == ConfidenceTier.B
class TestConnectivityScore:
"""Reference checks for the weighted-KS connectivity score (PLAN §6 Week 3 task 4).
Query: up = {U1, U2}, down = {D1, D2}. We build drug profiles with a known relationship to
the query and assert the sign/ordering the CMap convention requires.
"""
UP = ["U1", "U2"]
DOWN = ["D1", "D2"]
@staticmethod
def _profile(values: dict[str, float]) -> pd.Series:
# 20 filler genes at ~0 so the query genes sit clearly at the extremes.
base = {f"N{i}": 0.01 * ((i % 5) - 2) for i in range(20)}
base.update(values)
return pd.Series(base)
def test_perfect_reversal_is_strongly_negative(self):
from src.scoring import connectivity_score
# Drug pushes disease-up genes DOWN (very negative) and disease-down genes UP (very
# positive) => reversal => negative connectivity.
prof = self._profile({"U1": -8, "U2": -7, "D1": 8, "D2": 7})
assert connectivity_score(self.UP, self.DOWN, prof) < -0.4
def test_perfect_mimic_is_strongly_positive(self):
from src.scoring import connectivity_score
prof = self._profile({"U1": 8, "U2": 7, "D1": -8, "D2": -7})
assert connectivity_score(self.UP, self.DOWN, prof) > 0.4
def test_reversal_beats_mimic_and_null(self):
from src.scoring import connectivity_score
rev = connectivity_score(self.UP, self.DOWN, self._profile({"U1": -8, "U2": -7, "D1": 8, "D2": 7}))
mimic = connectivity_score(self.UP, self.DOWN, self._profile({"U1": 8, "U2": 7, "D1": -8, "D2": -7}))
null = connectivity_score(self.UP, self.DOWN, self._profile({"U1": 0.2, "U2": -0.1, "D1": 0.1, "D2": -0.2}))
assert rev < null < mimic
assert abs(null) < abs(rev)
def test_same_sign_enrichment_returns_zero(self):
from src.scoring import connectivity_score
# Both up- and down-sets at the top => same-sign ES => ambiguous => 0 (WTCS rule).
prof = self._profile({"U1": 8, "U2": 7, "D1": 6, "D2": 5})
assert connectivity_score(self.UP, self.DOWN, prof) == 0.0
def test_genes_absent_from_profile_are_ignored(self):
from src.scoring import connectivity_score
prof = self._profile({"U1": -8, "U2": -7, "D1": 8, "D2": 7})
# Adding a query gene not in the profile must not change the score.
s1 = connectivity_score(self.UP, self.DOWN, prof)
s2 = connectivity_score(self.UP + ["NOT_IN_PROFILE"], self.DOWN, prof)
assert s1 == pytest.approx(s2)
class TestMechanisticPrior:
def test_counts_distinct_sickle_pathways(self):
from src.scoring import mechanistic_prior
# ribonucleotide reductase (hydroxyurea) -> hbf_epigenetic category.
assert mechanistic_prior(["Ribonucleoside-diphosphate reductase RR1"]) == 1.0
# DNMT (epigenetic) + hemoglobin -> two categories.
assert mechanistic_prior(["DNA (cytosine-5)-methyltransferase 1", "Hemoglobin subunit beta"]) == 2.0
assert mechanistic_prior([]) == 0.0
assert mechanistic_prior(["Some unrelated kinase"]) == 0.0
def test_rank_drugs_orders_by_reversal():
from src.scoring import rank_drugs
genes = ["U1", "U2", "D1", "D2"] + [f"N{i}" for i in range(10)]
base = {g: 0.0 for g in genes}
reverser = {**base, "U1": -8, "U2": -7, "D1": 8, "D2": 7}
mimic = {**base, "U1": 8, "U2": 7, "D1": -8, "D2": -7}
matrix = pd.DataFrame([reverser, mimic], index=["reverser", "mimic"])
ranked = rank_drugs(["U1", "U2"], ["D1", "D2"], matrix)
assert ranked.loc["reverser", "rank"] == 1
assert ranked.loc["reverser", "connectivity_score"] < ranked.loc["mimic", "connectivity_score"]