Source code for rbfenetmap.core.meta.scorers

"""The scorer contract: reduce edge descriptors to a scalar cost."""

from __future__ import annotations

from abc import ABC, abstractmethod
from typing import ClassVar, Mapping, Sequence

from rbfenetmap.core.models import EdgeScore, RejectionReason

__all__ = ("AbstractScorer",)


[docs] class AbstractScorer(ABC): """Turn precomputed edge descriptors into a cost. Lower is better. Notes ----- A scorer receives a plain ``Mapping[str, float]`` and nothing else -- no molecules, no mapping object, no RDKit. Descriptors are computed once, centrally, by :func:`rbfenetmap.core.descriptors.compute_descriptors`. That narrow interface buys three things. Re-scoring a network under different weights costs nothing, because no mapping has to be recomputed. A scorer can be tested against hand-written dictionaries, with no chemistry in the test at all. And a third-party scorer cannot accidentally reach past its inputs and reintroduce a dependency on how the mapping was produced. A scorer must not invent rejections. Feasibility is decided upstream by the mapper and the repair; *rejections* is passed in so the scorer can propagate it into the returned :class:`~rbfenetmap.core.models.EdgeScore`, not so it can add to it. A scorer that wants to express "this edge is terrible" returns a large finite cost -- which leaves the planner free to use it anyway if the alternative is a disconnected network. """ name: ClassVar[str] = "abstract"
[docs] @abstractmethod def score_edge(self, descriptors: Mapping[str, float], *, rejections: Sequence[RejectionReason]) -> EdgeScore: """Return the cost of an edge described by *descriptors*. Parameters ---------- descriptors : Mapping[str, float] Raw descriptor values from :func:`rbfenetmap.core.descriptors.compute_descriptors`. rejections : Sequence[RejectionReason] Structural rejections already determined upstream. Non-empty means the implementation must return ``EdgeScore.rejected(*rejections, scorer=self.name)``. Returns ------- EdgeScore """
[docs] def describe_weights(self) -> Mapping[str, float]: """Return the scorer's tunable weights, for display by ``rbfenet score``.""" return {}
def __repr__(self) -> str: # pragma: no cover - trivial return f"{type(self).__name__}(name={self.name!r})"