The Principia Machina/TPC-PY/tpc yardstick.py
Appearance
# -*- 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])]