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.
36 lines
1.1 KiB
Python
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"]
|