experiment: record pressure positivity and shape curvature evidence
This commit is contained in:
143
experiments/plateau_analysis/audit_focused_outputs.py
Normal file
143
experiments/plateau_analysis/audit_focused_outputs.py
Normal file
@@ -0,0 +1,143 @@
|
||||
"""Audit the pressure/shape campaign's completed outputs without running solvers.
|
||||
|
||||
The sole argument is the campaign directory containing captures/ and focused/.
|
||||
The script reads existing results and writes focused_audit_checks.json there.
|
||||
Unfinished cases are reported as pending; this is an observation audit, not a
|
||||
stellar convergence or physical acceptance gate.
|
||||
"""
|
||||
import argparse
|
||||
import csv
|
||||
import hashlib
|
||||
import json
|
||||
import math
|
||||
from pathlib import Path
|
||||
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("campaign_root", type=Path)
|
||||
ROOT = parser.parse_args().campaign_root.resolve()
|
||||
FOCUSED = ROOT / "focused"
|
||||
|
||||
|
||||
def rows(path):
|
||||
with path.open() as source:
|
||||
result = list(csv.DictReader(source))
|
||||
if any(None in r or any(v is None for v in r.values()) for r in result):
|
||||
raise ValueError(f"Malformed CSV row: {path}")
|
||||
return result
|
||||
|
||||
|
||||
def metrics(path):
|
||||
data = rows(path)
|
||||
if len({r["metric"] for r in data}) != len(data):
|
||||
raise ValueError(f"Duplicate metric: {path}")
|
||||
return {r["metric"]: float(r["value"]) for r in data}
|
||||
|
||||
|
||||
def sha(path):
|
||||
return hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
|
||||
|
||||
def clean(value):
|
||||
if isinstance(value, dict):
|
||||
return {k: clean(v) for k, v in value.items()}
|
||||
if isinstance(value, list):
|
||||
return [clean(v) for v in value]
|
||||
if isinstance(value, float) and not math.isfinite(value):
|
||||
return None
|
||||
return value
|
||||
|
||||
|
||||
provenance = json.loads((FOCUSED / "provenance.json").read_text())
|
||||
result = {
|
||||
"source_commit": provenance["commit"],
|
||||
"binary_hash_matches": sha(FOCUSED / "plateau_diagnostic") == provenance["binary_sha256"],
|
||||
"completed": [],
|
||||
"pending": [],
|
||||
}
|
||||
for case in json.loads((FOCUSED / "cases.json").read_text()):
|
||||
case_id = case["id"]
|
||||
manifest = FOCUSED / f"{case_id}_run.json"
|
||||
if not manifest.exists():
|
||||
result["pending"].append(case_id)
|
||||
continue
|
||||
run = json.loads(manifest.read_text())
|
||||
if "exit_code" not in run:
|
||||
result["pending"].append(case_id)
|
||||
continue
|
||||
folder = FOCUSED / case_id
|
||||
data = {
|
||||
"id": case_id,
|
||||
"exit_code": run["exit_code"],
|
||||
"seconds": run["seconds"],
|
||||
"input_hash_count": len(run["input_state_hashes"]),
|
||||
"input_hash_mismatches": [str(p) for p, h in run["input_state_hashes"].items() if sha(Path(p)) != h],
|
||||
"output_hash_count": len(run["output_hashes"]),
|
||||
"output_hash_mismatches": [p for p, h in run["output_hashes"].items() if sha(folder / p) != h],
|
||||
"unhashed_data_files": sorted(str(p.relative_to(folder)) for p in folder.rglob("*") if p.is_file() and p.suffix in (".txt", ".csv", ".json") and str(p.relative_to(folder)) not in run["output_hashes"]),
|
||||
"csv_rows": {p.name: len(rows(p)) for p in sorted(folder.glob("*.csv"))},
|
||||
"frozen": metrics(folder / "frozen_input_checks.csv"),
|
||||
}
|
||||
args = case["arguments"]
|
||||
direction = Path(args[args.index("--probe-direction") + 1])
|
||||
physical = Path(args[args.index("--probe-state") + 1])
|
||||
data["direction_copy_byte_identical"] = direction.read_bytes() == (folder / "probe_normalized_correction.txt").read_bytes()
|
||||
data["physical_base_copy_byte_identical"] = physical.read_bytes() == (folder / "frozen_physical_base.txt").read_bytes()
|
||||
if "iteration_" in case_id:
|
||||
iteration = int(case_id.rsplit("_", 1)[1])
|
||||
capture_root = ROOT / "captures" / ("n1p5_J0_capture" if case_id.startswith("n1p5") else "n1_J0p10_capture")
|
||||
capture_manifest = json.loads((capture_root.parent / f"{capture_root.name}_run.json").read_text())
|
||||
data["auxiliary_capture_input_hashes_match"] = all(sha(physical.parent / name) == capture_manifest["output_hashes"][str((physical.parent / name).relative_to(capture_root))] for name in ("normalized_base.txt", "normalized_residual.txt"))
|
||||
old = next(r for r in rows(capture_root / "newton.csv") if int(r["iteration"]) == iteration)
|
||||
data["historical_linear_defect_relative"] = float(old["linear_true"])
|
||||
trial = next(r for r in rows(capture_root / "trials.csv") if int(r["iteration"]) == iteration and r["trial"] == "0")
|
||||
data["original_first_trial"] = trial
|
||||
replay = rows(folder / "replayed_candidate.csv")[0]
|
||||
data["candidate_replay"] = replay
|
||||
if trial["disposition"] == "4":
|
||||
data["replayed_candidate_residual_exact"] = replay["admissible"] == "1" and replay["residual_norm"] == trial["F"]
|
||||
data["replayed_candidate_residual_difference"] = float(replay["residual_norm"]) - float(trial["F"])
|
||||
data["replayed_candidate_minimum_jacobian_exact"] = replay["minimum_jacobian"] == trial["min_jacobian"]
|
||||
else:
|
||||
previous = physical.parent
|
||||
data["historical_linear_defect_relative"] = float(next(r for r in rows(previous / "frozen_linear.csv") if r["restart"] == "160")["true_relative"])
|
||||
data["linear_defect_relative_difference"] = data["frozen"]["linear_defect_relative"] - data["historical_linear_defect_relative"]
|
||||
layout = rows(folder / "layout.csv")
|
||||
residual_blocks = {r["block"] for r in layout if r["kind"] == "residual"}
|
||||
value_blocks = {r["block"] for r in layout if r["kind"] == "value"}
|
||||
nr = sum(r["kind"] == "residual" for r in layout)
|
||||
nv = sum(r["kind"] == "value" for r in layout)
|
||||
if "--shape-curvature" in args:
|
||||
data["shape_summary"] = metrics(folder / "shape_curvature_summary.csv")
|
||||
expected = {"shape_curvature_directions.csv": 3 * nv, "shape_curvature_actions.csv": nr, "shape_curvature_preparations.csv": 22, "shape_curvature_restorations.csv": 4 * nr, "shape_curvature_remainders.csv": 18 * nr, "shape_curvature_mixed.csv": 6 * nr}
|
||||
data["shape_row_count_mismatches"] = {f: [data["csv_rows"][f], count] for f, count in expected.items() if data["csv_rows"][f] != count}
|
||||
restorations = rows(folder / "shape_curvature_restorations.csv")
|
||||
data["shape_max_restoration_difference"] = max(float(r["restoration_difference_norm"]) for r in restorations)
|
||||
mixed = rows(folder / "shape_curvature_mixed.csv")
|
||||
alphas = {r["alpha"] for r in mixed}
|
||||
remainders = rows(folder / "shape_curvature_remainders.csv")
|
||||
data["shape_six_alphas"] = len(alphas) == 6
|
||||
data["shape_mixed_keys_complete"] = {(r["alpha"], r["block"]) for r in mixed} == {(a, b) for a in alphas for b in residual_blocks}
|
||||
data["shape_remainder_keys_complete"] = {(r["alpha"], r["direction"], r["block"]) for r in remainders} == {(a, d, b) for a in alphas for d in ("full", "shape", "fields") for b in residual_blocks}
|
||||
data["shape_direction_keys_complete"] = {(r["direction"], r["value_block"]) for r in rows(folder / "shape_curvature_directions.csv")} == {(d, b) for d in ("full", "shape", "fields") for b in value_blocks}
|
||||
data["shape_max_absolute_merit_identity_error"] = max(abs(float(r["merit_identity_error"])) for r in mixed)
|
||||
data["shape_max_absolute_projection_decomposition_error"] = max(abs(float(r["projection_decomposition_error"])) for r in mixed)
|
||||
data["shape_all_trials_admissible"] = all(r["all_trials_admissible"] == "1" for r in mixed)
|
||||
data["shape_all_increments_resolved"] = all(r["all_state_increments_resolved"] == "1" for r in mixed)
|
||||
data["shape_all_alpha_squared_valid"] = all(r["alpha_squared_valid"] == "1" for r in mixed)
|
||||
if "--pressure-probe" in args:
|
||||
data["pressure_summary"] = metrics(folder / "pressure_obstruction_summary.csv")
|
||||
pressure = rows(folder / "pressure_obstruction_trials.csv")
|
||||
blocks = rows(folder / "pressure_obstruction_blocks.csv")
|
||||
data["pressure_admissible_trials"] = sum(r["admissible"] == "1" for r in pressure)
|
||||
data["pressure_expected_block_rows"] = nr * data["pressure_admissible_trials"]
|
||||
data["pressure_block_rows_complete"] = len(blocks) == data["pressure_expected_block_rows"]
|
||||
data["pressure_trial_keys_unique"] = len({(r["direction"], r["alpha"]) for r in pressure}) == len(pressure)
|
||||
data["pressure_block_keys_complete"] = {(r["direction"], r["alpha"], r["block"]) for r in blocks} == {(r["direction"], r["alpha"], b) for r in pressure if r["admissible"] == "1" for b in residual_blocks}
|
||||
data["pressure_limiting_h_prediction_max_absolute_error"] = {
|
||||
family: max(abs(float(r[f"{family}_limiting_h_prediction"]) - float(r[f"{family}_limiting_h_evaluated"])) for r in pressure)
|
||||
for family in ("closure", "pressure")
|
||||
}
|
||||
result["completed"].append(data)
|
||||
|
||||
(ROOT / "focused_audit_checks.json").write_text(json.dumps(clean(result), indent=2) + "\n")
|
||||
print(json.dumps({"completed": [r["id"] for r in result["completed"]], "pending": result["pending"]}, indent=2))
|
||||
Reference in New Issue
Block a user