Jump to content

The Principia Machina/TPC-PY/tpc yardstick.py

From The Invariance Itself
Revision as of 02:10, 30 September 2026 by BFP Rauch (talk | contribs) (TPC-PY source tpc_yardstick.py (0.2.0), SHA-256 61bc436a04c9a7a6; conformed TPS 0.4.0; PROPOSED, not declared (TPC-PY 0.2.0))
# -*- coding: utf-8 -*-
"""
TPC-PY --- the yardstick evaluator (TPC step 2, increment 5)
============================================================
Compiles a yardstick declared in a UWC's TMS (the closed grammar of
D100.1; tpm_zone_schemas 0.2.0 section 4) into the SR9 executable-entry
interface, and runs SR9 C1, C2, C4 on the synthetic record suite. This is
the second line behind the static checks tms_well_formed makes at entry:
TPS 4.5 row 17, MECHANICAL when the entry is executable. Standard library
only; Python 3.7+; Windows/Linux.

WHAT IT BUILDS, from one named yardstick of a well-formed TMS:
  key(rid, records, tfk)  the KEY evaluated on the record, lexicographic,
                          lower is closer; a record whose DOMAIN equals a
                          LIMITING DOMAIN (after text normalization) is
                          keyed by that limiting KEY instead;
  resolve                 FIXED to the framework: SR7 for an L-M pair, SR5
                          on the key otherwise (D100.1: a yardstick orders,
                          it never resolves);
  limiting                the declared LIMITING DOMAINs, where SR9 C2
                          exempts a lowered key.

TERMS, on a synthetic record (TPS 4.5.2 fields):
  SR1 = 1 if the record fails SR1, else 0;  A = a(r) (SR4, tpc_strike);
  S = s(r), the record's steps;  FIELD:U_C = float(canon_decimal(fields[
  rid]["U_C"])) from the field table (0.2.0);  NEG:t = -t;  WSUM = the sum,
  in canonical atom order, of weight * atom, weights read from TMS
  CONSTANTS as doubles. Every value is a double; -0.0 is written as 0.0.

THE FIELD TABLE (0.2.0; tpm_record_functions 0.2.0 section 10, D195 (f)
at Option 1, RD-RS-35): {rid: {"U_C": canonical decimal}}, built by the
caller with tpc_record field_values (an L record "0"); an argument of
yardstick_key and suite["fields"] for the checks and the harness; absent
when no FIELD term is used. It holds U_C alone. A record r+ that SR9 C2
builds from r carries r's row (FIELD is independent of the assumption
count): the key reads a record's row from a copy of the records in which
each row is placed on its record, under the key "fields".

REFUSED, as ["REFUSED", reason], never raised:
  "TMS is not well-formed"                      (tms_well_formed first)
  "no yardstick named <name>"
  "FIELD <ident> is not in the field table"     (any FIELD but U_C)
  "FIELD U_C has no value for <rid>"            (no row, or no canonical
                                                 decimal U_C in it)
  "weight <c> is not a finite double"
  "a yardstick value is not finite"

WHAT A PASS MEANS. On a well-formed, FIELD-free yardstick the grammar
already guarantees C1 (SR1 first) and C4 (fixed resolution), and static C2
decides C2 for SR1, A and S exactly, FIELD being independent of the
assumption count. The harness confirms those claims on the suite as a
regression test; it is the only mechanical C2 line for FIELD terms. A pass on a finite suite is evidence, not
proof (TPS 4.5.2).
"""

import math

import tpc_sr9 as C
import tpc_strike as S
import tpc_zone as Z

TPC_YARDSTICK_VERSION = "0.2.0"   # increment 12 (S95: one MINOR bump over the declared 0.1.0): FIELD:U_C by the field table; 0.1.1 C-MS-5


class _Refusal(Exception):
    pass


def _compile_term(t, weights):
    """A canonical term -> (neg, base, parts): parts is the canonical WSUM
    list of (weight, atom), or None for a bare atom."""
    c = Z.canonical_term(Z._text(t))
    neg = c.startswith("NEG:")
    base = c[4:] if neg else c
    for a in (base[5:].split(";") if base.startswith("WSUM:") else [base]):
        a = a.split("*", 1)[1] if base.startswith("WSUM:") else a
        if a.startswith("FIELD:") and a != "FIELD:U_C":
            raise _Refusal(Z._show("FIELD " + a[6:] + " is not in the field table"))
    if base.startswith("WSUM:"):
        parts = []
        for wt in base[5:].split(";"):
            name, atom = wt.split("*", 1)
            w = float(Z.canon_decimal(weights[name]))
            if not math.isfinite(w):
                raise _Refusal("weight " + name + " is not a finite double")
            parts.append((w, atom))
        return neg, "WSUM", parts
    return neg, base, None


def _atom(atom, rid, records, tfk):
    r = records[rid]
    if atom == "SR1":
        return 1.0 if r["sr1_fail"] else 0.0
    if atom == "A":
        return float(S.a_of_r(rid, records, tfk))
    if atom == "FIELD:U_C":
        row = r.get("fields")
        v = row.get("U_C") if type(row) is dict else None
        if type(v) is not str or Z.canon_decimal(v) != v:
            raise _Refusal(Z._show("FIELD U_C has no value for " + rid))
        return float(v)
    return float(r["steps"])                          # "S"


def _value(term, rid, records, tfk):
    neg, base, parts = term
    if parts is None:
        v = _atom(base, rid, records, tfk)
    else:
        v = 0.0
        for w, atom in parts:
            v = v + w * _atom(atom, rid, records, tfk)
    if neg:
        v = -v
    v = v + 0.0                                       # -0.0 -> 0.0
    if not math.isfinite(v):
        raise _Refusal("a yardstick value is not finite")
    return v


def _build(tms, name):
    """The SR9 entry tuple for one named yardstick, or a _Refusal."""
    if not Z.tms_well_formed(tms)[0]:
        raise _Refusal("TMS is not well-formed")
    ys = [y for y in tms.get("YARDSTICKS", []) if y.get("NAME") == name]
    if not ys:
        raise _Refusal(Z._show("no yardstick named " + name))
    y = ys[0]
    weights = dict((c["NAME"], c["VALUE"]) for c in tms.get("CONSTANTS", []))
    main = [_compile_term(t, weights) for t in y["KEY"]]
    lim = {}
    for le in y["LIMITING"]:
        lim[Z._text(le["DOMAIN"])] = [_compile_term(t, weights) for t in le["KEY"]]

    def key(rid, records, tfk):
        terms = lim.get(Z._text(records[rid]["domain"]), main)
        return tuple(_value(t, rid, records, tfk) for t in terms)

    return (key, C._resolve_regime_aware(key), sorted(lim))


def _placed(records, fields):
    """A copy of the records with each field-table row placed on its record
    under "fields" (none when the table has no row for it)."""
    out = {}
    for rid, r in records.items():
        c = dict(r)
        c.pop("fields", None)
        if type(fields) is dict and rid in fields:
            c["fields"] = fields[rid]
        out[rid] = c
    return out


def yardstick_key(tms, name, rid, records, tfk_assumptions=(), fields=None):
    """["OK", [values]] for one record, or ["REFUSED", reason]. fields: the
    field table (0.2.0), None when no FIELD term is used."""
    try:
        key, _, _ = _build(tms, name)
        return ["OK", list(key(rid, _placed(records, fields), list(tfk_assumptions)))]
    except _Refusal as e:
        return ["REFUSED", str(e)]


def yardstick_checks(tms, name, suite):
    """["OK", C1, C2, C4] with each check's full result from tpc_sr9 (the
    witnesses CAC's refusal names), or ["REFUSED", reason]. suite["fields"]
    is the field table (0.2.0), absent when no FIELD term is used."""
    try:
        entry = _build(tms, name)
        suite = dict(suite, records=_placed(suite["records"], suite.get("fields")))
        return ["OK", C.sr9_c1(entry, suite), C.sr9_c2(entry, suite), C.sr9_c4(entry, suite)]
    except _Refusal as e:
        return ["REFUSED", str(e)]


def yardstick_harness(tms, name, suite):
    """["OK", C1 pass, C2 pass, C4 pass, total violations] for the CAC
    gate, or ["REFUSED", reason]."""
    r = yardstick_checks(tms, name, suite)
    if r[0] != "OK":
        return r
    c1, c2, c4 = r[1], r[2], r[3]
    return ["OK", c1[0], c2[0], c4[0], len(c1[1]) + len(c2[1]) + len(c4[1])]