Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/changelog.d/974-utility-control-boundary.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Selection utility numeric trust boundary

## Fixed

- Hardened classical selection-utility and Taylor-Russell scalar controls so booleans, non-real objects, and non-finite values fail with package-owned validation before compiled Rust discovery.
- Prevented arbitrary caller-defined `__float__` callbacks from executing during public control marshalling while preserving genuine Python/NumPy real scalar compatibility and keeping all BCG, Naylor-Shine, and Taylor-Russell arithmetic Rust-owned.
53 changes: 50 additions & 3 deletions python/fast_mlsirm/utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,29 @@

from __future__ import annotations

import math
from dataclasses import dataclass

import numpy as np


_TRUSTED_REAL_SCALAR_TYPES = (
int,
float,
np.int8,
np.int16,
np.int32,
np.int64,
np.uint8,
np.uint16,
np.uint32,
np.uint64,
np.float16,
np.float32,
np.float64,
np.longdouble,
)


@dataclass
class SelectionUtilityResult:
Expand All @@ -46,7 +67,7 @@ class SelectionUtilityResult:
``xc`` is the standard-normal predictor cutoff ``Phi^-1(1 - sr)``;
``ux`` the selection intensity ``phi(xc)/sr`` (mean standardized
predictor of those selected); ``pux = rxy * ux`` the Naylor-Shine mean
standardized criterion of those selected; ``utility_gain`` the BCG
standardized criterion of those selected); ``utility_gain`` the BCG
gain ``n * period * sdy * pux - cost_total``."""

xc: float
Expand All @@ -67,6 +88,16 @@ class TaylorRussellResult:
q_joint: float


def _coerce_finite_real(value: object, *, name: str) -> float:
"""Return a trusted finite real scalar without invoking caller protocols."""
if type(value) not in _TRUSTED_REAL_SCALAR_TYPES:
raise ValueError(f"{name} must be a finite real number")
marshaled = float(value)
if not math.isfinite(marshaled):
raise ValueError(f"{name} must be a finite real number")
return marshaled
Comment thread
seonghobae marked this conversation as resolved.


def selection_utility(
n: float,
sdy: float,
Expand All @@ -84,10 +115,22 @@ def selection_utility(
applicant" but never multiplies by ``n``; we document the actual
semantics), ``period`` expected tenure (>= 1).
"""
marshaled_n = _coerce_finite_real(n, name="n")
marshaled_sdy = _coerce_finite_real(sdy, name="sdy")
marshaled_rxy = _coerce_finite_real(rxy, name="rxy")
marshaled_sr = _coerce_finite_real(sr, name="sr")
marshaled_cost_total = _coerce_finite_real(cost_total, name="cost_total")
marshaled_period = _coerce_finite_real(period, name="period")

from . import _core

r = _core.selection_utility(
float(n), float(sdy), float(rxy), float(sr), float(cost_total), float(period)
marshaled_n,
marshaled_sdy,
marshaled_rxy,
marshaled_sr,
marshaled_cost_total,
marshaled_period,
)
return SelectionUtilityResult(
xc=r["xc"], ux=r["ux"], pux=r["pux"], utility_gain=r["utility_gain"]
Expand All @@ -101,9 +144,13 @@ def taylor_russell(rxy: float, sr: float, br: float) -> TaylorRussellResult:
base rate of success in (0, 1). At ``rxy = 0`` the success ratio
equals ``br`` (no selection information).
"""
marshaled_rxy = _coerce_finite_real(rxy, name="rxy")
marshaled_sr = _coerce_finite_real(sr, name="sr")
marshaled_br = _coerce_finite_real(br, name="br")

from . import _core

r = _core.taylor_russell(float(rxy), float(sr), float(br))
r = _core.taylor_russell(marshaled_rxy, marshaled_sr, marshaled_br)
return TaylorRussellResult(
success_ratio=r["success_ratio"],
base_rate=r["base_rate"],
Expand Down
93 changes: 93 additions & 0 deletions tests/test_utility_control_boundary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"""Trust-boundary regressions for classical selection utility controls."""

from __future__ import annotations

import math

import numpy as np
import pytest

from fast_mlsirm.utility import selection_utility, taylor_russell


class _HostileFloat:
"""Arbitrary float-protocol object whose callback must stay unreachable."""

calls = 0

@classmethod
def reset(cls) -> None:
"""Reset the callback counter."""
cls.calls = 0

def __float__(self) -> float:
type(self).calls += 1
raise AssertionError("caller __float__ callback must not execute")


class _HostileFloatSubclass(float):
"""Float subclass whose conversion callback must stay unreachable."""

calls = 0

@classmethod
def reset(cls) -> None:
"""Reset the callback counter."""
cls.calls = 0

def __float__(self) -> float:
type(self).calls += 1
raise AssertionError("caller float-subclass __float__ callback must not execute")


_UTILITY_CONTROL_CALLS = (
lambda value: selection_utility(value, 1.0, 0.5, 0.5),
lambda value: selection_utility(1.0, value, 0.5, 0.5),
lambda value: selection_utility(1.0, 1.0, value, 0.5),
lambda value: selection_utility(1.0, 1.0, 0.5, value),
lambda value: selection_utility(1.0, 1.0, 0.5, 0.5, value, 1.0),
lambda value: selection_utility(1.0, 1.0, 0.5, 0.5, 0.0, value),
lambda value: taylor_russell(value, 0.5, 0.5),
lambda value: taylor_russell(0.4, value, 0.5),
lambda value: taylor_russell(0.4, 0.5, value),
)


@pytest.mark.parametrize("call", _UTILITY_CONTROL_CALLS)
@pytest.mark.parametrize("hostile_type", (_HostileFloat, _HostileFloatSubclass))
def test_utility_rejects_float_protocol_objects_without_callbacks(
call, hostile_type
) -> None:
"""Every scalar position rejects hostile conversion without callback execution."""
hostile_type.reset()

with pytest.raises(ValueError, match="must be a finite real number"):
call(hostile_type())

assert hostile_type.calls == 0


@pytest.mark.parametrize("call", _UTILITY_CONTROL_CALLS)
@pytest.mark.parametrize("value", (True, False, math.inf, -math.inf, math.nan))
def test_utility_rejects_boolean_and_nonfinite_controls(call, value) -> None:
"""Every scalar position rejects boolean and non-finite control values."""
with pytest.raises(ValueError, match="must be a finite real number"):
call(value)


def test_numpy_real_scalars_preserve_native_results() -> None:
"""Trusted NumPy real scalars produce the same Rust-owned results as floats."""
py_utility = selection_utility(10.0, 2.0, 0.4, 0.5, 1.0, 2.0)
np_utility = selection_utility(
np.float64(10.0),
np.float64(2.0),
np.float64(0.4),
np.float64(0.5),
np.float64(1.0),
np.float64(2.0),
)
assert np_utility == py_utility

py_tr = taylor_russell(0.4, 0.5, 0.6)
np_tr = taylor_russell(np.float64(0.4), np.float64(0.5), np.float64(0.6))
assert np_tr == py_tr
Loading