Skip to content

Commit 810bfab

Browse files
committed
Use mhctools.best_direction; bump floor to 3.14.1
mhctools 3.14.1 ships ``best_direction(kind, field)`` (openvax/mhctools#212, closes #211). Drop topiary's local ``_BEST_FIELD_DIRECTIONS`` / ``_BEST_VALUE_DIRECTIONS`` / ``_best_direction`` and import the canonical helper instead — the direction table is mhctools-canonical metadata and shouldn't live in two places. - ``topiary/ranking/nodes.py``: replace the local table + helper with ``from mhctools import best_direction as _best_direction``. - ``requirements.txt``: bump mhctools floor 3.13.7 → 3.14.1. - ``tests/test_best_allele.py``: match the upstream error message ("best_direction undefined" instead of "best_value direction is undefined"). - Bump topiary 5.12.0 → 5.13.0 (additive minor — the mhctools floor bump tightens deps, but no public API change).
1 parent e7436d8 commit 810bfab

4 files changed

Lines changed: 11 additions & 49 deletions

File tree

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
numpy>=2.0.0,<3.0.0
22
pandas>=2.0.0
3-
mhctools>=3.13.7
3+
mhctools>=3.14.1
44
varcode>=0.3.17
55
gtfparse>=0.0.4
66
mhcnames

tests/test_best_allele.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,8 @@ def test_value_undefined_for_presentation_raises(self, presentation_df):
234234
# value direction is kind-dependent and not registered for
235235
# pMHC_presentation. Asking for best_value should raise loudly.
236236
ctx = EvalContext(presentation_df)
237-
with pytest.raises(ValueError, match="best_value direction is undefined"):
237+
# Error message comes from mhctools.best_direction.
238+
with pytest.raises(ValueError, match="best_direction undefined"):
238239
Presentation["mhcflurry"].best_value.eval(ctx)
239240

240241
def test_affinity_best_value_is_min(self):

topiary/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
from .result import TopiaryResult, concat
5454
from .wide import detect_form, from_wide, to_wide
5555

56-
__version__ = "5.12.0"
56+
__version__ = "5.13.0"
5757

5858
__all__ = [
5959
"TopiaryPredictor",

topiary/ranking/nodes.py

Lines changed: 7 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -723,55 +723,16 @@ def to_ast_string(self):
723723
# (Scoped fields cannot appear in filters — guarded in Comparison.__init__)
724724

725725

726-
# Direction conventions: which way is "best" for a given (kind, field).
727-
# - ``score``: higher is better, every kind (binding strength,
728-
# presentation likelihood, immunogenicity, processing).
729-
# - ``percentile_rank``: 0 means best — min is better, every kind.
730-
# - ``value``: kind-dependent — pMHC_affinity uses IC50 nM (lower
731-
# better); pMHC_stability would use half-life in hours (higher
732-
# better). Required entry per kind.
733-
_BEST_FIELD_DIRECTIONS = {
734-
"score": "max",
735-
"percentile_rank": "min",
736-
}
737-
738-
_BEST_VALUE_DIRECTIONS = {
739-
"pMHC_affinity": "min", # IC50 nM
740-
"pMHC_stability": "max", # half-life
741-
}
726+
# Canonical "best direction" per (kind, field) lives upstream in
727+
# mhctools (since 3.14.0) so consumers don't replicate the table —
728+
# see openvax/mhctools#211.
729+
from mhctools import best_direction as _best_direction # noqa: E402
742730

743731

744732
def _field_short(field: str) -> str:
745733
return "rank" if field == "percentile_rank" else field
746734

747735

748-
def _best_direction(kind, field):
749-
"""Return ``"max"`` or ``"min"`` for a (kind, field) pair.
750-
751-
Raises ``ValueError`` for ``field='value'`` on a kind without a
752-
registered direction — `value` semantics are kind-dependent and we
753-
refuse to guess. New `value`-bearing kinds need an entry in
754-
:data:`_BEST_VALUE_DIRECTIONS`.
755-
"""
756-
direction = _BEST_FIELD_DIRECTIONS.get(field)
757-
if direction is not None:
758-
return direction
759-
if field == "value":
760-
kind_name = _kind_name(kind)
761-
if kind_name not in _BEST_VALUE_DIRECTIONS:
762-
raise ValueError(
763-
f"best_value direction is undefined for kind "
764-
f"{kind_name!r} — `value` semantics depend on the kind "
765-
f"(IC50 vs half-life vs ...). Add an entry to "
766-
f"topiary.ranking.nodes._BEST_VALUE_DIRECTIONS."
767-
)
768-
return _BEST_VALUE_DIRECTIONS[kind_name]
769-
raise ValueError(
770-
f"No best-direction defined for field {field!r}. "
771-
f"Known: {sorted(_BEST_FIELD_DIRECTIONS) + ['value']}."
772-
)
773-
774-
775736
class BestAlleleField(DSLNode):
776737
"""Per-peptide aggregation of a (kind, field) value across alleles.
777738
@@ -781,9 +742,9 @@ class BestAlleleField(DSLNode):
781742
(``return_allele=True``) to every per-(peptide, allele) entry in
782743
``ctx.group_index``. Composes naturally with the rest of the DSL.
783744
784-
Direction is taken from :func:`_best_direction`: ``score`` is max,
785-
``percentile_rank`` is min, ``value`` is per-kind (IC50 → min,
786-
half-life → max).
745+
Direction is taken from :func:`mhctools.best_direction`: ``score``
746+
is max, ``percentile_rank`` is min, ``value`` is per-kind (IC50 →
747+
min, half-life → max).
787748
788749
**Semantics depend on the upstream predictor's allele mode.** With
789750
mhctools >=3.13.7 these are reported via ``predictor.kind_support()``:

0 commit comments

Comments
 (0)