Files
digital-drosophila/models/male-cns-lif/author-semantics-lab-v1/finalize.py
T
2026-09-30 07:23:09 +08:00

412 lines
25 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Seal a descriptive laboratory audit, including incomplete and failed runs.
Run this with the frozen scientific environment, not the isolated Brian2 venv.
No simulation, parameter selection, candidate promotion or old-artifact write occurs.
"""
from datetime import datetime, timezone
import json
import math
from pathlib import Path
import sys
import traceback
import numpy as np
import pandas as pd
from context import CFG, EXP, MODEL, ROOT, check_prereg, digest, save, source_hashes
from prepare import frozen_integrity
UNIT_NAMES = ("zero", "weak_excitation", "inhibition", "refractory_boundary",
"equal_threshold", "above_threshold", "nonzero_g_reset", "delay_18")
EXCLUDED_PARTS = {".venv", "__pycache__"}
SEAL_NAMES = {"manifest.json", "checksums.sha256"}
def read_json(path):
with Path(path).open(encoding="utf-8") as stream:
return json.load(stream, parse_constant=lambda value: (_ for _ in ()).throw(
ValueError("Nonfinite JSON number: " + value)))
def exception_record(error):
return {"type": type(error).__name__, "message": str(error),
"traceback": traceback.format_exc()}
def attempt(records, name, function):
"""Never let an earlier failed arm hide subsequent available evidence."""
try:
detail = function()
if not isinstance(detail, dict) or type(detail.get("pass")) is not bool:
raise ValueError("Check must provide an explicit Boolean pass")
item = {"name": name, **detail}
except Exception as error:
item = {"name": name, "pass": False, "error": exception_record(error)}
records.append(item)
return item
def require(condition, message):
if not condition:
raise ValueError(message)
def load_trace(directory, expected_nodes, steps, cfg, seed_node, natural_events=False):
events = pd.read_csv(directory / "events.csv", dtype={"step": "int64", "neuron_idx": "int64"})
require(list(events.columns) == ["step", "neuron_idx"], "Unexpected event columns")
for key in ("step", "neuron_idx"):
values = events[key].to_numpy()
require(np.isfinite(values).all() and np.equal(values, np.floor(values)).all(),
"Nonfinite or nonintegral event field: " + key)
ticks = events.step.to_numpy(np.int64)
nodes = events.neuron_idx.to_numpy(np.int64)
require(bool(np.all((ticks >= 0) & (ticks < steps))), "Event outside trial")
require(bool(np.all(np.diff(ticks) >= 0)), "Events not time ordered")
require(not events.duplicated().any(), "Duplicate event at one neuron/timestep")
require(bool(np.isin(nodes, expected_nodes).all()), "Event identity outside motif")
with np.load(directory / "state.npz", allow_pickle=False) as stored:
state = {key: stored[key].copy() for key in stored.files}
require(np.array_equal(state["nodes"], expected_nodes), "State neuron order differs")
history = state["history"]
require(history.shape == (steps, 3, len(expected_nodes)), "Incomplete state history")
require(all(np.isfinite(value).all() for value in state.values()), "Nonfinite state")
actual_counts = np.array([np.count_nonzero(nodes == node) for node in expected_nodes])
require(np.array_equal(state["counts"], actual_counts), "Counts disagree with raw events")
for index, key in enumerate(("v", "g", "ref")):
require(np.array_equal(state[key], history[-1, index]),
"Final state disagrees with history: " + key)
if seed_node is None and not natural_events:
require(len(ticks) == 0, "No-input control emitted a spike")
elif seed_node is not None:
require(np.array_equal(nodes[ticks == 0], [seed_node]),
"Declared initial seed event missing or duplicated")
late = ticks >= round(cfg["late_start_ms"] / cfg["dt_ms"])
per_node = [{"neuron_idx": int(node), "spikes": int(actual_counts[index]),
"late_spikes": int(np.count_nonzero(late & (nodes == node))),
"last_ms": float(ticks[nodes == node][-1] * cfg["dt_ms"])
if actual_counts[index] else None}
for index, node in enumerate(expected_nodes)]
return {"step": ticks, "neuron_idx": nodes, **state}, per_node
def compare_raw(actual, expected, tolerance):
require(math.isfinite(tolerance) and tolerance >= 0, "Invalid preregistered tolerance")
a = np.asarray(actual["history"], np.float64)
b = np.asarray(expected["history"], np.float64)
require(a.shape == b.shape and a.size > 0, "Incomplete comparison trajectories")
require(np.isfinite(a).all() and np.isfinite(b).all(), "Nonfinite comparison trajectory")
error = np.abs(a[:, :2] - b[:, :2])
exact_events = bool(np.array_equal(actual["step"], expected["step"]) and
np.array_equal(actual["neuron_idx"], expected["neuron_idx"]))
first = None
if not exact_events:
for step in range(len(a)):
aa = actual["neuron_idx"][actual["step"] == step]
bb = expected["neuron_idx"][expected["step"] == step]
if not np.array_equal(aa, bb):
first = step
break
prefix = error if first is None else error[:first]
reference_exact = bool(np.array_equal(a[:, 2], b[:, 2]))
count_exact = bool(np.array_equal(actual["counts"], expected["counts"]))
maximum = float(error.max())
return {"exact_events": exact_events, "exact_counts": count_exact,
"ref_exact": reference_exact, "max_abs_mv": maximum,
"mean_abs_mv": float(error.mean()),
"same_event_prefix_max_abs_mv": float(prefix.max()) if prefix.size else None,
"first_event_divergence_step": first, "tolerance_mv": tolerance,
"pass_oracle": exact_events and count_exact and reference_exact and maximum <= tolerance}
def comparison_record_matches(raw, stored):
"""Only descriptive mean reduction may differ between scientific NumPy builds."""
if set(raw) != set(stored):
return False
return all(math.isclose(raw[key], stored[key], rel_tol=0, abs_tol=1e-12)
if key == "mean_abs_mv" else raw[key] == stored[key] for key in raw)
def motif_nodes(cfg, motif):
with np.load(ROOT / cfg["prior_experiment"] / "motifs" / (motif + "-state.npz"),
allow_pickle=False) as prior:
return prior["nodes"].copy()
def check_summary_rows(table, motif, policy, precision, per_node):
part = table[(table.motif == motif) & (table.policy == policy) &
(table.precision == precision)]
require(len(part) == len(per_node) and not part.neuron_idx.duplicated().any(),
"Missing/duplicate per-neuron summary rows")
require(set(part.neuron_idx) == {row["neuron_idx"] for row in per_node},
"Summary neuron identities differ")
indexed = part.set_index("neuron_idx")
for row in per_node:
current = indexed.loc[row["neuron_idx"]]
require(current.spikes == row["spikes"] and current.late_spikes == row["late_spikes"],
"Summary spike counts disagree with raw events")
last = current.last_ms
require(pd.isna(last) if row["last_ms"] is None else last == row["last_ms"],
"Summary last-spike time disagrees with raw events")
def seal(root):
require(not any((root / name).exists() for name in SEAL_NAMES),
"Refusing to overwrite an existing seal: " + str(root))
files = []
for path in sorted(root.rglob("*")):
relative = path.relative_to(root)
if EXCLUDED_PARTS.intersection(relative.parts):
continue
require(not path.is_symlink(), "Unsealed symlink not permitted: " + str(path))
if not path.is_file() or (len(relative.parts) == 1 and path.name in SEAL_NAMES):
continue
files.append({"path": relative.as_posix(), "bytes": path.stat().st_size,
"sha256": digest(path)})
require(bool(files), "Refusing empty artifact manifest")
manifest = {"schema": "sha256-artifact-manifest-v1", "root": str(root),
"created_utc": datetime.now(timezone.utc).isoformat(),
"excluded_directory_names": sorted(EXCLUDED_PARTS),
"self_hash_policy": "Manifest lists payload files; checksums also hashes manifest; neither hashes checksums.",
"files": files}
save(root / "manifest.json", manifest)
checksum_rows = files + [{"path": "manifest.json", "sha256": digest(root / "manifest.json")}]
with (root / "checksums.sha256").open("x", encoding="utf-8", newline="\n") as stream:
for row in checksum_rows:
stream.write(row["sha256"] + " " + row["path"] + "\n")
for row in checksum_rows:
require(digest(root / row["path"]) == row["sha256"],
"Artifact changed while sealing: " + row["path"])
def main():
outputs = [EXP / "audit/finalize-start.json", EXP / "audit/final-summary.json",
EXP / "regression/old-integrity-after.json", EXP / "regression/preregistration-after.json",
EXP / "REPORT.md"] + [root / name for root in (MODEL, EXP) for name in SEAL_NAMES]
require(not any(path.exists() for path in outputs), "Refusing reused finalization outputs")
save(EXP / "audit/finalize-start.json", {"timestamp_utc": datetime.now(timezone.utc).isoformat(),
"python": sys.version, "executable": sys.executable, "learning": False,
"whole_brain_alternative_run": False})
checks = []
prereg = attempt(checks, "preregistration", lambda: {"pass": True, "record": check_prereg()})
if prereg["pass"]:
cfg = prereg["record"]["config"]
else:
# Read the sealed declaration for descriptive recovery, never bless source drift.
try:
cfg = read_json(EXP / "protocol/preregistered.json")["config"]
except Exception:
cfg = CFG
integrity = attempt(checks, "old_frozen_integrity_after", lambda: {
"pass": True, "results": frozen_integrity()})
save(EXP / "regression/old-integrity-after.json", integrity)
def completed():
record = read_json(EXP / "audit/run-complete.json")
require(record.get("learning_achieved") is False and
record.get("whole_brain_alternative_run") is False, "Run scope inconsistent")
require(record.get("author_original_source_sha256") == digest(EXP / "external-resources/model.py"),
"Author source hash disagrees with run-complete record")
return {"pass": record.get("all_author_oracle_comparisons_pass") is True and
record.get("all_frozen_components_match_archive") is True, "record": record}
run_complete = attempt(checks, "run_complete", completed)
def frozen_five_check():
record = read_json(EXP / "regression/frozen-five.json")
trials = record.get("trials", [])
require(len(trials) == 5 and len({row["trial"] for row in trials}) == 5,
"Exactly five distinct frozen reference trials are required")
required = {"step", "neuron_idx", "counts", "v", "g"}
exact = all(set(row.get("exact", {})) == required and
all(value is True for value in row["exact"].values()) for row in trials)
return {"pass": record.get("pass_all") is True and exact, "record": record,
"scope": "Old frozen runtime regression only, not a new-model dynamics gate."}
frozen_five = attempt(checks, "frozen_five_gpu_references", frozen_five_check)
def interface_check():
record = read_json(EXP / "oracle/poi-interface/audit.json")
flags = record.get("checks", {})
require(isinstance(flags, dict) and bool(flags), "Empty Poisson interface audit")
require(record.get("author_sha256") == digest(EXP / "external-resources/model.py"),
"Poisson audit source hash differs")
require(record.get("simulation_duration_ms") == 0 and
record.get("stochastic_time_steps") == 0 and
record.get("random_realization_tested") is False and
record.get("common_seed_experiment") is False, "Poisson audit scope differs")
for name, expected in record.get("input_hashes", {}).items():
require(digest(EXP / "oracle/poi-interface" / name) == expected,
"Poisson fixture input changed: " + name)
return {"pass": record.get("gate") is True and all(value is True for value in flags.values()),
"record": record, "scope": "Compile-time interface audit; no Poisson realization."}
interface = attempt(checks, "author_poi_compile_only", interface_check)
outcomes = []
components = []
steps = round(cfg["duration_ms"] / cfg["dt_ms"])
for motif in cfg["motifs"]:
for policy in cfg["component_policies"]:
def component_check(motif=motif, policy=policy):
nodes = motif_nodes(cfg, motif)
raw, per_node = load_trace(EXP / "components" / (motif + "-" + policy), nodes,
steps, cfg, None if motif == "no_input" else cfg["seed_node"])
outcomes.append({"motif": motif, "policy": policy, "precision": "float32",
"neurons": per_node, "activity_is_not_a_validation_gate": True})
table = pd.read_csv(EXP / "components/summary.csv", float_precision="round_trip")
check_summary_rows(table, motif, policy, "float32", per_node)
if policy == "frozen":
prior_dir = ROOT / cfg["prior_experiment"] / "motifs"
old_events = pd.read_parquet(prior_dir / (motif + "-events.parquet"))
require(np.array_equal(raw["step"], old_events.step) and
np.array_equal(raw["neuron_idx"], old_events.neuron_idx),
"Frozen events disagree with 4AC")
with np.load(prior_dir / (motif + "-state.npz"), allow_pickle=False) as old:
require(all(np.array_equal(old[key], raw[key]) for key in old.files),
"Frozen states disagree with 4AC")
return {"pass": True, "raw_and_summary_consistent": True,
"interpretation": "Artifact/finite/seed checks only; activity is descriptive."}
components.append(attempt(checks, "component:" + motif + ":" + policy, component_check))
unit_results = []
for precision in cfg["author_precisions"]:
for fixture in UNIT_NAMES:
def unit_check(precision=precision, fixture=fixture):
stem = fixture
record = read_json(EXP / "regression" / ("unit-" + stem + "-" + precision + ".json"))
require(record.get("fixture") == fixture and record.get("precision") == precision,
"Unit identity mismatch")
unit_cfg = dict(cfg, duration_ms=20.0)
seeded = fixture in ("refractory_boundary", "above_threshold")
directory = EXP / "oracle" / ("unit-" + stem + "-" + precision)
nodes = np.array([-2, -1]) if fixture == "delay_18" else np.array([-1])
seed = -2 if fixture == "delay_18" else (-1 if seeded else None)
actual, _ = load_trace(directory / "brian-output", nodes, 200, unit_cfg, seed,
natural_events=fixture == "nonzero_g_reset")
independent, _ = load_trace(directory / "independent-output", nodes, 200, unit_cfg, seed,
natural_events=fixture == "nonzero_g_reset")
raw = compare_raw(actual, independent, float(cfg["comparison"][precision + "_state_max_abs_tolerance_mv"]))
stored = read_json(directory / "comparison.json")
require(comparison_record_matches(raw, stored) and
comparison_record_matches(raw, record["comparison"]),
"Unit comparison differs from raw trajectories")
flags = record.get("checks")
require(isinstance(flags, dict) and bool(flags), "Empty unit checks cannot pass")
return {"pass": raw["pass_oracle"] and all(value is True for value in flags.values()),
"checks": flags, "raw_comparison": raw}
unit_results.append(attempt(checks, "unit:" + fixture + ":" + precision, unit_check))
author_results = []
precision_cross = []
for motif in cfg["motifs"]:
by_precision = {}
for precision in cfg["author_precisions"]:
def author_check(motif=motif, precision=precision):
nodes = motif_nodes(cfg, motif)
directory = EXP / "oracle" / (motif + "-" + precision)
seed = None if motif == "no_input" else cfg["seed_node"]
actual, per_node = load_trace(directory / "brian-output", nodes, steps, cfg, seed)
independent, _ = load_trace(directory / "independent-output", nodes, steps, cfg, seed)
by_precision[precision] = actual
outcomes.append({"motif": motif, "policy": "author_complete", "precision": precision,
"neurons": per_node, "activity_is_not_a_validation_gate": True})
raw = compare_raw(actual, independent, float(cfg["comparison"][precision + "_state_max_abs_tolerance_mv"]))
stored = read_json(directory / "comparison.json")
require(comparison_record_matches(raw, stored), "Author comparison differs from raw trajectories")
repeated, _ = load_trace(EXP / "oracle" / (motif + "-" + precision + "-repeat") /
"brian-output", nodes, steps, cfg, seed)
repeat_flags = read_json(EXP / "regression" / (motif + "-" + precision + "-repeat.json"))
require(set(repeat_flags) == set(actual) - {"nodes"}, "Incomplete repeat comparison fields")
repeat_raw = {key: bool(np.array_equal(actual[key], repeated[key])) for key in repeat_flags}
require(repeat_raw == repeat_flags, "Repeat flags disagree with raw arrays")
table = pd.read_csv(EXP / "audit/all-motif-results.csv", float_precision="round_trip")
check_summary_rows(table, motif, "author_complete", precision, per_node)
return {"pass": raw["pass_oracle"] and all(repeat_raw.values()),
"raw_comparison": raw, "repeat_exact": repeat_raw}
author_results.append(attempt(checks, "author:" + motif + ":" + precision, author_check))
def cross_check(motif=motif):
raw = compare_raw(by_precision["float32"], by_precision["float64"],
float(cfg["comparison"]["float32_state_max_abs_tolerance_mv"]))
stored = read_json(EXP / "regression" / (motif + "-precision-cross-comparison.json"))
require(comparison_record_matches(raw, stored), "Precision cross comparison differs from raw trajectories")
return {"pass": True, "comparison": raw,
"interpretation": "Reporting consistency only; cross-precision divergence is allowed and retained."}
precision_cross.append(attempt(checks, "precision_cross_record:" + motif, cross_check))
retained_errors = []
for path in sorted(EXP.rglob("error.json")):
if EXCLUDED_PARTS.intersection(path.relative_to(EXP).parts):
continue
try:
error = read_json(path)
except Exception as exception:
error = exception_record(exception)
retained_errors.append({"path": path.relative_to(EXP).as_posix(), "error": error})
checks.append({"name": "no_recorded_oracle_errors", "pass": not retained_errors,
"errors": retained_errors})
final_prereg = attempt(checks, "preregistration_at_seal", lambda: {"pass": True, "record": check_prereg()})
save(EXP / "regression/preregistration-after.json", {
"initial": prereg, "at_seal": final_prereg, "observed_source_hashes": source_hashes()})
passed = bool(checks) and all(item["pass"] for item in checks)
summary = {"phase": cfg.get("phase", "4AD"),
"status": "COMPLETE-ORACLE-VALIDATED-LAB-ONLY" if passed else "INCOMPLETE-OR-FAILED-LAB-VALIDATION",
"laboratory_validation_pass": passed, "checks": checks,
"component_checks": components, "unit_checks": unit_results,
"frozen_five_gpu_check": frozen_five, "author_poi_interface_check": interface,
"author_oracle_checks": author_results, "precision_cross_checks": precision_cross,
"motif_outcomes": outcomes, "run_complete_record": run_complete,
"whole_brain_alternative_run": False, "conditioning_run": False,
"learning_achieved": False, "candidate_generated": False, "selected_variant": None,
"goal_remains_unachieved": True,
"limits": ["No whole-brain or learning experiment in this protocol.",
"No activity-based variant selection; silence is not a success gate.",
"Isolated motifs omit external network input and graded APL.",
"Author equations on MaleCNS motifs do not reproduce the original paper's full brain or sensory interface.",
"Linux host and Python patch differences prevent a binary-identical author-environment claim."]}
save(EXP / "audit/final-summary.json", summary)
failures = [item for item in checks if not item["pass"]]
lines = ["# Phase 4AD 作者动力学语义实验室", "", "状态:`" + summary["status"] + "`。", "",
"本轮仅有预声明的小回路与算子实验;没有整脑替代动力学运行、conditioning、学习成功或新学习候选。",
"检查以原始事件、状态轨迹和预注册误差界限重算。缺失、失败、非有限值或初始事件丢失均不能通过;放电减少或静默本身不构成成功。", "",
f"核查通过 {sum(item['pass'] for item in checks)}/{len(checks)} 项;失败或不完整 {len(failures)} 项。",
"冻结历史完整性:" + ("通过。" if integrity["pass"] else "失败或未完成,详见保存的错误。"),
"源码与输入预注册一致性:" + ("通过。" if prereg["pass"] and final_prereg["pass"] else "失败或未完成;不得授予验证成功。"), "",
"| 回路 | 语义 | 精度 | 总spikes | late spikes |", "|---|---|---|---:|---:|"]
for row in outcomes:
lines.append("| {} | {} | {} | {} | {} |".format(row["motif"], row["policy"], row["precision"],
sum(item["spikes"] for item in row["neurons"]), sum(item["late_spikes"] for item in row["neurons"])))
if not outcomes:
lines.append("| 无完整可读取结果 | 未执行或缺失 | — | — | — |")
lines.extend(["", "作者Brian2与独立NumPy比较:", "",
"| 实验 | 结果 | 事件一致 | 最大V/g误差 mV | 首次事件分歧step |", "|---|---|---|---:|---:|"])
for item in author_results:
raw = item.get("raw_comparison", {})
lines.append("| {} | {} | {} | {} | {} |".format(item["name"], "通过" if item["pass"] else "失败/缺失",
raw.get("exact_events", "未知"), raw.get("max_abs_mv", "未知"),
raw.get("first_event_divergence_step", "无或未取得")))
lines.extend(["", "float32与float64交叉比较仅记录精度影响,允许保留分歧,不能用于掩盖各自与独立oracle的失败。"])
for item in precision_cross:
raw = item.get("comparison")
if raw:
lines.append(f"- {item['name']}:事件一致={raw['exact_events']},最大误差={raw['max_abs_mv']} mV,首次事件分歧step={raw['first_event_divergence_step']}。")
if failures:
lines.extend(["", "失败与未完成项:", ""])
for item in failures:
detail = item.get("error", {}).get("message") or "原始条件未全部满足;完整字段见 final-summary.json。"
lines.append("- `" + item["name"] + "`:" + str(detail).replace("\n", " "))
lines.extend(["", "所有失败轨迹和错误文件原样保留;本简报没有选择获胜变体。",
"小回路缺少外部整脑输入与graded APL,不能证明整脑因果必要性、生物真实性或信息保持。初始诊断seed也不是作者的感官Poisson接口。",
"环境为Linux宿主,作者记录为Windows;Python补丁版本与环境差异见environment记录,不能称为二进制相同或原论文整脑复现。",
"后续整脑嗅觉比较仍须核查固定输入、实际送达事件、持续活动和信息保持;可用学习仍需至少5个训练seed、独立评估、MBON/downstream的A对中性B特异性及acquisition/reversal/retention/delayed-reward对照。",
"封存排除 .venv 与 __pycache__,保留环境安装报告;manifest列出payload哈希,checksums另含manifest,不存在自哈希循环。", ""])
with (EXP / "REPORT.md").open("x", encoding="utf-8") as stream:
stream.write("\n".join(lines))
seal(MODEL)
seal(EXP)
print(json.dumps({"status": summary["status"], "laboratory_validation_pass": passed,
"failed_checks": len(failures), "learning_achieved": False,
"whole_brain_alternative_run": False, "sealed": [str(MODEL), str(EXP)]},
ensure_ascii=False), flush=True)
return 0 if passed else 1
if __name__ == "__main__":
raise SystemExit(main())