Cross-platform replay of the frozen assessor returned INVALID_SET because recomputed eps(128) differed from the Horsea receipt by one ULP. Compare only that derived float within 4 ULPs (close_ulps); hashes, case identity, other metadata and scientific thresholds stay exact. Tests accept the observed one/two-ULP differences and reject 1e-12 relative, 2x, string, NaN and missing values. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
205 lines
9.9 KiB
Python
205 lines
9.9 KiB
Python
"""Fixed four-case shear-wave contracts.
|
|
|
|
Default: N128 (U128, U256, constant R128, projected R128). Opt-in
|
|
--diagnostic scaled_shear_n256: N256 (U256, U512, constant R256, projected
|
|
R256), same physical rectangle, t* schedule, nu = U N / 100, eps(N), limits
|
|
and floors, plus a descriptive comparison with the hash-pinned N128 receipts.
|
|
|
|
Shared provenance, build, workload and sample-time checks are reused from
|
|
assess_projected_pairs (same cases, source pins and geometry); only the
|
|
Taylor-Green-specific checks are replaced by shear-wave metadata and physics.
|
|
"""
|
|
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import math
|
|
from pathlib import Path
|
|
|
|
import assess_projected_pairs as tg
|
|
import shear_wave as sw
|
|
from run_case import SCALED_DIAGNOSTIC, SCALED_SHEAR, SHEAR_WAVE_SHA256
|
|
|
|
# Audited de5b0cc N128 receipts (evidence commit cba6bca, shear-wave-de5b0cc zip).
|
|
REFERENCE_REVISION = "de5b0cc080794d0ce44d37f845aa8e8dbf8c2016"
|
|
REFERENCE_RECEIPTS = {
|
|
"uniform128": "d4a60fb806d04bfa8098a862cceba51e00b49979a6a250fef17a5fc8e3ea3255",
|
|
"uniform256": "e0b58e734724be5f82ee5d6df84395e3181b4e96e4246dd5a574ea23a8d12386",
|
|
"constant128": "c70d7cd14b1257a01cc49f8edfcc7685d430e5301e2f2d60ed426936f8b96c51",
|
|
"projected128": "980161458b9463af09a8e3178063fea0f4bbeb465329e1d3becdb0f224537b54",
|
|
}
|
|
ROLES = ("uniform_root", "uniform_fine", "constant", "projected_linear")
|
|
|
|
SHARED = ("complete_matrix", "no_extra_cases", "no_duplicate_cases", "finite_receipts",
|
|
"same_source_and_harness", "clean_a135252_numerical_source",
|
|
"same_recorded_runtime", "same_explicit_device", "same_early_sampling")
|
|
PER_CASE = ("complete_times", "scales", "actual_builds", "reference", "runtime_source",
|
|
"patch", "workload")
|
|
|
|
|
|
METADATA_ULPS = 4
|
|
|
|
|
|
def close_ulps(recorded, expected, ulps=METADATA_ULPS):
|
|
"""Portable check for recomputed float metadata only (e.g. eps = |k|^2).
|
|
|
|
The recorded value comes from another runtime/libm, so exact equality can
|
|
fail by one ULP (observed: 0.012047856934923534 Horsea vs ...532 here).
|
|
A few ULPs is far below any meaningful change; hashes, case identity and
|
|
scientific thresholds stay exact.
|
|
"""
|
|
return (isinstance(recorded, float) and math.isfinite(recorded)
|
|
and abs(recorded - expected) <= ulps * math.ulp(max(abs(recorded), abs(expected))))
|
|
|
|
|
|
def metadata_ok(record, n):
|
|
return (record.get("flow") == "shear_wave" and record.get("mode") == list(sw.MODE)
|
|
and record.get("polarization") == sw.E.tolist()
|
|
and record.get("angle_deg") == sw.ANGLE_DEG and record.get("phase") == sw.PHI
|
|
and close_ulps(record.get("eps"), sw.eps(n))
|
|
and record.get("numerics") == tg.NUMERICS
|
|
and record.get("initialization") == sw.INITIALIZATION
|
|
and record.get("limits") == sw.LIMITS
|
|
and record.get("early_pressure") == dict(enabled=False, rows=[],
|
|
sampled_max_pressure_l2=None)
|
|
and (record.get("source") or {}).get("shear_wave_sha256") == SHEAR_WAVE_SHA256)
|
|
|
|
|
|
def cases_for(scaled):
|
|
factor = 2 if scaled else 1
|
|
return tuple((re, n * factor, depth, scheme) for re, n, depth, scheme in tg.CASES)
|
|
|
|
|
|
def assess(records, expected_revision=None, *, scaled=False):
|
|
if not isinstance(records, list) or not all(isinstance(r, dict) for r in records):
|
|
raise ValueError("records must be a list of receipt objects")
|
|
# The scaled TG assessor supplies N256/512 cases, builds and workload; its
|
|
# TG-only diagnostic identity and early-sampling checks are not used here.
|
|
base_checks = tg.assess(records, expected_revision,
|
|
diagnostic=SCALED_DIAGNOSTIC if scaled else None)["checks"]
|
|
checks = {name: base_checks.get(name, False) for name in SHARED}
|
|
checks["shear_diagnostic_identity"] = all(
|
|
r.get("diagnostic") == (SCALED_SHEAR if scaled else None) for r in records)
|
|
cases = cases_for(scaled)
|
|
indexed = {}
|
|
for r in records:
|
|
indexed.setdefault(tuple(r.get(k) for k in ("re_L", "n", "depth", "explosion")), r)
|
|
for key in cases:
|
|
_, n, depth, scheme = key
|
|
label = f"{n}/{depth}/{scheme}"
|
|
for name in PER_CASE:
|
|
checks[f"{label} {name}"] = base_checks.get(f"{label} {name}", False)
|
|
checks[f"{label} shear_metadata"] = key in indexed and metadata_ok(indexed[key], n)
|
|
contract_valid = all(checks.values())
|
|
result = dict(checks=checks, contract_valid=contract_valid,
|
|
cases=[list(k) for k in cases], expected_revision=expected_revision,
|
|
diagnostic=SCALED_SHEAR if scaled else None,
|
|
scope="2D periodic Re100 oblique (2,1) shear wave; one phase and one "
|
|
"rectangle; no placement robustness or convergence order")
|
|
if not contract_valid:
|
|
result.update(status="INVALID_SET", passed=False)
|
|
return result
|
|
try:
|
|
physics = sw.assess(indexed[cases[0]], indexed[cases[1]],
|
|
[indexed[cases[2]], indexed[cases[3]]])
|
|
except (KeyError, ValueError) as error:
|
|
result.update(status="INVALID_SET", passed=False, reason=str(error))
|
|
return result
|
|
result.update(physics=physics, status=physics["status"],
|
|
passed=physics["status"] == "PASS")
|
|
try:
|
|
result["signed"] = signed_summary([indexed[k] for k in cases])
|
|
except (KeyError, TypeError, ValueError) as error:
|
|
result["signed_unavailable"] = str(error)
|
|
return result
|
|
|
|
|
|
def _signed(record):
|
|
"""Raw signed quantities of one valid record (None when the fit is unavailable)."""
|
|
s = sw.summarize(record)
|
|
bias = None if not s["rate_fit_valid"] else s["rate"] / s["exact_rate"] - 1
|
|
return dict(n=record["n"], depth=record["depth"], explosion=record["explosion"],
|
|
rate_fit_valid=s["rate_fit_valid"], signed_rate_bias=bias,
|
|
rate_tstar=s["rate_tstar"], exact_rate_tstar=s["exact_rate_tstar"],
|
|
interval_rate_errors=s["interval_rate_errors"],
|
|
residual_l2=s["residual_l2"], spurious_pressure_scaled=s["spurious_pressure_scaled"],
|
|
spurious_pressure_rms=s["spurious_pressure_rms"],
|
|
final_amplitude_error=record["rows"][-1]["amplitude_error"])
|
|
|
|
|
|
def signed_summary(records):
|
|
"""Signed bias per case and refined excess over each uniform control."""
|
|
rows = dict(zip(ROLES, (_signed(r) for r in records)))
|
|
|
|
def minus(a, b):
|
|
return None if a is None or b is None else a - b
|
|
|
|
for role in ("constant", "projected_linear"):
|
|
for control in ("uniform_root", "uniform_fine"):
|
|
rows[role][f"excess_over_{control}"] = {
|
|
key: minus(rows[role][key], rows[control][key])
|
|
for key in ("signed_rate_bias", "residual_l2", "spurious_pressure_rms")}
|
|
return rows
|
|
|
|
|
|
def load_reference(paths):
|
|
"""The four audited N128 receipts verbatim; never relabelled as fresh controls."""
|
|
blobs = [Path(path).read_bytes() for path in paths]
|
|
hashes = {hashlib.sha256(blob).hexdigest() for blob in blobs}
|
|
if len(blobs) != 4 or hashes != set(REFERENCE_RECEIPTS.values()):
|
|
raise ValueError("N128 receipt hashes differ from the audited de5b0cc evidence")
|
|
return [json.loads(blob) for blob in blobs]
|
|
|
|
|
|
def resolution_trend(new_result, reference, reference_revision=REFERENCE_REVISION):
|
|
"""Descriptive N128 -> N256 signed changes; never an acceptance gate or an order."""
|
|
old_result = assess(reference, reference_revision)
|
|
if "signed" not in old_result or "signed" not in new_result:
|
|
return dict(valid=False, reason="N128 reference or N256 set lacks a valid signed summary")
|
|
out = {}
|
|
for role in ROLES:
|
|
old, new = old_result["signed"][role], new_result["signed"][role]
|
|
out[role] = {}
|
|
for key in ("signed_rate_bias", "residual_l2", "spurious_pressure_rms",
|
|
*(f"excess_over_{c}" for c in ("uniform_root", "uniform_fine"))):
|
|
if key not in old:
|
|
continue
|
|
pairs = old[key].items() if isinstance(old[key], dict) else [(None, old[key])]
|
|
for sub, low in pairs:
|
|
high = new[key][sub] if sub else new[key]
|
|
ok = low is not None and high is not None
|
|
out[role][key if sub is None else f"{key}.{sub}"] = dict(
|
|
n128=low, n256=high, signed_change=high - low if ok else None,
|
|
n256_to_n128=high / low if ok and low else None)
|
|
return dict(valid=True, reference_revision=reference_revision,
|
|
reference_receipt_sha256=REFERENCE_RECEIPTS,
|
|
reference_status=old_result["status"], changes=out,
|
|
scope="same-placement two-resolution trend; not a convergence order, "
|
|
"placement robustness or redesign decision")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
p = argparse.ArgumentParser(description=__doc__)
|
|
p.add_argument("files", type=Path, nargs=4)
|
|
p.add_argument("--output", type=Path, required=True)
|
|
p.add_argument("--expected-revision", required=True)
|
|
p.add_argument("--diagnostic", choices=[SCALED_SHEAR])
|
|
p.add_argument("--reference-files", type=Path, nargs=4)
|
|
args = p.parse_args()
|
|
if args.output.exists():
|
|
p.error("Output already exists; choose a new path to retain prior evidence")
|
|
if bool(args.diagnostic) != bool(args.reference_files):
|
|
p.error("--diagnostic scaled_shear_n256 requires --reference-files (N128 receipts)")
|
|
result = assess([json.loads(path.read_text()) for path in args.files], args.expected_revision,
|
|
scaled=bool(args.diagnostic))
|
|
if args.diagnostic:
|
|
try:
|
|
reference = load_reference(args.reference_files)
|
|
except ValueError as exc:
|
|
p.error(str(exc))
|
|
result["resolution_trend"] = resolution_trend(result, reference)
|
|
text = json.dumps(result, indent=2, allow_nan=False) + "\n"
|
|
args.output.write_text(text)
|
|
print(text, end="")
|
|
raise SystemExit(0 if result["passed"] else 1)
|