core/generate/combined_rate_comprehension/solver.py
Shay 2a7e94c011 feat(combined-rate): CMB-b — exact combined-rate solver (int-or-refuse)
solve_combined_rate(CombinedRateProblem) -> int | Refusal over effective_rate = rate_a +/- rate_b.
effective_rate query returns the net (even <=0); quantity/time queries need a positive net rate
(non_positive_net_rate) and exact division (non_integer_solution). Pure integer arithmetic, no
float/Fraction (CMB v1 crosses no units). Off-serving. Graded by a new run_solver lane in
evals/combined_rate_oracle against the committed gold.

Adversarial 5-lens(+adjudicator) verification returned fix_first; both real hazards fixed:
- wrong=0 breach: negative known-slot inputs (time<0/quantity<0) bypassed the eff<=0 guard and
  produced negative answers. FIXED UPSTREAM in model.py __post_init__ — rate_a/rate_b and the known
  time/quantity are now positive ints, so the illegal state is unrepresentable and the solver can
  never receive a negative-yielding path. Added model + gold (cmb-07d eff<0/time) + lane coverage.
- doc overclaim: the solver lane does NOT grade 'two independent paths' (both solver and the
  oracle's _canonical_outcome delegate to model.effective_rate). Corrected both docstrings to name
  the true anchor (committed gold + inline-computed literal tests) and added a difference-mode
  inline-computed test.

R3-vac came back SOLID (separate PR). gold 18/18 (6/5/7); solver lane 6/0 + 5/0; 36 CMB tests;
router-hygiene + serving unchanged.
2026-06-08 07:59:45 -07:00

54 lines
2.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Exact integer combined-rate solver (CMB-b).
Solves the queried slot of a :class:`CombinedRateProblem` over the **effective rate** —
```text
effective_rate = rate_a + rate_b (combine_mode == "sum")
effective_rate = rate_a - rate_b (combine_mode == "difference")
query effective_rate: return effective_rate (well-defined even if <= 0)
query quantity: effective_rate × time (exact int)
query time: quantity ÷ effective_rate (exact int or REFUSE)
```
Two refusals, the closed CMB solver taxonomy:
- ``non_positive_net_rate`` — a ``quantity`` or ``time`` query whose net rate is ``<= 0`` cannot
accumulate or finish (and guards the ``eff == 0`` time query from dividing by zero). The
``effective_rate`` query is exempt: the net rate is a well-defined answer even when ``<= 0``.
- ``non_integer_solution`` — a ``time`` query that does not divide exactly; never rounds.
Pure integer arithmetic — **no float, no Fraction** (CMB v1 crosses no units, so no rational
conversion is needed; the reader will refuse cross-unit problems, CMB-c). Off-serving (imports no
``generate.derivation`` / ``core.reliability_gate``); deterministic. This is the runtime solver; the
oracle's ``_canonical_outcome`` is a separate gold-coherence path. Note both this solver and
``_canonical_outcome`` delegate the net-rate arithmetic to ``model.effective_rate``, so the
solver lane grades the solver against the *committed static gold*, and the hand-computed literal
tests (``test_literal_grid_values``, plus the inline ``(rate_a ± rate_b) × time`` in
``test_quantity_query_is_always_integral``) — **not** path-independence — are the anchor against a
shared ``effective_rate`` bug.
"""
from __future__ import annotations
from generate.combined_rate_comprehension.model import CombinedRateProblem
from generate.meaning_graph.reader import Refusal
def solve_combined_rate(problem: CombinedRateProblem) -> int | Refusal:
"""Solve the queried slot exactly over the effective rate, or refuse with the closed taxonomy."""
eff = problem.effective_rate
if problem.query == "effective_rate":
return eff # the net rate is the answer, well-defined even when <= 0
if eff <= 0:
return Refusal("non_positive_net_rate", f"effective_rate={eff}")
if problem.query == "quantity":
assert problem.time is not None # guaranteed by the model's per-query slot guard
return eff * problem.time
# query == "time": exact integer division in the rate's denominator unit, or refuse.
assert problem.quantity is not None
if problem.quantity % eff != 0:
return Refusal("non_integer_solution", f"{problem.quantity}/{eff}")
return problem.quantity // eff
__all__ = ["solve_combined_rate"]