Source code for pen_assemble.pen_score
"""
PenScore: composite scoring formula for IS110-family bridge recombinase designs.
The PenScore (Programmable Editor Nomination score) is a weighted linear combination
of seven mechanistic axes calibrated for the human_therapeutic_aav_insertion use-case.
References
----------
- PEN-ASSEMBLE v0.5.0 documented methodology
- IS621 published lockpoint: pen_score = 0.929 (verbatim pre-registered)
- MHCflurry 2.2.1-calibrated lockpoint: 0.9255 (secondary analysis)
"""
from __future__ import annotations
from dataclasses import dataclass
__all__ = [
"WEIGHTS",
"IS621_LOCKPOINT",
"IS621_LOCKPOINT_CALIBRATED",
"PenScoreAxes",
"pen_score",
"beats_is621",
]
# ---------------------------------------------------------------------------
# Constants
# ---------------------------------------------------------------------------
#: Axis weights for human_therapeutic_aav_insertion use-case.
WEIGHTS: dict[str, float] = {
"S_DSB": 0.25,
"S_Spec": 0.10,
"S_Cargo": 0.20,
"S_Deliv": 0.15,
"S_Immuno": 0.10,
"S_Prog": 0.15,
"S_Mature": 0.05,
}
#: Verbatim pre-registered IS621 lockpoint (primary threshold).
IS621_LOCKPOINT: float = 0.929
#: MHCflurry 2.2.1-calibrated lockpoint (secondary analysis only).
IS621_LOCKPOINT_CALIBRATED: float = 0.9255
# ---------------------------------------------------------------------------
# Data class for axis scores
# ---------------------------------------------------------------------------
[docs]
@dataclass
class PenScoreAxes:
"""Container for the seven PenScore axis scores.
All axes are in [0, 1]. Missing values (``None``) are treated as 0.0
when computing the composite score.
Parameters
----------
S_DSB:
Double-strand break induction score.
S_Spec:
Target-site specificity score.
S_Cargo:
Payload compatibility score (IS110-family = 1.0 by mechanism).
S_Deliv:
Delivery suitability score (AAV packaging compatibility).
S_Immuno:
De-immunization score (1 - normalised predicted immunogenicity).
S_Prog:
Programmability score (bRNA re-targeting tractability).
S_Mature:
Maturity / technology-readiness score.
"""
S_DSB: float = 0.0
S_Spec: float = 0.0
S_Cargo: float = 0.0
S_Deliv: float = 0.0
S_Immuno: float = 0.0
S_Prog: float = 0.0
S_Mature: float = 0.0
[docs]
def as_dict(self) -> dict[str, float]:
"""Return axes as a plain dict."""
return {
"S_DSB": self.S_DSB,
"S_Spec": self.S_Spec,
"S_Cargo": self.S_Cargo,
"S_Deliv": self.S_Deliv,
"S_Immuno": self.S_Immuno,
"S_Prog": self.S_Prog,
"S_Mature": self.S_Mature,
}
[docs]
def contributions(self) -> dict[str, float]:
"""Return per-axis weighted contributions."""
return {ax: val * WEIGHTS[ax] for ax, val in self.as_dict().items()}
# ---------------------------------------------------------------------------
# Core formula
# ---------------------------------------------------------------------------
[docs]
def pen_score(axes: PenScoreAxes) -> float:
"""Compute the composite PenScore for a single design.
Parameters
----------
axes:
Seven axis scores packaged as a :class:`PenScoreAxes` instance.
Returns
-------
float
Composite PenScore in [0, 1].
Examples
--------
>>> from pen_assemble.pen_score import pen_score, PenScoreAxes
>>> ax = PenScoreAxes(S_DSB=1.0, S_Spec=1.0, S_Cargo=1.0,
... S_Deliv=1.0, S_Immuno=0.8777, S_Prog=1.0, S_Mature=0.5)
>>> round(pen_score(ax), 4)
0.9628
"""
total = sum(getattr(axes, ax) * w for ax, w in WEIGHTS.items())
return float(total)
[docs]
def beats_is621(score: float, calibrated: bool = False) -> bool:
"""Return True if *score* exceeds the IS621 reference lockpoint.
Parameters
----------
score:
A PenScore value.
calibrated:
If ``True``, compare against the MHCflurry 2.2.1-calibrated lockpoint
(0.9255). Default is ``False`` (verbatim pre-registered 0.929).
Returns
-------
bool
Examples
--------
>>> from pen_assemble.pen_score import beats_is621
>>> beats_is621(0.935)
True
>>> beats_is621(0.928)
False
>>> beats_is621(0.928, calibrated=True)
True
"""
threshold = IS621_LOCKPOINT_CALIBRATED if calibrated else IS621_LOCKPOINT
return score > threshold