refactor(policy): extract pure scoring and ranking functions

This commit is contained in:
0xrsydn 2026-09-23 14:17:38 +07:00
commit 5719faa05b

53
policy/scoring.py Normal file
View file

@ -0,0 +1,53 @@
"""Pure score arithmetic. No model types, game rules, I/O, or policy thresholds.
Invalid or incomplete inputs raise ValueError. Callers own evidence checks and
fallbacks; missing evidence must never become a neutral score.
"""
from __future__ import annotations
import math
from collections.abc import Mapping
def _finite(value: float) -> bool:
return isinstance(value, (int, float)) and not isinstance(value, bool) and math.isfinite(value)
def normalize_score(value: float, minimum: float, neutral: float, maximum: float) -> float:
"""Map an ordered scale to [-1, 1], with neutral at zero.
Each side is linear. Equal score intervals are a modeling assumption, not
a calibrated measure of game value. Values outside the scale are rejected.
"""
if not all(_finite(v) for v in (value, minimum, neutral, maximum)):
raise ValueError("score and scale must be finite numbers")
if not minimum < neutral < maximum:
raise ValueError("scale must satisfy minimum < neutral < maximum")
if not minimum <= value <= maximum:
raise ValueError("score is outside the scale")
span = maximum - neutral if value >= neutral else neutral - minimum
return (value - neutral) / span
def weighted_utility(components: Mapping[str, float], weights: Mapping[str, float]) -> float:
"""Combine complete normalized components using explicit nonnegative weights.
Weights must sum to one. Do not silently remove missing components or
renormalize weights. Confidence is not a component or a utility multiplier.
"""
if not weights or components.keys() != weights.keys():
raise ValueError("components must exactly match nonempty weights")
if any(not _finite(w) or w < 0 for w in weights.values()):
raise ValueError("weights must be finite and nonnegative")
if not math.isclose(sum(weights.values()), 1.0, rel_tol=0, abs_tol=1e-9):
raise ValueError("weights must sum to one")
if any(not _finite(v) or not -1 <= v <= 1 for v in components.values()):
raise ValueError("components must be finite normalized scores")
return sum(weights[axis] * components[axis] for axis in weights)
def rank_candidates(utilities: Mapping[str, float]) -> list[str]:
"""Rank highest utility first; ties retain input order. Empty input is valid."""
if any(not _finite(value) for value in utilities.values()):
raise ValueError("candidate utilities must be finite numbers")
return sorted(utilities, key=utilities.__getitem__, reverse=True)