* 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.
276 lines
10 KiB
Python
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)
|