Source code for tabench.core.budget
"""Budgets count work in hardware-free coordinates (P6).
Wall-clock is always recorded but never used as the primary ranking axis.
"""
from __future__ import annotations
from dataclasses import dataclass
__all__ = ["Budget", "BudgetCoords"]
[docs]
@dataclass(frozen=True)
class BudgetCoords:
"""Where a checkpoint sits in budget space. All coordinates recorded (P6)."""
iterations: int = 0
sp_calls: int = 0
wall_ms: float = 0.0
[docs]
@dataclass(frozen=True)
class Budget:
"""Resource limits for one solve. ``None`` means unconstrained on that axis.
``target_relative_gap`` is an optional convergence early-stop, not a
resource axis: solvers that monitor their own convergence measure MAY
stop once it is reached (the certified metrics are still recomputed by
the harness). For UE solvers the measure is the relative gap; for the
SUE solver it is the fixed-point residual (ADR-001). Boyce,
Ralevic-Dekic & Bar-Gera (2004) recommend RG <= 1e-4 for stable link
flows; a resource axis is still required so that non-converging models
(and single-shot models, which ignore the target) terminate.
"""
iterations: int | None = None
sp_calls: int | None = None
wall_seconds: float | None = None
target_relative_gap: float | None = None
def __post_init__(self) -> None:
if self.iterations is None and self.sp_calls is None and self.wall_seconds is None:
raise ValueError(
"Budget must constrain at least one resource axis "
"(target_relative_gap alone cannot bound a non-converging model)"
)
def exhausted(self, coords: BudgetCoords) -> bool:
if self.iterations is not None and coords.iterations >= self.iterations:
return True
if self.sp_calls is not None and coords.sp_calls >= self.sp_calls:
return True
if self.wall_seconds is not None and coords.wall_ms >= 1000.0 * self.wall_seconds:
return True
return False
[docs]
def target_met(self, relative_gap: float | None) -> bool:
"""True when a self-monitored relative gap satisfies the target."""
return (
self.target_relative_gap is not None
and relative_gap is not None
and relative_gap <= self.target_relative_gap
)