summaryrefslogtreecommitdiff
path: root/experiments
diff options
context:
space:
mode:
authorYurenHao0426 <Blackhao0426@gmail.com>2026-07-22 05:36:17 -0500
committerYurenHao0426 <Blackhao0426@gmail.com>2026-07-22 05:36:17 -0500
commit46517d604a8caa23242f209f809f159044d9878b (patch)
treee6f76e2b042490bf6e72544eb6776db415eb48ba /experiments
parentb329856d15b91a6f31fb75f010f0a550cddd0ee8 (diff)
baselines: freeze strict C4 audit gate
Diffstat (limited to 'experiments')
-rw-r--r--experiments/audit_native_baselines.py236
-rwxr-xr-xexperiments/finalize_accept.sh15
-rw-r--r--experiments/import_native_baseline.py14
-rw-r--r--experiments/native_baseline_protocols.json15
4 files changed, 279 insertions, 1 deletions
diff --git a/experiments/audit_native_baselines.py b/experiments/audit_native_baselines.py
new file mode 100644
index 0000000..8bae59b
--- /dev/null
+++ b/experiments/audit_native_baselines.py
@@ -0,0 +1,236 @@
+#!/usr/bin/env python3
+"""Strict C4 audit and deterministic table for native author-code baselines."""
+import argparse
+import hashlib
+import json
+import math
+import os
+import re
+
+
+ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+DEFAULT_PROTOCOLS = os.path.join(
+ ROOT, "experiments", "native_baseline_protocols.json")
+PROTOCOL_IDS = (
+ "burstccn_cifar10_symplastic_paper_seed0",
+ "dualprop_cifar10_vgg16_a0_b0p1_seed1988",
+)
+SHA256_RE = re.compile(r"^[0-9a-f]{64}$")
+GIT_REVISION_RE = re.compile(r"^[0-9a-f]{40,64}$")
+
+
+def file_hash(path):
+ digest = hashlib.sha256()
+ with open(path, "rb") as handle:
+ for chunk in iter(lambda: handle.read(1024 * 1024), b""):
+ digest.update(chunk)
+ return digest.hexdigest()
+
+
+def require(condition, message):
+ if not condition:
+ raise ValueError(message)
+
+
+def number(value, label, low=None, high=None):
+ require(isinstance(value, (int, float)) and math.isfinite(value),
+ f"{label} must be finite, found {value!r}")
+ if low is not None:
+ require(value >= low, f"{label} must be >= {low}, found {value}")
+ if high is not None:
+ require(value <= high, f"{label} must be <= {high}, found {value}")
+ return float(value)
+
+
+def accuracy(metrics, key, label):
+ return number(metrics.get(key), f"{label}.{key}", 0.0, 100.0)
+
+
+def verify_artifacts(records, label):
+ require(isinstance(records, list) and records, f"{label} must be nonempty")
+ for index, record in enumerate(records):
+ prefix = f"{label}[{index}]"
+ require(isinstance(record.get("path"), str) and record["path"],
+ f"{prefix}.path is missing")
+ require(isinstance(record.get("bytes"), int) and record["bytes"] > 0,
+ f"{prefix}.bytes must be positive")
+ require(bool(SHA256_RE.fullmatch(str(record.get("sha256", "")))),
+ f"{prefix}.sha256 is invalid")
+
+
+def validate_common(row, protocol_id, protocol):
+ label = protocol_id
+ require(row.get("schema_version") == 1, f"{label}: unsupported schema")
+ require(row.get("protocol_id") == protocol_id, f"{label}: protocol ID mismatch")
+ require(row.get("protocol") == protocol, f"{label}: frozen protocol drift")
+ require(row.get("method") == protocol["method"], f"{label}: method mismatch")
+ require(row.get("dataset") == protocol["dataset"], f"{label}: dataset mismatch")
+ require(row.get("seed") == protocol["seed"], f"{label}: seed mismatch")
+ audit = row.get("audit", {})
+ for key in ("complete", "finite", "source_revision_match",
+ "source_tracked_clean", "selection_semantics_frozen"):
+ require(audit.get(key) is True, f"{label}: audit.{key} is not true")
+ require(audit.get("expected_epochs") == protocol["expected_epochs"],
+ f"{label}: expected epoch audit mismatch")
+ source = row.get("source_provenance", {})
+ require(source.get("revision") == protocol["source_revision"],
+ f"{label}: author source revision mismatch")
+ require(source.get("tracked_dirty") is False,
+ f"{label}: author source is not tracked-clean")
+ importer = row.get("importer_provenance", {})
+ require(bool(GIT_REVISION_RE.fullmatch(str(importer.get("revision", "")))),
+ f"{label}: importer revision is invalid")
+ require(importer.get("tracked_dirty") is False,
+ f"{label}: importer source was not tracked-clean")
+ environment = row.get("runtime_environment", {})
+ freeze = environment.get("pip_freeze")
+ require(isinstance(freeze, list) and freeze, f"{label}: package freeze missing")
+ freeze_text = ("\n".join(freeze) + "\n").encode()
+ require(hashlib.sha256(freeze_text).hexdigest()
+ == environment.get("pip_freeze_sha256"),
+ f"{label}: package freeze hash mismatch")
+ gpu = row.get("gpu", {})
+ for key, expected in protocol["expected_gpu"].items():
+ require(gpu.get(key) == expected,
+ f"{label}: GPU {key} expected {expected!r}, found {gpu.get(key)!r}")
+ verify_artifacts(row.get("dataset_artifacts"), f"{label}.dataset_artifacts")
+ dataset_hashes = {item["sha256"] for item in row["dataset_artifacts"]}
+ require(set(protocol["required_dataset_sha256"]).issubset(dataset_hashes),
+ f"{label}: required dataset hashes missing")
+ verify_artifacts(row.get("run_artifacts"), f"{label}.run_artifacts")
+ metrics = row.get("metrics", {})
+ require(metrics.get("epochs_observed") == protocol["expected_epochs"],
+ f"{label}: incomplete metric history")
+ number(metrics.get("wall_s"), f"{label}.wall_s", 0.0)
+ require(metrics["wall_s"] > 0, f"{label}: wall time must be positive")
+ require(isinstance(metrics.get("wall_definition"), str)
+ and metrics["wall_definition"], f"{label}: wall definition missing")
+
+
+def validate_burst(row, protocol):
+ label = row["protocol_id"]
+ validate_common(row, label, protocol)
+ metrics = row["metrics"]
+ require(metrics.get("optimization_epochs") == 399,
+ f"{label}: author-loop optimization epoch count mismatch")
+ require(metrics.get("initial_evaluation_epoch") == 0,
+ f"{label}: epoch-0 evaluation missing")
+ require(metrics.get("primary_final_epoch") == 399,
+ f"{label}: final epoch mismatch")
+ require(metrics.get("test_evaluations") == 400,
+ f"{label}: native per-epoch test count mismatch")
+ for key in (
+ "primary_final_test_accuracy_percent",
+ "final_validation_accuracy_percent",
+ "validation_selected_validation_accuracy_percent",
+ "validation_selected_test_accuracy_percent",
+ "test_selected_test_accuracy_percent"):
+ accuracy(metrics, key, label)
+ require(0 <= metrics.get("validation_selected_epoch", -1) <= 399,
+ f"{label}: validation-selected epoch invalid")
+ require(0 <= metrics.get("test_selected_epoch", -1) <= 399,
+ f"{label}: test-selected epoch invalid")
+ require(metrics["test_selected_test_accuracy_percent"]
+ >= metrics["primary_final_test_accuracy_percent"],
+ f"{label}: test-selected result is below final result")
+
+
+def validate_dualprop(row, protocol):
+ label = row["protocol_id"]
+ validate_common(row, label, protocol)
+ metrics = row["metrics"]
+ require(metrics.get("final_epoch") == 130, f"{label}: final epoch mismatch")
+ require(metrics.get("test_evaluations") == 1,
+ f"{label}: expected a single test evaluation")
+ require(metrics.get("test_selected_test_accuracy_percent") is None,
+ f"{label}: test-selected metric must be unavailable")
+ for key in (
+ "final_train_accuracy_percent",
+ "final_validation_accuracy_percent",
+ "validation_selected_validation_accuracy_percent",
+ "validation_selected_test_accuracy_percent",
+ "primary_test_accuracy_percent",
+ "primary_test_top5_accuracy_percent"):
+ accuracy(metrics, key, label)
+ require(1 <= metrics.get("validation_selected_epoch", 0) <= 130,
+ f"{label}: validation-selected epoch invalid")
+ require(metrics["primary_test_accuracy_percent"]
+ == metrics["validation_selected_test_accuracy_percent"],
+ f"{label}: primary is not the validation-selected checkpoint")
+ component_sum = (metrics.get("training_wall_s", 0)
+ + metrics.get("validation_wall_s", 0)
+ + metrics.get("test_wall_s", 0))
+ require(math.isclose(component_sum, metrics["wall_s"], rel_tol=1e-12,
+ abs_tol=1e-9),
+ f"{label}: component wall times do not sum to total")
+
+
+def render(rows, protocols):
+ burst = rows[PROTOCOL_IDS[0]]
+ dual = rows[PROTOCOL_IDS[1]]
+ bm, dm = burst["metrics"], dual["metrics"]
+ bp, dp = protocols[PROTOCOL_IDS[0]], protocols[PROTOCOL_IDS[1]]
+ return f"""# Native author-code baseline audit
+
+Both records passed the strict C4 artifact, source, protocol, environment,
+dataset, completeness, finiteness, selection, and cost-definition checks.
+These are method-native reproductions, not equal-compute comparisons with SDIL.
+
+| method | reproduced seed | primary test (%) | validation-selected test (%) | test-selected test (%) | published (%) | audited wall (s) |
+|:--|--:|--:|--:|--:|--:|--:|
+| BurstCCN | {bp['seed']} | {bm['primary_final_test_accuracy_percent']:.3f} | {bm['validation_selected_test_accuracy_percent']:.3f} | {bm['test_selected_test_accuracy_percent']:.3f} | {bp['published_accuracy_percent_mean']:.2f} ± {bp['published_accuracy_percent_sd']:.2f} | {bm['wall_s']:.1f} |
+| Dual Prop | {dp['seed']} | {dm['primary_test_accuracy_percent']:.3f} | {dm['validation_selected_test_accuracy_percent']:.3f} | — | {dp['published_accuracy_percent_mean']:.2f} ± {dp['published_accuracy_percent_sd']:.2f} | {dm['wall_s']:.1f} |
+
+BurstCCN's primary metric is its final epoch. Its validation-selected metric is
+the test accuracy from the minimum-validation-error epoch; its best test value
+is explicitly test-selected because the native trainer evaluates test every
+epoch. The `n_epochs=400` author configuration performs an epoch-0 evaluation
+and 399 optimization epochs. Dual Prop evaluates test once after restoring the
+best-validation checkpoint, so its primary and validation-selected values are
+identical and no test-selected value exists.
+
+The BurstCCN wall value is W&B run time through the final epoch log. The Dual
+Prop wall value sums author-measured train, validation, and final test time and
+excludes setup, checkpoint I/O, and diagnostic plotting. One reproduced seed
+establishes executable fidelity; published multi-seed uncertainty is retained
+for context and is not replaced by a one-seed error bar.
+"""
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--protocols", default=DEFAULT_PROTOCOLS)
+ parser.add_argument("--results", default=os.path.join(ROOT, "results", "native"))
+ parser.add_argument("--output", default=os.path.join(
+ ROOT, "results", "native_baseline_audit.md"))
+ args = parser.parse_args()
+ with open(args.protocols) as handle:
+ ledger = json.load(handle)
+ require(ledger.get("schema_version") == 1, "unsupported protocol ledger schema")
+ protocols = ledger["protocols"]
+ rows = {}
+ for protocol_id in PROTOCOL_IDS:
+ path = os.path.join(args.results, protocol_id + ".json")
+ require(os.path.isfile(path), f"missing native result: {path}")
+ with open(path) as handle:
+ rows[protocol_id] = json.load(handle)
+ validate_burst(rows[PROTOCOL_IDS[0]], protocols[PROTOCOL_IDS[0]])
+ validate_dualprop(rows[PROTOCOL_IDS[1]], protocols[PROTOCOL_IDS[1]])
+ text = render(rows, protocols)
+ os.makedirs(os.path.dirname(os.path.abspath(args.output)), exist_ok=True)
+ with open(args.output, "w") as handle:
+ handle.write(text)
+ manifest = {
+ "strict": True,
+ "c4_pass": True,
+ "protocol_ledger_sha256": file_hash(args.protocols),
+ "result_sha256": {
+ protocol_id: file_hash(os.path.join(args.results, protocol_id + ".json"))
+ for protocol_id in PROTOCOL_IDS
+ },
+ }
+ print(json.dumps(manifest, indent=2, sort_keys=True))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/experiments/finalize_accept.sh b/experiments/finalize_accept.sh
new file mode 100755
index 0000000..f8eb808
--- /dev/null
+++ b/experiments/finalize_accept.sh
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+# Strict accept-bar finalizer. It intentionally fails until the two frozen
+# native author-code runs are complete, imported, and independently audited.
+set -eu
+
+cd "$(dirname "$0")/.."
+
+experiments/finalize_claims.sh
+/home/yurenh2/miniconda3/envs/ep_pascal/bin/python3 \
+ experiments/audit_native_baselines.py \
+ --results results/native \
+ --output results/native_baseline_audit.md
+
+git diff --check
+echo "accept-bar mechanical audits passed"
diff --git a/experiments/import_native_baseline.py b/experiments/import_native_baseline.py
index ae61727..29dc011 100644
--- a/experiments/import_native_baseline.py
+++ b/experiments/import_native_baseline.py
@@ -290,6 +290,18 @@ def import_result(args):
if args.resolved_config:
artifacts.append(file_record(args.resolved_config))
dataset_artifacts = [file_record(path) for path in args.dataset_artifact]
+ dataset_hashes = {record["sha256"] for record in dataset_artifacts}
+ missing_dataset_hashes = (set(protocol["required_dataset_sha256"])
+ - dataset_hashes)
+ if missing_dataset_hashes:
+ raise ValueError(
+ "required dataset artifact hashes are missing: "
+ + ", ".join(sorted(missing_dataset_hashes)))
+ gpu = gpu_record(args.gpu_index)
+ for key, expected in protocol["expected_gpu"].items():
+ if gpu.get(key) != expected:
+ raise ValueError(
+ f"GPU {key} mismatch: expected {expected!r}, found {gpu.get(key)!r}")
result = {
"schema_version": 1,
"protocol_id": args.protocol,
@@ -310,7 +322,7 @@ def import_result(args):
"source_provenance": source,
"importer_provenance": local_source,
"runtime_environment": environment_record(not args.skip_package_freeze),
- "gpu": gpu_record(args.gpu_index),
+ "gpu": gpu,
"dataset_artifacts": dataset_artifacts,
"run_artifacts": artifacts,
}
diff --git a/experiments/native_baseline_protocols.json b/experiments/native_baseline_protocols.json
index 9edba86..e81874c 100644
--- a/experiments/native_baseline_protocols.json
+++ b/experiments/native_baseline_protocols.json
@@ -8,6 +8,13 @@
"expected_epochs": 400,
"repository_url": "https://github.com/neuralml/BurstCCN-journal",
"source_revision": "9170e110b4d18729dc5bc5d517fc52eefcd04d69",
+ "required_dataset_sha256": [
+ "6d958be074577803d12ecdefd02955f39262c83c16fe9348329d7fe0b5c001ce"
+ ],
+ "expected_gpu": {
+ "physical_index": "5",
+ "name": "NVIDIA GeForce GTX 1080"
+ },
"paper_url": "https://www.biorxiv.org/content/10.64898/2026.06.16.732595v1",
"published_accuracy_percent_mean": 82.97,
"published_accuracy_percent_sd": 0.21,
@@ -57,6 +64,14 @@
"expected_epochs": 130,
"repository_url": "https://github.com/Rasmuskh/dualprop_icml_2024",
"source_revision": "7b2595b34421e1483a721dbfdeff8cdabda3a1ff",
+ "required_dataset_sha256": [
+ "6d958be074577803d12ecdefd02955f39262c83c16fe9348329d7fe0b5c001ce",
+ "0b97b0a786c64f4cd0996cace20e81e189669aba9f4b4e517eb529f9c7105cec"
+ ],
+ "expected_gpu": {
+ "physical_index": "7",
+ "name": "NVIDIA GeForce GTX 1080"
+ },
"paper_url": "https://arxiv.org/abs/2402.08573",
"published_accuracy_percent_mean": 92.41,
"published_accuracy_percent_sd": 0.07,