core/evals/rate_oracle/signature.py
Shay 4c271f5e9f feat(rate): R3 RateProblem model + rate gold + setup oracle (R3b)
generate/rate_comprehension/model.py: RateProblem (quantity = rate × time, exactly one unknown == query; rate_unit fixes all three units by construction; structural guard refuses zero/two unknowns). evals/rate_oracle/: span-free signature, a 12-fixture gold (6 solved / 2 solver_refuses / 2... reader_refuses), closed expect+reason taxonomy, gold-validation runner + CLI. No reader yet — proves the ruler (12/12 valid).

Gold shape gives the 6/0/6 target: 6 single-rate solved (forward + both inverses, non-temporal 'per box' included), 2 non-integer-inverse solver refusals, 4 reader refusals (unit mismatch minutes-vs-hour, missing time, combined rates, clock-time temporal). Off-serving. 9 oracle + 10 unit tests; per-branch meaningful-fail.
2026-06-07 23:01:25 -07:00

36 lines
1.1 KiB
Python

"""Span-free canonical signature for the R3 rate setup oracle (R3b).
A deterministic, order-independent projection of a single-rate setup — the rate unit, the known
values keyed by role, and the query — used to compare a reader's comprehended ``RateProblem``
against the independent gold. The R3 twin of ``evals.setup_oracle.signature`` /
``evals.constraint_oracle.signature``. Pure, deterministic.
"""
from __future__ import annotations
from typing import Any
from generate.rate_comprehension.model import RateProblem
def rate_setup_signature(problem: RateProblem) -> dict[str, Any]:
"""Canonical ``(rate_unit, knowns, query)`` signature of a single-rate setup."""
knowns = tuple(
sorted(
(role, value)
for role, value in (
("rate", problem.rate),
("time", problem.time),
("quantity", problem.quantity),
)
if value is not None
)
)
return {
"rate_unit": (problem.rate_unit.numerator, problem.rate_unit.denominator),
"knowns": knowns,
"query": problem.query,
}
__all__ = ["rate_setup_signature"]