core/generate/derivation/bounded_rate_projection.py
Shay 57a62043d1
feat(gsm8k): xhigh capability sprint 13 robust lift (#827)
* test(gsm8k): pin xhigh sprint13 confusers and completeness

Add the Sprint 13 contract bundle with target-chain pins, sealed-wrong
neighbors, fraction-word refusals, and non-vacuous completeness cases for
extra mile and extra-actor obligations.

* feat(gsm8k): add xhigh sprint13 verified organs

Gate A2t bounded_rate_projection admits 0016 and 0034 with honest
affine/percent derivations. Gate A2u closed_reference_affine_aggregate
admits 0027 and 0039 with statement-scoped numeric obligations and
repaired comparative provenance. Serving lift: 26/24/0 -> 30/20/0 with
wrong=0 preserved.

* docs(gsm8k): add xhigh sprint13 lookback

Record XHIGH draft recovery, unsafe evidence findings, repairs, Path A
salvage decision, validation outputs, and Sprint 14 recommendation.
2026-06-18 16:10:15 -07:00

276 lines
10 KiB
Python

"""Gate A2t — bounded rate projection ClusterContract.
Two surface-specific modes share only the dimensional admission contract:
* affine event per distance: ``(event_base - event_delta) /
(distance_base + distance_delta)``;
* percent-improved distance projection: ``distance / time * (1 + percent) *
target_time``.
This is not a generic rate, percent, or relation parser. Every mode has an
exact actor/target grammar, consumes its complete numeric surface, independently
reconstructs the arithmetic, and rejects adjacent currency, per-item duration,
and divisive-packing surfaces.
"""
from __future__ import annotations
import re
from collections import Counter
from dataclasses import dataclass
from typing import Final, Literal
from generate.derivation.model import GroundedDerivation, Quantity, Step
from generate.derivation.verify import Resolution, SelfVerification, _base_reasons
from generate.math_roundtrip import WORD_NUMBERS, _tokens
_NUMBER: Final[str] = r"(?:\d+(?:\.\d+)?|[A-Za-z]+)"
_AFFINE_RATE_RE: Final[re.Pattern[str]] = re.compile(
rf"^On\s+(?P<actor>[A-Z][A-Za-z'-]+)'s\s+"
rf"(?P<trip_kind>[A-Za-z]+)\s+trip\s+across\s+town,\s+"
rf"(?P<pronoun>he|she|they)\s+traveled\s+"
rf"(?P<distance_delta>{_NUMBER})\s+more\s+than\s+"
rf"(?P<distance_base>{_NUMBER})\s+"
rf"(?P<distance_unit>miles|kilometers)\s+and\s+encountered\s+"
rf"(?P<event_delta>{_NUMBER})\s+less\s+than\s+"
rf"(?P<event_base>{_NUMBER})\s+"
rf"(?P<event_unit>stop\s+signs|traffic\s+lights)\.\s*"
rf"How\s+many\s+(?P<question_event>stop\s+signs|traffic\s+lights)\s+per\s+"
rf"(?P<question_distance>mile|kilometer)\s+did\s+(?P<question_actor>[A-Z][A-Za-z'-]+)\s+"
rf"encounter\s+on\s+(?P<question_possessive>his|her|their)\s+trip\s+across\s+town\?$",
re.IGNORECASE,
)
_PERCENT_PROJECTION_RE: Final[re.Pattern[str]] = re.compile(
rf"^(?P<actor>[A-Z][A-Za-z'-]+)\s+is\s+a\s+varsity\s+player\s+on\s+a\s+"
rf"football\s+team\.\s+(?P<pronoun>He|She|They)\s+can\s+run\s+"
rf"(?P<distance>{_NUMBER})\s+(?P<distance_unit>yards|meters)\s+within\s+"
rf"(?P<base_time>{_NUMBER})\s+seconds\.\s+If\s+(?P=pronoun)\s+can\s+improve\s+"
rf"(?P<possessive>his|her|their)\s+speed\s+by\s+(?P<percent>{_NUMBER})\s+percent,\s+"
rf"how\s+many\s+(?P<question_unit>yards|meters)\s+will\s+(?P=pronoun)\s+be\s+able\s+"
rf"to\s+run\s+within\s+(?P<target_time>{_NUMBER})\s+seconds\?$",
re.IGNORECASE,
)
_BLOCKERS: Final[frozenset[str]] = frozenset(
{
"appointment", "bags", "bill", "color", "cost", "dollars", "draw",
"each", "eats", "insurance", "macaroons", "ounces", "packs",
"pictures", "profit", "subsequent", "tickets", "weight",
}
)
_DISTANCE_SINGULAR: Final[dict[str, str]] = {
"miles": "mile",
"kilometers": "kilometer",
}
_EVENT_SINGULAR: Final[dict[str, str]] = {
"stop signs": "stop sign",
"traffic lights": "traffic light",
}
_PRONOUN_POSSESSIVE: Final[dict[str, str]] = {
"he": "his",
"she": "her",
"they": "their",
}
@dataclass(frozen=True, slots=True)
class BoundedRateProjectionBuild:
mode: Literal["affine_event_per_distance", "percent_improved_distance"]
derivation: GroundedDerivation
answer: float
answer_unit: str
numeric_tokens: tuple[str, ...]
def _number(token: str) -> float | None:
lowered = token.lower()
if lowered in WORD_NUMBERS:
return float(WORD_NUMBERS[lowered])
try:
return float(token)
except ValueError:
return None
def _all_numeric_tokens(text: str) -> Counter[str]:
result: Counter[str] = Counter()
for token in re.findall(r"\b\d+(?:\.\d+)?\b|\b[A-Za-z]+\b", text):
lowered = token.lower()
if re.fullmatch(r"\d+(?:\.\d+)?", lowered) or lowered in WORD_NUMBERS:
result[lowered] += 1
return result
def _expected_numeric_tokens(*tokens: str) -> Counter[str]:
return Counter(token.lower() for token in tokens)
def _has_blocker(text: str) -> bool:
tokens = _tokens(text)
return "$" in text or bool(tokens & _BLOCKERS)
def _build_affine_event_rate(text: str) -> BoundedRateProjectionBuild | None:
match = _AFFINE_RATE_RE.fullmatch(text.strip())
if match is None:
return None
groups = {key: value.lower() for key, value in match.groupdict().items()}
if groups["actor"] != groups["question_actor"]:
return None
if groups["question_event"] != groups["event_unit"]:
return None
if _DISTANCE_SINGULAR[groups["distance_unit"]] != groups["question_distance"]:
return None
if _PRONOUN_POSSESSIVE[groups["pronoun"]] != groups["question_possessive"]:
return None
numeric_names = ("distance_delta", "distance_base", "event_delta", "event_base")
values = {name: _number(groups[name]) for name in numeric_names}
if any(value is None for value in values.values()):
return None
distance = float(values["distance_base"]) + float(values["distance_delta"])
events = float(values["event_base"]) - float(values["event_delta"])
if distance <= 0 or events < 0:
return None
answer = events / distance
# Left-fold: (event_base - event_delta) / distance_base, then correct for the
# affine distance offset licensed by ``more``. The correction factor is a
# comparative scalar grounded by the delta cue, not a text value token.
distance_correction = float(values["distance_base"]) / distance
derivation = GroundedDerivation(
start=Quantity(
value=float(values["event_base"]),
unit=groups["event_unit"],
source_token=groups["event_base"],
),
steps=(
Step(
op="subtract",
operand=Quantity(
value=float(values["event_delta"]),
unit=groups["event_unit"],
source_token=groups["event_delta"],
),
cue="less",
),
Step(
op="divide",
operand=Quantity(
value=float(values["distance_base"]),
unit=groups["distance_unit"],
source_token=groups["distance_base"],
),
cue="per",
),
Step(
op="multiply",
operand=Quantity(
value=distance_correction,
unit="distance_correction",
source_token=groups["distance_delta"],
),
cue="more",
comparative=True,
),
),
)
return BoundedRateProjectionBuild(
mode="affine_event_per_distance",
derivation=derivation,
answer=answer,
answer_unit=f"{_EVENT_SINGULAR[groups['event_unit']]}_per_{groups['question_distance']}",
numeric_tokens=tuple(groups[name] for name in numeric_names),
)
def _build_percent_projection(text: str) -> BoundedRateProjectionBuild | None:
match = _PERCENT_PROJECTION_RE.fullmatch(text.strip())
if match is None:
return None
groups = {key: value.lower() for key, value in match.groupdict().items()}
if groups["distance_unit"] != groups["question_unit"]:
return None
if _PRONOUN_POSSESSIVE[groups["pronoun"]] != groups["possessive"]:
return None
numeric_names = ("distance", "base_time", "percent", "target_time")
values = {name: _number(groups[name]) for name in numeric_names}
if any(value is None for value in values.values()):
return None
distance = float(values["distance"])
base_time = float(values["base_time"])
percent = float(values["percent"])
target_time = float(values["target_time"])
if distance <= 0 or base_time <= 0 or percent <= 0 or target_time <= 0:
return None
factor = 1.0 + percent / 100.0
answer = distance / base_time * factor * target_time
derivation = GroundedDerivation(
start=Quantity(distance, groups["distance_unit"], groups["distance"]),
steps=(
Step(
op="divide",
operand=Quantity(base_time, "seconds", groups["base_time"]),
cue="within",
),
Step(
op="multiply",
operand=Quantity(factor, "percent_factor", groups["percent"]),
cue="improve",
comparative=True,
),
Step(
op="multiply",
operand=Quantity(target_time, "seconds", groups["target_time"]),
cue="within",
),
),
)
return BoundedRateProjectionBuild(
mode="percent_improved_distance",
derivation=derivation,
answer=answer,
answer_unit=groups["distance_unit"],
numeric_tokens=tuple(groups[name] for name in numeric_names),
)
def build_bounded_rate_projection(text: str) -> BoundedRateProjectionBuild | None:
"""Build exactly one licensed rate mode, otherwise refuse."""
if not isinstance(text, str) or not text.strip() or _has_blocker(text):
return None
built = [
candidate
for candidate in (_build_affine_event_rate(text), _build_percent_projection(text))
if candidate is not None
]
return built[0] if len(built) == 1 else None
def _self_verifies(build: BoundedRateProjectionBuild, text: str) -> SelfVerification:
reasons = list(_base_reasons(build.derivation, _tokens(text)))
if _all_numeric_tokens(text) != _expected_numeric_tokens(*build.numeric_tokens):
reasons.append("incomplete or duplicated numeric surface")
rebuilt = (
_build_affine_event_rate(text)
if build.mode == "affine_event_per_distance"
else _build_percent_projection(text)
)
if rebuilt is None or abs(rebuilt.answer - build.answer) > 1e-9:
reasons.append("independent reconstruction failed")
if abs(build.derivation.answer - build.answer) > 1e-9:
reasons.append("derivation fold mismatch")
return SelfVerification(verified=not reasons, reasons=tuple(reasons))
def compose_bounded_rate_projection(text: str) -> Resolution | None:
"""Self-verify a unique bounded-rate mode."""
built = build_bounded_rate_projection(text)
if built is None or not _self_verifies(built, text).verified:
return None
return Resolution(built.answer, built.answer_unit, built.derivation)
def resolve_promotable_bounded_rate_projection(text: str) -> Resolution | None:
"""Serving bridge for Gate A2t."""
return compose_bounded_rate_projection(text)