Validate isolated author LIF semantics without changing frozen models
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
# Phase 4AD — independently authorized author-semantics laboratory
|
||||
|
||||
Human authorization: "允许独立新模型分支测试动力学假设".
|
||||
Goal remains usable stimulus-specific learning, not reducing spike count or passing an isolated fixture.
|
||||
|
||||
All Phase 1–4AC scientific artifacts, canonical CSR/raw counts, transmitter signs,
|
||||
W_syn=0.275, APL calibration, eligibility-v2 and the 14,232-edge plastic mask are
|
||||
read-only. No training, threshold/gain/eta search, decoder, game or visual work in
|
||||
this prerequisite laboratory. Existing candidate names are not replaced.
|
||||
|
||||
## Fixed comparison before results
|
||||
|
||||
Use the exact five preselected 4AC motifs and the same initial single seed event,
|
||||
not newly selected cells. Record all five outcomes, including persistent firing.
|
||||
Preserve true edge IDs, counts and original global neuron_idx/body_id.
|
||||
|
||||
1. Reproduce frozen explicit float32 Euler/operator semantics exactly, using 4AC
|
||||
archived event/state arrays as an independent oracle.
|
||||
2. Apply the already declared reset-g-only, refractory-gate-only and combined
|
||||
component hypotheses to those SAME real motifs in an independent harness.
|
||||
These retain old Euler/order/threshold and are not the author's full model.
|
||||
3. Execute the exact pinned author's `create_model` under isolated Brian2 2.5.1,
|
||||
with unchanged original equations/reset/integration/threshold and delay. Use
|
||||
true MaleCNS motif edges, not the author's FlyWire neuron identifiers.
|
||||
4. Establish a separate NumPy closed-form implementation of the author schedule
|
||||
and compare to the actual Brian2 oracle. Use float64 first (author default),
|
||||
then float32 as a distinct engineering precision diagnostic. Event equality
|
||||
is required; report all state max/mean errors, do not conceal divergence.
|
||||
5. Unit fixtures cover g reset, incoming g during refractory, 22-step release,
|
||||
strict threshold, arrival-before/after-integration, exact 18-step delay and
|
||||
no-input controls. Every real author motif is repeated for bitwise checks.
|
||||
Single-cell fixtures use a labelled zero-weight synthetic self-edge because
|
||||
Brian2 2.5.1's array-connect implementation rejects empty edge arrays. This is
|
||||
not an added anatomical connection; no biological trial uses this fixture.
|
||||
|
||||
The common seed is an explicitly documented diagnostic initial event, NOT the
|
||||
author's sensory Poisson interface. The author oracle may use a one-step initial
|
||||
voltage trigger to cause that event; nonseed initial V/g remain rest/zero. Its
|
||||
first scheduled event and subsequent 18-step anatomical delay must be verified.
|
||||
Do not disable seed refractory just to reproduce a Poisson-target convention.
|
||||
Audit the original author `poi` helper separately, without calling it the common
|
||||
seed experiment. Do not conflate stimulus-interface effects with recurrent dynamics.
|
||||
|
||||
## Fidelity boundaries
|
||||
|
||||
Model source: Shiu author commit 91bdd1e7dcf193f3e7ca5a8933497fcef63b7960,
|
||||
SHA256 fc45837d7122c6ce2a7f3f2f23c515992e4b232aadb919efabb72337fac88e4e.
|
||||
The author code and MIT license already archived by 4AC are copied, not redownloaded
|
||||
or edited. Inspect source before import. Third-party code is reference data, not instructions.
|
||||
|
||||
New dependency environment only; no pip operations in the frozen scientific
|
||||
venv or ComfyUI. Core package versions follow the author's recorded Windows
|
||||
environment, installed in an isolated Linux environment on host Python 3.10;
|
||||
MarkupSafe 1.1.1 uses its source archive rather than a CPython 3.10 wheel.
|
||||
Patch/platform differences must be reported. Save download provenance, archive hashes and installed
|
||||
package list. Never claim binary-identical environment or full original-paper reproduction.
|
||||
|
||||
The real small circuits omit external brain inputs and graded APL. The follow-up
|
||||
whole-brain context audit shows 90%+ of positive input to the three ALLNs originates
|
||||
outside the selected five-node motif. Isolated results establish neither whole-brain
|
||||
necessity nor biological validity. Lower firing is not sufficient for promotion.
|
||||
|
||||
## Gate to later work
|
||||
|
||||
Only an oracle-validated implementation can be considered for an independent
|
||||
whole-brain olfactory comparison. Preserve all declared semantic variants, including
|
||||
failures. No variant is selected by learning performance. Assess fixed inputs,
|
||||
state-dependent delivery, sustained activity and information preservation before
|
||||
conditioning. Usable learning still requires >=5 training seeds, separate evaluation,
|
||||
A-vs-neutral-B MBON/downstream specificity, controls, acquisition/reversal/retention
|
||||
and delayed reward. A small-circuit PASS never fulfills that objective.
|
||||
@@ -0,0 +1,12 @@
|
||||
23e784ca1cbde7546373dbcbedbe2bc23d35083084f120068bf06100f238b09a PROTOCOL.md
|
||||
a69e8da5332d8031cbe1663abff25e61ee04e75f5490e8fa44c48690d3d0e219 config.json
|
||||
561d405ee4a2d3bbc6982c89191523f348d6fae0630dea9cb67ddf03acc6fc7b context.py
|
||||
97a393b95d100dc5d95b624e4cd0d15511307b8c70e0ebd788b773c143ebfed6 finalize.py
|
||||
1e2b31545f1206fe1c1f4084b4a9ac0f21c74990c52e8fd156e809725fc5fb95 interface_audit.py
|
||||
9663da41d5f623e1d7750e3cad3df880148079968f115bf583b58799fc5fb31f math_lab.py
|
||||
2f4b073261965cf18a94697caf725c40b2e8db790c2871862201337f40c399e5 oracle_runner.py
|
||||
49caa9553938f06539ecf282868491f8e1319d8f24220f89ac9e232db2d82e51 prepare.py
|
||||
a69efc6509b85a5cc00a652caa4820c9c9e1406f6b97c6cc741322728c4d51c3 requirements-oracle.txt
|
||||
8746e1971c5a40a738e10f8f574a243c29cefa5a8b9f61a2846557ceffd1289a run_lab.py
|
||||
fe37a48ce804e6c50f93404bfd3c23a7e54cd49061bbdfb4860315879648c73b verify_frozen.py
|
||||
07a22bade4892adca3a4585656aa3ecce5219790b0dc8a851a12bfd7248d3769 manifest.json
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "author-semantics-lab-v1",
|
||||
"phase": "4AD",
|
||||
"root": "/data/digital-drosophila",
|
||||
"experiment": "experiments/phase4ad-author-semantics",
|
||||
"model_directory": "models/male-cns-lif/author-semantics-lab-v1",
|
||||
"prior_experiment": "experiments/phase4ac-lif-semantics",
|
||||
"prior_model": "models/male-cns-lif/lif-semantics-audit-v1",
|
||||
"human_authorization": "允许独立新模型分支测试动力学假设",
|
||||
"dt_ms": 0.1,
|
||||
"duration_ms": 1000.0,
|
||||
"seed_margin_mv": 1.0,
|
||||
"late_start_ms": 500.0,
|
||||
"model": {
|
||||
"dt_ms": 0.1,
|
||||
"membrane_tau_ms": 20.0,
|
||||
"synaptic_tau_ms": 5.0,
|
||||
"refractory_ms": 2.2,
|
||||
"resting_potential_mv": -52.0,
|
||||
"reset_potential_mv": -52.0,
|
||||
"threshold_mv": -45.0,
|
||||
"synaptic_delay_ms": 1.8,
|
||||
"w_syn_mv_per_anatomical_synapse": 0.275
|
||||
},
|
||||
"seed_node": 1381,
|
||||
"motifs": ["pair", "trio", "five", "feedforward", "no_input"],
|
||||
"component_policies": ["frozen", "reset_g_only", "refractory_gate_only", "reset_and_refractory"],
|
||||
"author_precisions": ["float64", "float32"],
|
||||
"comparison": {
|
||||
"float64_state_max_abs_tolerance_mv": 0.00000001,
|
||||
"float32_state_max_abs_tolerance_mv": 0.001,
|
||||
"exact_spike_events_required_for_oracle_validation": true,
|
||||
"precision_cross_comparison_may_diverge_but_must_be_reported": true
|
||||
},
|
||||
"no_training_or_candidate": true
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
MODEL=Path(__file__).resolve().parent
|
||||
CFG=json.loads((MODEL/'config.json').read_text(encoding='utf-8'))
|
||||
ROOT=Path(CFG['root']);EXP=ROOT/CFG['experiment'];AC=ROOT/CFG['prior_experiment']
|
||||
|
||||
def digest(path):
|
||||
h=hashlib.sha256()
|
||||
with Path(path).open('rb') as f:
|
||||
for b in iter(lambda:f.read(8<<20),b''):h.update(b)
|
||||
return h.hexdigest()
|
||||
|
||||
def save(path,value):
|
||||
path=Path(path);path.parent.mkdir(parents=True,exist_ok=True)
|
||||
with path.open('x',encoding='utf-8') as f:json.dump(value,f,indent=2,ensure_ascii=False,allow_nan=False);f.write('\n')
|
||||
|
||||
def source_hashes():
|
||||
return {p.name:digest(p) for p in sorted(MODEL.iterdir()) if p.is_file() and p.name not in ('manifest.json','checksums.sha256')}
|
||||
|
||||
def check_prereg():
|
||||
pre=json.loads((EXP/'protocol/preregistered.json').read_text())
|
||||
assert pre['source_hashes']==source_hashes(),'Source changed after preregistration'
|
||||
for r in pre['input_hashes']:
|
||||
assert digest(ROOT/r['path'])==r['sha256'],r['path']
|
||||
return pre
|
||||
@@ -0,0 +1,411 @@
|
||||
"""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())
|
||||
@@ -0,0 +1,149 @@
|
||||
"""Compile-only audit of the pinned author's sensory Poisson interface.
|
||||
|
||||
This is separate from the common single-seed diagnostic. Zero simulation time
|
||||
is advanced, so this audit does not draw or validate any Poisson realization.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import types
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
from context import CFG, EXP, digest, save
|
||||
from oracle_runner import AUTHOR_COMMIT, AUTHOR_SHA256, _code_records, _environment
|
||||
|
||||
|
||||
def audit_author_poi() -> dict:
|
||||
"""Create the original model and poi helper, compile at t=0, and save evidence."""
|
||||
author_path = EXP / "external-resources" / "model.py"
|
||||
author_bytes = author_path.read_bytes()
|
||||
source_hash = hashlib.sha256(author_bytes).hexdigest()
|
||||
if source_hash != AUTHOR_SHA256:
|
||||
raise ValueError("The Poisson audit requires the exact pinned author source")
|
||||
output = EXP / "oracle" / "poi-interface"
|
||||
output.mkdir(parents=True, exist_ok=False)
|
||||
environment = _environment()
|
||||
save(output / "environment.json", environment)
|
||||
if environment["packages"]["brian2"]["actual"] != "2.5.1":
|
||||
raise RuntimeError("The Poisson interface audit requires Brian2 2.5.1")
|
||||
|
||||
import brian2 as b2
|
||||
|
||||
preferences = {name: b2.prefs[name] for name in (
|
||||
"codegen.target", "core.default_float_dtype", "legacy.refractory_timing")}
|
||||
old_dt = b2.defaultclock.dt
|
||||
try:
|
||||
b2.start_scope()
|
||||
b2.prefs["codegen.target"] = "numpy"
|
||||
b2.prefs["core.default_float_dtype"] = np.float64
|
||||
b2.prefs["legacy.refractory_timing"] = False
|
||||
b2.defaultclock.dt = float(CFG["dt_ms"]) * b2.ms
|
||||
author = types.ModuleType("phase4ad_poi_author_" + source_hash[:12])
|
||||
author.__file__ = str(author_path)
|
||||
# Execute exactly the reviewed bytes; create no bytecode beside the source.
|
||||
exec(compile(author_bytes, str(author_path), "exec"), author.__dict__)
|
||||
parameters = dict(author.default_params)
|
||||
|
||||
nodes = np.array([-2, -1], dtype=np.int32)
|
||||
completeness = pd.DataFrame(
|
||||
{"local_index": np.array([0, 1], dtype=np.int32)},
|
||||
index=pd.Index(nodes, name="synthetic_global_neuron_idx"))
|
||||
connections = pd.DataFrame({
|
||||
"Presynaptic_Index": np.array([0], dtype=np.int32),
|
||||
"Postsynaptic_Index": np.array([1], dtype=np.int32),
|
||||
"Excitatory x Connectivity": np.array([1], dtype=np.int64),
|
||||
"raw_count": np.array([1], dtype=np.int64),
|
||||
"edge_id": np.array([-1], dtype=np.int64),
|
||||
"source_neuron_idx": nodes[:1], "target_neuron_idx": nodes[1:],
|
||||
})
|
||||
comp_path = output / "completeness.csv"
|
||||
con_path = output / "connectivity.parquet"
|
||||
completeness.to_csv(comp_path)
|
||||
connections.to_parquet(con_path, index=False, engine="pyarrow")
|
||||
neu, syn, spikes = author.create_model(comp_path, con_path, parameters)
|
||||
rfc_before = np.asarray(neu.rfc[:] / b2.ms, dtype=np.float64).copy()
|
||||
# One excited target plus one untouched cell makes the helper's scope
|
||||
# observable. The optional second-rate class is not exercised here.
|
||||
inputs, same_neu = author.poi(neu, [0], [], parameters)
|
||||
if same_neu is not neu:
|
||||
raise AssertionError("The author poi helper returned a different neuron group")
|
||||
rfc_after = np.asarray(neu.rfc[:] / b2.ms, dtype=np.float64).copy()
|
||||
before_v = np.asarray(neu.v_[:], dtype=np.float64).copy()
|
||||
before_g = np.asarray(neu.g_[:], dtype=np.float64).copy()
|
||||
before_w = np.asarray(syn.w_[:], dtype=np.float64).copy()
|
||||
network = b2.Network(neu, syn, spikes, *inputs)
|
||||
save(output / "schedule.json", {
|
||||
"schedule": list(network.schedule),
|
||||
"summary": str(network.scheduling_summary()),
|
||||
})
|
||||
network.run(0 * b2.ms, namespace={})
|
||||
save(output / "generated-code.json", _code_records(network))
|
||||
|
||||
input_records = []
|
||||
for item in inputs:
|
||||
# PoissonInput 2.5.1 retains its on-the-fly subgroup as _group;
|
||||
# N/rate/weight/target_var are its public read-only properties.
|
||||
target = item._group
|
||||
input_records.append({
|
||||
"name": item.name, "type": type(item).__name__,
|
||||
"target_group": target.name,
|
||||
"target_local_start": int(target.start),
|
||||
"target_local_stop": int(target.stop),
|
||||
"target_global_nodes": nodes[int(target.start):int(target.stop)].tolist(),
|
||||
"target_var": item.target_var, "N": int(item.N),
|
||||
"rate_hz": float(item.rate / b2.Hz),
|
||||
"weight_mv": float(item.weight / b2.mV),
|
||||
"weight_volt": float(item.weight / b2.volt),
|
||||
"when": item.when, "order": int(item.order),
|
||||
"dt_ms": float(item.clock.dt / b2.ms),
|
||||
"abstract_code": str(item.abstract_code),
|
||||
"generated_code": [str(code.code) for code in item.code_objects],
|
||||
})
|
||||
expected_rfc = float(parameters["t_rfc"] / b2.ms)
|
||||
expected_weight = float(parameters["w_syn"] * parameters["f_poi"] / b2.mV)
|
||||
expected_rate = float(parameters["r_poi"] / b2.Hz)
|
||||
checks = {
|
||||
"exact_author_source": digest(author_path) == source_hash,
|
||||
"one_poisson_input": len(input_records) == 1,
|
||||
"initial_refractory_is_original": bool(np.array_equal(rfc_before, [expected_rfc, expected_rfc])),
|
||||
"only_excited_target_loses_refractory": bool(np.array_equal(rfc_after, [0., expected_rfc])),
|
||||
"actual_voltage_interface": all(r["target_var"] == "v" for r in input_records),
|
||||
"actual_N_one": all(r["N"] == 1 for r in input_records),
|
||||
"actual_rate_150_hz": bool(expected_rate == 150. and all(r["rate_hz"] == expected_rate for r in input_records)),
|
||||
"actual_weight_68_75_mv": bool(np.isclose(expected_weight, 68.75, rtol=0, atol=1e-12)
|
||||
and all(np.isclose(r["weight_mv"], expected_weight, rtol=0, atol=1e-12) for r in input_records)),
|
||||
"actual_synapses_slot_order_zero": all(r["when"] == "synapses" and r["order"] == 0 for r in input_records),
|
||||
"zero_elapsed_time": float(network.t / b2.ms) == 0.,
|
||||
"no_recorded_spikes": len(spikes.i[:]) == 0,
|
||||
"compile_preserves_voltage": bool(np.array_equal(before_v, np.asarray(neu.v_[:]))),
|
||||
"compile_preserves_g": bool(np.array_equal(before_g, np.asarray(neu.g_[:]))),
|
||||
"compile_preserves_weights": bool(np.array_equal(before_w, np.asarray(syn.w_[:]))),
|
||||
"compile_preserves_refractory_configuration": bool(np.array_equal(rfc_after, np.asarray(neu.rfc[:] / b2.ms))),
|
||||
}
|
||||
result = {
|
||||
"gate": bool(all(checks.values())), "checks": checks,
|
||||
"author_commit": AUTHOR_COMMIT, "author_sha256": source_hash,
|
||||
"fixture": "two synthetic nodes, one raw_count=1 connection",
|
||||
"nodes": nodes.tolist(), "exc_local_indices": [0], "exc2_local_indices": [],
|
||||
"rfc_before_ms": rfc_before.tolist(), "rfc_after_ms": rfc_after.tolist(),
|
||||
"poisson_inputs": input_records,
|
||||
"synaptic_weight_mv": np.asarray(syn.w[:] / b2.mV, dtype=np.float64).tolist(),
|
||||
"input_hashes": {"completeness.csv": digest(comp_path), "connectivity.parquet": digest(con_path)},
|
||||
"simulation_duration_ms": 0., "stochastic_time_steps": 0,
|
||||
"random_realization_tested": False, "common_seed_experiment": False,
|
||||
"interpretation": "Original poi adds 68.75 mV to voltage at 150 Hz with N=1 in the synapses slot and sets its target rfc to zero. This interface is not the common initial seed experiment.",
|
||||
"scope": "Compile-time interface audit only; no stochastic trial, learning, or whole-brain simulation. The optional exc2 helper branch is not exercised.",
|
||||
}
|
||||
save(output / "audit.json", result)
|
||||
if not result["gate"]:
|
||||
raise AssertionError("Author Poisson interface gate failed: " + str(checks))
|
||||
return result
|
||||
except Exception as error:
|
||||
save(output / "error.json", {"type": type(error).__name__, "message": str(error)})
|
||||
raise
|
||||
finally:
|
||||
for name, value in preferences.items():
|
||||
b2.prefs[name] = value
|
||||
b2.defaultclock.dt = old_dt
|
||||
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"schema": "sha256-artifact-manifest-v1",
|
||||
"root": "/data/digital-drosophila/models/male-cns-lif/author-semantics-lab-v1",
|
||||
"created_utc": "2026-09-29T23:19:04.144370+00:00",
|
||||
"excluded_directory_names": [
|
||||
".venv",
|
||||
"__pycache__"
|
||||
],
|
||||
"self_hash_policy": "Manifest lists payload files; checksums also hashes manifest; neither hashes checksums.",
|
||||
"files": [
|
||||
{
|
||||
"path": "PROTOCOL.md",
|
||||
"bytes": 4577,
|
||||
"sha256": "23e784ca1cbde7546373dbcbedbe2bc23d35083084f120068bf06100f238b09a"
|
||||
},
|
||||
{
|
||||
"path": "config.json",
|
||||
"bytes": 1332,
|
||||
"sha256": "a69e8da5332d8031cbe1663abff25e61ee04e75f5490e8fa44c48690d3d0e219"
|
||||
},
|
||||
{
|
||||
"path": "context.py",
|
||||
"bytes": 1057,
|
||||
"sha256": "561d405ee4a2d3bbc6982c89191523f348d6fae0630dea9cb67ddf03acc6fc7b"
|
||||
},
|
||||
{
|
||||
"path": "finalize.py",
|
||||
"bytes": 25910,
|
||||
"sha256": "97a393b95d100dc5d95b624e4cd0d15511307b8c70e0ebd788b773c143ebfed6"
|
||||
},
|
||||
{
|
||||
"path": "interface_audit.py",
|
||||
"bytes": 8415,
|
||||
"sha256": "1e2b31545f1206fe1c1f4084b4a9ac0f21c74990c52e8fd156e809725fc5fb95"
|
||||
},
|
||||
{
|
||||
"path": "math_lab.py",
|
||||
"bytes": 4533,
|
||||
"sha256": "9663da41d5f623e1d7750e3cad3df880148079968f115bf583b58799fc5fb31f"
|
||||
},
|
||||
{
|
||||
"path": "oracle_runner.py",
|
||||
"bytes": 18548,
|
||||
"sha256": "2f4b073261965cf18a94697caf725c40b2e8db790c2871862201337f40c399e5"
|
||||
},
|
||||
{
|
||||
"path": "prepare.py",
|
||||
"bytes": 3453,
|
||||
"sha256": "49caa9553938f06539ecf282868491f8e1319d8f24220f89ac9e232db2d82e51"
|
||||
},
|
||||
{
|
||||
"path": "requirements-oracle.txt",
|
||||
"bytes": 453,
|
||||
"sha256": "a69efc6509b85a5cc00a652caa4820c9c9e1406f6b97c6cc741322728c4d51c3"
|
||||
},
|
||||
{
|
||||
"path": "run_lab.py",
|
||||
"bytes": 10795,
|
||||
"sha256": "8746e1971c5a40a738e10f8f574a243c29cefa5a8b9f61a2846557ceffd1289a"
|
||||
},
|
||||
{
|
||||
"path": "verify_frozen.py",
|
||||
"bytes": 2613,
|
||||
"sha256": "fe37a48ce804e6c50f93404bfd3c23a7e54cd49061bbdfb4860315879648c73b"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
"""Independent event-scheduled author equation and preserved frozen component arms."""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import numpy as np
|
||||
|
||||
sys.path.insert(0,'/data/digital-drosophila/models/male-cns-lif/lif-semantics-audit-v1')
|
||||
from math_reference import params, state
|
||||
|
||||
|
||||
def component(nodes,edges,weights,config,policy,seed_node):
|
||||
p=params(config['model']);n=len(nodes);steps=round(config['duration_ms']/config['dt_ms'])
|
||||
ix={int(x):i for i,x in enumerate(nodes)}
|
||||
src=np.array([ix[int(x)] for x in edges.source_neuron_idx],np.int32)
|
||||
dst=np.array([ix[int(x)] for x in edges.target_neuron_idx],np.int32)
|
||||
v=np.full(n,p['rest'],np.float32);g=np.zeros(n,np.float32);ref=np.zeros(n,np.int32)
|
||||
qlen=p['delay_steps']+1;q=np.zeros((qlen,n),np.float32)
|
||||
history=np.empty((steps,3,n),np.float32);counts=np.zeros(n,np.int32);ts=[];ns=[]
|
||||
for t in range(steps):
|
||||
v,g,ref,fire=state(v,g,ref,q[t%qlen],p,policy);q[t%qlen]=0
|
||||
if t==0 and seed_node is not None:
|
||||
k=ix[seed_node];assert not fire[k] and ref[k]==0
|
||||
fire[k]=True;v[k]=p['reset'];ref[k]=p['ref_steps']
|
||||
if policy in ('reset_g_only','reset_and_refractory'):g[k]=np.float32(0)
|
||||
counts+=fire;active=np.flatnonzero(fire);ts.extend([t]*len(active));ns.extend(nodes[active].tolist())
|
||||
selected=np.flatnonzero(fire[src]);selected=selected[np.argsort(dst[selected],kind='stable')]
|
||||
for target in np.unique(dst[selected]):
|
||||
acc=np.float32(0)
|
||||
for e in selected[dst[selected]==target]:acc=np.float32(acc+weights[e])
|
||||
slot=(t+p['delay_steps'])%qlen;q[slot,target]=np.float32(q[slot,target]+acc)
|
||||
history[t]=v,g,ref
|
||||
return dict(step=np.array(ts,np.int32),neuron_idx=np.array(ns,np.int32),counts=counts,v=v,g=g,ref=ref,history=history,queue=q)
|
||||
|
||||
|
||||
def author_numpy(nodes,edges,config,precision,seed_node,external_arrivals=None):
|
||||
"""Analytic integration in SI volts; sequential arrival writes follow spike detection."""
|
||||
f=np.dtype(precision).type;m=config['model'];n=len(nodes);dt=config['dt_ms']
|
||||
steps=round(config['duration_ms']/dt);rs=round(m['refractory_ms']/dt);ds=round(m['synaptic_delay_ms']/dt)
|
||||
ix={int(x):i for i,x in enumerate(nodes)}
|
||||
src=np.array([ix[int(x)] for x in edges.source_neuron_idx],np.int32)
|
||||
dst=np.array([ix[int(x)] for x in edges.target_neuron_idx],np.int32)
|
||||
weights=np.asarray(edges.raw_count.to_numpy(np.float64)*(m['w_syn_mv_per_anatomical_synapse']*.001),dtype=precision)
|
||||
rest=m['resting_potential_mv']*.001;reset=m['reset_potential_mv']*.001;threshold=m['threshold_mv']*.001
|
||||
v=np.full(n,rest,dtype=precision);g=np.zeros(n,dtype=precision)
|
||||
last=np.full(n,-1000000,np.int64);counts=np.zeros(n,np.int32)
|
||||
em=np.exp(-dt/m['membrane_tau_ms']);es=np.exp(-dt/m['synaptic_tau_ms'])
|
||||
drive=m['synaptic_tau_ms']/(m['membrane_tau_ms']-m['synaptic_tau_ms'])*(em-es)
|
||||
history=np.empty((steps,3,n),np.float64);queue=[[] for _ in range(ds+1)];ts=[];ns=[]
|
||||
external=np.zeros((steps,n),dtype=precision) if external_arrivals is None else np.asarray(np.asarray(external_arrivals,np.float64)*.001,dtype=precision)
|
||||
assert external.shape==(steps,n)
|
||||
for t in range(steps):
|
||||
available=(t-last)>=rs
|
||||
# Use previous g for the coupled exact differential update, then cast to variable dtype.
|
||||
oldg=g.copy()
|
||||
v[available]=np.asarray(rest+em*(v[available]-rest)+drive*oldg[available],dtype=precision)
|
||||
g[available]=np.asarray(es*oldg[available],dtype=precision)
|
||||
if t==0 and seed_node is not None:v[ix[seed_node]]=f(threshold+config['seed_margin_mv']*.001)
|
||||
fire=available&(v>threshold);last[fire]=t;counts+=fire
|
||||
active=np.flatnonzero(fire);ts.extend([t]*len(active));ns.extend(nodes[active].tolist())
|
||||
accept=available&~fire
|
||||
due=queue[t%(ds+1)];queue[t%(ds+1)]=[]
|
||||
for e in due:
|
||||
if accept[dst[e]]:g[dst[e]]=f(g[dst[e]]+weights[e])
|
||||
for j in np.flatnonzero(external[t]):
|
||||
if accept[j]:g[j]=f(g[j]+external[t,j])
|
||||
queue[(t+ds)%(ds+1)].extend(np.flatnonzero(fire[src]).tolist())
|
||||
v[fire]=f(reset);g[fire]=f(0)
|
||||
ref=np.maximum(rs-(t-last),0).astype(np.int32)
|
||||
history[t]=np.asarray(v,np.float64)*1000,np.asarray(g,np.float64)*1000,ref
|
||||
return dict(step=np.array(ts,np.int32),neuron_idx=np.array(ns,np.int32),counts=counts,
|
||||
v=np.asarray(v,np.float64)*1000,g=np.asarray(g,np.float64)*1000,ref=ref,history=history,weights=np.asarray(weights,np.float64)*1000)
|
||||
@@ -0,0 +1,346 @@
|
||||
"""Run the pinned author's create_model in a dedicated, serial Brian2 process.
|
||||
|
||||
This module never edits the author source or its parameters. The caller supplies
|
||||
an exclusive output directory and the already validated, signed connectivity
|
||||
multipliers in ``raw_count``. Global IDs are not FlyWire IDs and are mapped only
|
||||
for this small-circuit diagnostic. Brian stores voltages in SI volts internally;
|
||||
the returned state and weights are converted to mV in the selected dtype.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import importlib.metadata
|
||||
import json
|
||||
import platform
|
||||
import sys
|
||||
import types
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
|
||||
AUTHOR_SHA256 = "fc45837d7122c6ce2a7f3f2f23c515992e4b232aadb919efabb72337fac88e4e"
|
||||
AUTHOR_COMMIT = "91bdd1e7dcf193f3e7ca5a8933497fcef63b7960"
|
||||
PACKAGE_PINS = {
|
||||
"brian2": "2.5.1", "numpy": "1.22.3", "pandas": "1.4.3",
|
||||
"scipy": "1.8.1", "pyarrow": "11.0.0", "joblib": "1.2.0",
|
||||
"cython": "0.29.33", "jinja2": "2.11.3", "markupsafe": "1.1.1",
|
||||
"sympy": "1.12", "mpmath": "1.3.0", "pyparsing": "3.0.9",
|
||||
"packaging": "23.1", "setuptools": "67.8.0",
|
||||
"python-dateutil": "2.8.2", "pytz": "2023.3", "six": "1.16.0",
|
||||
"wheel": "0.38.4",
|
||||
}
|
||||
|
||||
|
||||
def _json(path: Path, value) -> None:
|
||||
with path.open("x", encoding="utf-8") as output:
|
||||
json.dump(value, output, indent=2, sort_keys=True, allow_nan=False)
|
||||
output.write("\n")
|
||||
|
||||
|
||||
def _steps(value: float, dt: float, label: str, *, positive=False) -> int:
|
||||
ratio = float(value) / dt
|
||||
if not np.isfinite(ratio) or ratio < 0:
|
||||
raise ValueError(f"{label} must be finite and nonnegative")
|
||||
result = int(round(ratio))
|
||||
if not np.isclose(ratio, result, rtol=0, atol=1e-9):
|
||||
raise ValueError(f"{label} must be an integer number of dt steps")
|
||||
if result > np.iinfo(np.int32).max or (positive and result == 0):
|
||||
raise ValueError(f"{label} is outside the supported step range")
|
||||
return result
|
||||
|
||||
|
||||
def _int_vector(values, label: str, dtype=np.int64) -> np.ndarray:
|
||||
source = np.asarray(values)
|
||||
if source.ndim != 1 or source.dtype.kind not in "iu":
|
||||
raise ValueError(f"{label} must be a one-dimensional integer array")
|
||||
limits = np.iinfo(dtype)
|
||||
if np.any(source < limits.min) or np.any(source > limits.max):
|
||||
raise ValueError(f"{label} is outside {np.dtype(dtype).name} range")
|
||||
return source.astype(dtype, copy=True)
|
||||
|
||||
|
||||
def _environment() -> dict:
|
||||
packages = {}
|
||||
for name, expected in PACKAGE_PINS.items():
|
||||
try:
|
||||
actual = importlib.metadata.version(name)
|
||||
except importlib.metadata.PackageNotFoundError:
|
||||
actual = None
|
||||
packages[name] = {"expected": expected, "actual": actual,
|
||||
"matches": actual == expected}
|
||||
return {
|
||||
"python": platform.python_version(), "author_python": "3.10.11",
|
||||
"python_patch_matches": platform.python_version() == "3.10.11",
|
||||
"python_executable": sys.executable, "platform": platform.platform(),
|
||||
"author_platform": "Windows", "packages": packages,
|
||||
"all_package_pins_match": all(p["matches"] for p in packages.values()),
|
||||
"binary_identical_author_environment_claimed": False,
|
||||
}
|
||||
|
||||
|
||||
def _code_text(value):
|
||||
if isinstance(value, dict):
|
||||
return {str(key): _code_text(item) for key, item in value.items()}
|
||||
return str(value)
|
||||
|
||||
|
||||
def _code_records(network) -> list:
|
||||
records = []
|
||||
for obj in network.sorted_objects:
|
||||
record = {"name": obj.name, "type": type(obj).__name__,
|
||||
"when": obj.when, "order": int(obj.order),
|
||||
"active": bool(obj.active)}
|
||||
for key in ("abstract_code", "user_code"):
|
||||
if hasattr(obj, key):
|
||||
record[key] = _code_text(getattr(obj, key))
|
||||
record["code_objects"] = [
|
||||
{"name": code.name, "type": type(code).__name__,
|
||||
"generated_code": _code_text(code.code)}
|
||||
for code in getattr(obj, "code_objects", ())
|
||||
]
|
||||
records.append(record)
|
||||
return records
|
||||
|
||||
|
||||
def run_brian(author_path: Path, io_dir: Path, nodes: np.ndarray,
|
||||
edges: pd.DataFrame, config: dict, precision: str,
|
||||
seed_node: int | None, external_arrivals=None) -> dict:
|
||||
"""Execute an unchanged author model and return canonical end-of-tick arrays.
|
||||
|
||||
``external_arrivals`` is either None or a (T, N) array in mV. Every nonzero
|
||||
entry gets its own SpikeGeneratorGroup source and zero-delay conditional
|
||||
synaptic write, ordered after the anatomical synapses. It is not direct
|
||||
Python assignment to g. ``seed_margin_mv`` may be zero for a strict-threshold
|
||||
fixture; positive margins must remain representably above the threshold.
|
||||
|
||||
This function controls process-global Brian preferences and is not thread
|
||||
safe. Use only the isolated oracle environment, serially or in subprocesses.
|
||||
A failed run keeps its exclusive inputs and an error record for diagnosis.
|
||||
"""
|
||||
if precision not in ("float64", "float32"):
|
||||
raise ValueError("precision must be float64 or float32")
|
||||
dtype = np.dtype(precision)
|
||||
nodes = _int_vector(nodes, "nodes", np.int32)
|
||||
if not len(nodes) or np.any(np.diff(nodes.astype(np.int64)) <= 0):
|
||||
raise ValueError("nodes must be nonempty, sorted, and unique")
|
||||
required = ["edge_id", "source_neuron_idx", "target_neuron_idx", "raw_count"]
|
||||
if not set(required).issubset(edges.columns):
|
||||
raise ValueError(f"edges require columns {required}")
|
||||
edge_ids = _int_vector(edges.edge_id, "edge_id")
|
||||
if np.any(np.diff(edge_ids) <= 0):
|
||||
raise ValueError("edge_id order must be canonical, increasing, and unique")
|
||||
sources = _int_vector(edges.source_neuron_idx, "source_neuron_idx", np.int32)
|
||||
targets = _int_vector(edges.target_neuron_idx, "target_neuron_idx", np.int32)
|
||||
multipliers = _int_vector(edges.raw_count, "raw_count")
|
||||
if not np.isin(sources, nodes).all() or not np.isin(targets, nodes).all():
|
||||
raise ValueError("all edge endpoints must occur in nodes")
|
||||
lookup = {int(node): i for i, node in enumerate(nodes)}
|
||||
if seed_node is not None and int(seed_node) not in lookup:
|
||||
raise ValueError("seed_node must occur in nodes")
|
||||
|
||||
dt_ms = float(config["dt_ms"])
|
||||
if not np.isfinite(dt_ms) or dt_ms <= 0:
|
||||
raise ValueError("dt_ms must be finite and positive")
|
||||
total_steps = _steps(config["duration_ms"], dt_ms, "duration_ms", positive=True)
|
||||
model = config["model"]
|
||||
refractory_steps = _steps(model["refractory_ms"], dt_ms, "refractory_ms")
|
||||
delay_steps = _steps(model["synaptic_delay_ms"], dt_ms, "synaptic_delay_ms")
|
||||
margin_mv = float(config["seed_margin_mv"])
|
||||
if not np.isfinite(margin_mv) or margin_mv < 0:
|
||||
raise ValueError("seed_margin_mv must be finite and nonnegative")
|
||||
if "dt_ms" in model and float(model["dt_ms"]) != dt_ms:
|
||||
raise ValueError("top-level and model dt_ms disagree")
|
||||
arrivals = None
|
||||
if external_arrivals is not None:
|
||||
arrivals = np.asarray(external_arrivals, dtype=np.float64)
|
||||
if arrivals.shape != (total_steps, len(nodes)) or not np.isfinite(arrivals).all():
|
||||
raise ValueError("external_arrivals must be finite with shape (T, N)")
|
||||
|
||||
author_path = Path(author_path).resolve(strict=True)
|
||||
author_bytes = author_path.read_bytes()
|
||||
author_hash = hashlib.sha256(author_bytes).hexdigest()
|
||||
if author_hash != AUTHOR_SHA256:
|
||||
raise ValueError(f"Author source hash mismatch: {author_hash}")
|
||||
io_dir = Path(io_dir).resolve()
|
||||
io_dir.mkdir(parents=True, exist_ok=False)
|
||||
environment = _environment()
|
||||
_json(io_dir / "environment.json", environment)
|
||||
if environment["packages"]["brian2"]["actual"] != "2.5.1":
|
||||
raise RuntimeError("The author oracle requires exactly Brian2 2.5.1")
|
||||
|
||||
import brian2 as b2
|
||||
|
||||
saved_preferences = {
|
||||
"codegen.target": b2.prefs["codegen.target"],
|
||||
"core.default_float_dtype": b2.prefs["core.default_float_dtype"],
|
||||
"legacy.refractory_timing": b2.prefs["legacy.refractory_timing"],
|
||||
}
|
||||
saved_dt = b2.defaultclock.dt
|
||||
try:
|
||||
b2.start_scope()
|
||||
b2.prefs["codegen.target"] = "numpy"
|
||||
b2.prefs["core.default_float_dtype"] = dtype.type
|
||||
b2.prefs["legacy.refractory_timing"] = False
|
||||
b2.defaultclock.dt = dt_ms * b2.ms
|
||||
# Compile the checked bytes themselves: no source rewrite or __pycache__
|
||||
# is possible, and a subsequent file change cannot alter imported code.
|
||||
author = types.ModuleType("phase4ad_pinned_author_" + author_hash[:12])
|
||||
author.__file__ = str(author_path)
|
||||
exec(compile(author_bytes, str(author_path), "exec"), author.__dict__)
|
||||
parameters = dict(author.default_params)
|
||||
checks = {}
|
||||
for config_key, author_key, unit in (
|
||||
("membrane_tau_ms", "t_mbr", b2.ms),
|
||||
("synaptic_tau_ms", "tau", b2.ms),
|
||||
("refractory_ms", "t_rfc", b2.ms),
|
||||
("synaptic_delay_ms", "t_dly", b2.ms),
|
||||
("resting_potential_mv", "v_0", b2.mV),
|
||||
("reset_potential_mv", "v_rst", b2.mV),
|
||||
("threshold_mv", "v_th", b2.mV),
|
||||
("w_syn_mv_per_anatomical_synapse", "w_syn", b2.mV),
|
||||
):
|
||||
expected = float(model[config_key])
|
||||
actual = float(parameters[author_key] / unit)
|
||||
if not np.isfinite(expected) or not np.isclose(actual, expected, rtol=0, atol=1e-12):
|
||||
raise ValueError(f"config {config_key}={expected} differs from author {actual}")
|
||||
checks[config_key] = {"config": expected, "author": actual}
|
||||
|
||||
completeness = pd.DataFrame({"local_index": np.arange(len(nodes), dtype=np.int32)},
|
||||
index=pd.Index(nodes, name="global_neuron_idx"))
|
||||
connections = pd.DataFrame({
|
||||
"Presynaptic_Index": [lookup[int(x)] for x in sources],
|
||||
"Postsynaptic_Index": [lookup[int(x)] for x in targets],
|
||||
"Excitatory x Connectivity": multipliers,
|
||||
"edge_id": edge_ids,
|
||||
"source_neuron_idx": sources, "target_neuron_idx": targets,
|
||||
}).astype({"Presynaptic_Index": "int32", "Postsynaptic_Index": "int32"})
|
||||
comp_path = io_dir / "completeness.csv"
|
||||
con_path = io_dir / "connectivity.parquet"
|
||||
completeness.to_csv(comp_path)
|
||||
connections.to_parquet(con_path, index=False, engine="pyarrow")
|
||||
neu, syn, spikes = author.create_model(comp_path, con_path, parameters)
|
||||
if np.dtype(neu.variables["v"].dtype) != dtype or np.dtype(syn.variables["w"].dtype) != dtype:
|
||||
raise AssertionError("Brian did not allocate the requested floating-point dtype")
|
||||
np.testing.assert_array_equal(np.asarray(syn.i[:]), connections.Presynaptic_Index)
|
||||
np.testing.assert_array_equal(np.asarray(syn.j[:]), connections.Postsynaptic_Index)
|
||||
|
||||
objects = [neu, syn, spikes]
|
||||
seed_state = {"requested_global_node": None if seed_node is None else int(seed_node),
|
||||
"margin_mv": margin_mv, "injections": 0}
|
||||
if seed_node is not None:
|
||||
seed_local = lookup[int(seed_node)]
|
||||
|
||||
def inject_seed():
|
||||
if seed_state["injections"] == 0:
|
||||
if float(b2.defaultclock.t / b2.ms) != 0:
|
||||
raise AssertionError("Seed injection must occur at tick zero")
|
||||
neu.v[seed_local] = parameters["v_th"] + margin_mv * b2.mV
|
||||
applied = float(neu.v[seed_local] / b2.mV)
|
||||
if margin_mv > 0 and not applied > float(parameters["v_th"] / b2.mV):
|
||||
raise ValueError("The positive seed margin rounded away in the selected dtype")
|
||||
seed_state["applied_mv"] = applied
|
||||
seed_state["injections"] = 1
|
||||
|
||||
objects.append(b2.NetworkOperation(inject_seed, clock=neu.clock,
|
||||
when="before_thresholds", order=0,
|
||||
name="oracle_seed_injection"))
|
||||
|
||||
external_metadata = {"events": 0, "ordering": "after default_synapses.pre"}
|
||||
if arrivals is not None:
|
||||
event_ticks, event_targets = np.nonzero(arrivals)
|
||||
event_values = arrivals[event_ticks, event_targets]
|
||||
external_metadata["events"] = int(len(event_ticks))
|
||||
if len(event_ticks):
|
||||
event_ids = np.arange(len(event_ticks), dtype=np.int32)
|
||||
external_source = b2.SpikeGeneratorGroup(
|
||||
len(event_ticks), indices=event_ids,
|
||||
times=event_ticks.astype(np.float64) * dt_ms * b2.ms,
|
||||
sorted=True, clock=neu.clock, name="zz_external_sources")
|
||||
external_syn = b2.Synapses(
|
||||
external_source, neu, model="w : volt", on_pre="g_post += w",
|
||||
delay=0 * b2.ms, name="zz_external_arrivals")
|
||||
external_syn.connect(i=event_ids, j=event_targets)
|
||||
external_syn.w = event_values * b2.mV
|
||||
external_syn.pre.order = int(syn.pre.order) + 1
|
||||
if external_syn.pre.when != syn.pre.when:
|
||||
raise AssertionError("External and anatomical pathways must share a schedule slot")
|
||||
objects.extend([external_source, external_syn])
|
||||
external_metadata["pathway_order"] = int(external_syn.pre.order)
|
||||
external_metadata["actual_weights_mv"] = np.asarray(external_syn.w[:] / b2.mV, dtype=dtype).tolist()
|
||||
pd.DataFrame({"tick": event_ticks, "neuron_idx": nodes[event_targets],
|
||||
"requested_mv": event_values}).to_csv(io_dir / "external-arrivals.csv", index=False)
|
||||
|
||||
trace = b2.StateMonitor(neu, ["v", "g", "lastspike", "not_refractory"],
|
||||
record=True, when="end", name="oracle_end_state")
|
||||
objects.append(trace)
|
||||
network = b2.Network(*objects)
|
||||
_json(io_dir / "run-spec.json", {
|
||||
"author_sha256": author_hash, "author_commit": AUTHOR_COMMIT,
|
||||
"precision": precision, "config": config, "parameter_checks": checks,
|
||||
"dt_ms": dt_ms, "steps": total_steps, "refractory_steps": refractory_steps,
|
||||
"delay_steps": delay_steps, "nodes": nodes.tolist(), "edge_ids": edge_ids.tolist(),
|
||||
"seed": seed_state.copy(), "external_arrivals": external_metadata,
|
||||
"preferences": {"codegen.target": "numpy", "legacy.refractory_timing": False,
|
||||
"core.default_float_dtype": precision},
|
||||
"weight_rule": "raw_count multiplied by unchanged author w_syn; no sign inference",
|
||||
"internal_voltage_unit": "volt", "returned_voltage_unit": "mV",
|
||||
})
|
||||
_json(io_dir / "schedule.json", {"schedule": list(network.schedule),
|
||||
"summary": str(network.scheduling_summary())})
|
||||
network.run(float(config["duration_ms"]) * b2.ms, namespace={})
|
||||
_json(io_dir / "generated-code.json", _code_records(network))
|
||||
|
||||
ticks_float = np.asarray(spikes.t[:] / b2.ms, dtype=np.float64) / dt_ms
|
||||
ticks = np.rint(ticks_float).astype(np.int32)
|
||||
if not np.allclose(ticks_float, ticks, rtol=0, atol=1e-6):
|
||||
raise AssertionError("Spike timestamps do not lie on the configured integer clock")
|
||||
event_nodes = nodes[np.asarray(spikes.i[:], dtype=np.int32)]
|
||||
order = np.lexsort((event_nodes, ticks))
|
||||
ticks, event_nodes = ticks[order], event_nodes[order]
|
||||
counts = np.asarray(spikes.count[:], dtype=np.int32).copy()
|
||||
if int(counts.sum()) != len(ticks):
|
||||
raise AssertionError("SpikeMonitor counts disagree with event records")
|
||||
trace_ticks = np.asarray(trace.t[:] / b2.ms, dtype=np.float64) / dt_ms
|
||||
if trace_ticks.shape != (total_steps,) or not np.allclose(
|
||||
trace_ticks, np.arange(total_steps), rtol=0, atol=1e-6):
|
||||
raise AssertionError("End-state trace has the wrong tick range")
|
||||
voltage = np.asarray(trace.v / b2.mV, dtype=dtype).T.copy()
|
||||
conductance = np.asarray(trace.g / b2.mV, dtype=dtype).T.copy()
|
||||
last_spike_ms = np.asarray(trace.lastspike / b2.ms, dtype=np.float64).T
|
||||
seen = np.isfinite(last_spike_ms) & (last_spike_ms >= 0)
|
||||
# Brian's never-spiked sentinel can be far outside int32. Convert only
|
||||
# real nonnegative timestamps, then compute an end-of-tick countdown.
|
||||
last_ticks = np.zeros(last_spike_ms.shape, dtype=np.int64)
|
||||
last_ticks[seen] = np.rint(last_spike_ms[seen] / dt_ms).astype(np.int64)
|
||||
elapsed = np.arange(total_steps, dtype=np.int64)[:, None] - last_ticks
|
||||
ref = np.where(seen, np.maximum(refractory_steps - elapsed, 0), 0).astype(np.int32)
|
||||
available = np.asarray(trace.not_refractory, dtype=bool).T
|
||||
if not np.array_equal(available, ref == 0):
|
||||
raise AssertionError("Recorded refractory flag disagrees with the integer countdown")
|
||||
history = np.stack([voltage, conductance, ref.astype(dtype)], axis=1)
|
||||
if not np.isfinite(history).all():
|
||||
raise AssertionError("Nonfinite author state")
|
||||
if seed_node is not None:
|
||||
seed_state["spiked_at_zero"] = bool(np.any((ticks == 0) & (event_nodes == seed_node)))
|
||||
if seed_state["injections"] != 1 or (margin_mv > 0 and not seed_state["spiked_at_zero"]):
|
||||
raise AssertionError("The common initial seed event was not produced exactly once")
|
||||
_json(io_dir / "seed.json", seed_state)
|
||||
weights = np.asarray(syn.w[:] / b2.mV, dtype=dtype).copy()
|
||||
_json(io_dir / "actual-weights.json", {
|
||||
"edge_id": edge_ids.tolist(), "weights_mv": weights.tolist(),
|
||||
"weights_volt": np.asarray(syn.w_[:], dtype=np.float64).tolist(),
|
||||
})
|
||||
if hashlib.sha256(author_path.read_bytes()).hexdigest() != author_hash:
|
||||
raise AssertionError("Author source changed during execution")
|
||||
return {"step": ticks, "neuron_idx": event_nodes, "counts": counts,
|
||||
"v": voltage[-1].copy(), "g": conductance[-1].copy(),
|
||||
"ref": ref[-1].copy(), "history": history, "weights": weights}
|
||||
except Exception as error:
|
||||
_json(io_dir / "error.json", {"type": type(error).__name__, "message": str(error)})
|
||||
raise
|
||||
finally:
|
||||
for name, value in saved_preferences.items():
|
||||
b2.prefs[name] = value
|
||||
b2.defaultclock.dt = saved_dt
|
||||
@@ -0,0 +1,56 @@
|
||||
"""Predeclare and verify old inputs before executing any alternative dynamics."""
|
||||
import ast
|
||||
from datetime import datetime, timezone
|
||||
import importlib.metadata
|
||||
import json
|
||||
import platform
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from context import CFG,ROOT,EXP,MODEL,AC,digest,save,source_hashes
|
||||
|
||||
|
||||
def frozen_integrity():
|
||||
sys.path.insert(0,str(ROOT/CFG['prior_model']))
|
||||
import audit_semantics as old
|
||||
result=old.integrity()
|
||||
for p in (ROOT/CFG['prior_model'],AC,ROOT/'registry/feedback-context-review-20260930'):
|
||||
result[str(p.relative_to(ROOT))]=old.manifest_check(p,digest)
|
||||
assert all(x['pass'] for x in result.values())
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
assert (MODEL/'oracle_runner.py').is_file()
|
||||
assert not (EXP/'protocol').exists(),'Refusing reused experiment protocol'
|
||||
for d in ('protocol','external-resources','inputs','components','oracle','regression','audit','performance'):(EXP/d).mkdir()
|
||||
source=AC/'external-resources/model.py'
|
||||
assert digest(source)=='fc45837d7122c6ce2a7f3f2f23c515992e4b232aadb919efabb72337fac88e4e'
|
||||
tree=ast.parse(source.read_text())
|
||||
assert all(isinstance(x,(ast.Import,ast.ImportFrom,ast.Assign,ast.FunctionDef,ast.Expr)) for x in tree.body)
|
||||
inputs=[source,AC/'external-resources/LICENSE',AC/'external-resources/environment_full.yml',
|
||||
AC/'audit/selected-real-edges.csv',AC/'audit/selected-metadata.parquet',ROOT/CFG['prior_model']/'math_reference.py']
|
||||
for name in CFG['motifs']:
|
||||
inputs.extend([AC/'motifs'/f'{name}-edges.csv',AC/'motifs'/f'{name}-events.parquet',AC/'motifs'/f'{name}-state.npz'])
|
||||
save(EXP/'protocol/preregistered.json',dict(config=CFG,timestamp_utc=datetime.now(timezone.utc).isoformat(),
|
||||
source_hashes=source_hashes(),input_hashes=[dict(path=str(p.relative_to(ROOT)),sha256=digest(p),bytes=p.stat().st_size) for p in inputs],
|
||||
parent_commit=subprocess.check_output(['git','-C',str(ROOT),'rev-parse','HEAD'],text=True).strip(),
|
||||
pre_model_execution=True))
|
||||
save(EXP/'regression/old-integrity-before.json',frozen_integrity())
|
||||
for name in ('model.py','LICENSE','environment_full.yml'):
|
||||
dst=EXP/'external-resources'/name
|
||||
with (AC/'external-resources'/name).open('rb') as src,dst.open('xb') as out:shutil.copyfileobj(src,out)
|
||||
assert digest(dst)==digest(AC/'external-resources'/name)
|
||||
versions={n:importlib.metadata.version(n) for n in ('numpy','pandas','pyarrow','torch','scipy')}
|
||||
save(EXP/'environment/frozen-scientific-environment.json',dict(python=sys.version,executable=sys.executable,
|
||||
platform=platform.platform(),package_versions=versions))
|
||||
save(EXP/'environment/bootstrap-notes.json',dict(isolated_venv_only=True,existing_environments_modified=False,
|
||||
initial_install_failure='New venv pip22.0.2 lacks --report; upgraded ONLY new venv pip to author-pinned23.0.1 before installing',
|
||||
MarkupSafe='Author-pinned1.1.1 used PyPI source distribution and legacy setup.py because no CPython3.10 wheel; other requested core packages used wheels',
|
||||
author_environment='Windows conda CPython3.10.11',actual_environment='Linux CPython3.10.12; not binary-identical',
|
||||
package_download_provenance='install-report.json contains HTTPS PyPI archive URLs and SHA256 for packages'))
|
||||
print('PREPARED: old integrity PASS; independent model hypotheses preregistered',flush=True)
|
||||
|
||||
|
||||
if __name__=='__main__':main()
|
||||
@@ -0,0 +1,20 @@
|
||||
# Package versions in the pinned author's environment_full.yml, Linux wheels.
|
||||
# The host Python patch is recorded separately; this is not the author's Windows binary environment.
|
||||
brian2==2.5.1
|
||||
numpy==1.22.3
|
||||
pandas==1.4.3
|
||||
scipy==1.8.1
|
||||
pyarrow==11.0.0
|
||||
joblib==1.2.0
|
||||
cython==0.29.33
|
||||
jinja2==2.11.3
|
||||
markupsafe==1.1.1
|
||||
sympy==1.12
|
||||
mpmath==1.3.0
|
||||
pyparsing==3.0.9
|
||||
packaging==23.1
|
||||
setuptools==67.8.0
|
||||
python-dateutil==2.8.2
|
||||
pytz==2023.3
|
||||
six==1.16.0
|
||||
wheel==0.38.4
|
||||
@@ -0,0 +1,158 @@
|
||||
"""Run fixed component arms and independent author oracle, preserving divergences."""
|
||||
import importlib.metadata
|
||||
import json
|
||||
from pathlib import Path
|
||||
import platform
|
||||
import sys
|
||||
import subprocess
|
||||
import time
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from context import CFG,ROOT,EXP,MODEL,AC,digest,save,check_prereg
|
||||
from math_lab import component,author_numpy
|
||||
from oracle_runner import run_brian,PACKAGE_PINS
|
||||
|
||||
|
||||
def save_result(where,result,nodes):
|
||||
where.mkdir(parents=True,exist_ok=False)
|
||||
pd.DataFrame(dict(step=result['step'],neuron_idx=result['neuron_idx'])).to_csv(where/'events.csv',index=False)
|
||||
np.savez_compressed(where/'state.npz',nodes=nodes,**{k:v for k,v in result.items() if k not in ('step','neuron_idx')})
|
||||
|
||||
|
||||
def compare(actual,expected,precision):
|
||||
event=bool(np.array_equal(actual['step'],expected['step']) and np.array_equal(actual['neuron_idx'],expected['neuron_idx']))
|
||||
a=np.asarray(actual['history'],np.float64);b=np.asarray(expected['history'],np.float64)
|
||||
assert a.shape==b.shape
|
||||
errs=np.abs(a[:,:2]-b[:,:2])
|
||||
# Describe numerical prefix separately from post-event-divergence trajectories.
|
||||
first=None
|
||||
for t in range(len(a)):
|
||||
aa=actual['neuron_idx'][actual['step']==t];bb=expected['neuron_idx'][expected['step']==t]
|
||||
if not np.array_equal(aa,bb):first=t;break
|
||||
prefix=errs[:first] if first is not None else errs
|
||||
tol=CFG['comparison'][precision+'_state_max_abs_tolerance_mv']
|
||||
return dict(exact_events=event,exact_counts=bool(np.array_equal(actual['counts'],expected['counts'])),
|
||||
ref_exact=bool(np.array_equal(a[:,2],b[:,2])),max_abs_mv=float(errs.max()),mean_abs_mv=float(errs.mean()),
|
||||
same_event_prefix_max_abs_mv=float(prefix.max()) if prefix.size else None,
|
||||
first_event_divergence_step=first,tolerance_mv=tol,
|
||||
pass_oracle=bool(event and errs.max()<=tol and np.array_equal(a[:,2],b[:,2])))
|
||||
|
||||
|
||||
def oracle_trial(name,nodes,edges,cfg,precision,seed,external=None):
|
||||
io=EXP/'oracle'/(name+'-'+precision)
|
||||
result=run_brian(EXP/'external-resources/model.py',io,nodes,edges,cfg,precision,seed,external)
|
||||
independent=author_numpy(nodes,edges,cfg,precision,seed,external)
|
||||
comparison=compare(result,independent,precision)
|
||||
save(io/'comparison.json',comparison)
|
||||
save_result(io/'brian-output',result,nodes);save_result(io/'independent-output',independent,nodes)
|
||||
assert np.isfinite(result['history']).all() and np.isfinite(independent['history']).all()
|
||||
return result,comparison
|
||||
|
||||
|
||||
def unit_fixtures():
|
||||
rows=[];nodes=np.array([-1],np.int32)
|
||||
# Brian2 2.5.1 calls max/min on connect arrays and rejects zero-length arrays.
|
||||
# An explicitly synthetic zero-weight self-edge is inert, not an anatomical edge.
|
||||
inert=pd.DataFrame(dict(edge_id=[-1],source_neuron_idx=[-1],target_neuron_idx=[-1],raw_count=[0]),dtype='int64')
|
||||
for precision in CFG['author_precisions']:
|
||||
# Synthetic fixture indices are negative and are not MaleCNS body IDs.
|
||||
c=dict(CFG,duration_ms=20.0)
|
||||
cases={}
|
||||
a=np.zeros((200,1));cases['zero']=(a,None,1.0)
|
||||
a=np.zeros((200,1));a[0,0]=1.;cases['weak_excitation']=(a,None,1.0)
|
||||
a=np.zeros((200,1));a[0,0]=-1.;cases['inhibition']=(a,None,1.0)
|
||||
a=np.zeros((200,1));a[[0,21,22,23],0]=.1;cases['refractory_boundary']=(a,-1,1.0)
|
||||
a=np.zeros((200,1));cases['equal_threshold']=(a,-1,0.0)
|
||||
a=np.zeros((200,1));cases['above_threshold']=(a,-1,1.0)
|
||||
a=np.zeros((200,1));a[0,0]=2000.;cases['nonzero_g_reset']=(a,None,1.0)
|
||||
for name,(arr,seed,margin) in cases.items():
|
||||
cfg=dict(c,seed_margin_mv=margin)
|
||||
r,cmp=oracle_trial('unit-'+name,nodes,inert,cfg,precision,seed,arr)
|
||||
h=r['history'];events=r['step'];checks={}
|
||||
if name=='zero':checks={'no_events':len(events)==0,'g_zero':bool(np.all(h[:,1]==0)),
|
||||
'rest_stationary':bool(np.max(np.abs(h[:,0]-CFG['model']['resting_potential_mv']))<1e-3)}
|
||||
elif name in ('weak_excitation','inhibition'):
|
||||
checks={'no_events':len(events)==0,'arrival_at_zero':bool(h[0,1,0]>0 if name=='weak_excitation' else h[0,1,0]<0),
|
||||
'integration_next_step':bool(h[1,0,0]>h[0,0,0] if name=='weak_excitation' else h[1,0,0]<h[0,0,0])}
|
||||
elif name=='refractory_boundary':checks={'one_seed':np.array_equal(events,[0]),'reject_through_21':bool(np.all(h[:22,1]==0)),
|
||||
'accept_at_22':bool(h[22,1,0]>0),'release_at_22':bool(h[21,2,0]==1 and h[22,2,0]==0),'reset_g_at_zero':h[0,1,0]==0}
|
||||
elif name=='equal_threshold':checks={'strict_threshold_no_event':len(events)==0}
|
||||
elif name=='nonzero_g_reset':checks={'natural_spike_at_1':np.array_equal(events,[1]),
|
||||
'nonzero_g_before_spike':bool(h[0,1,0]>1999),'zero_g_after_spike':bool(np.all(h[1:,1,0]==0))}
|
||||
else:checks={'single_seed':np.array_equal(events,[0]),'reset_g':bool(h[0,1,0]==0)}
|
||||
row=dict(fixture=name,precision=precision,checks={k:bool(v) for k,v in checks.items()},comparison=cmp)
|
||||
rows.append(row);save(EXP/'regression'/('unit-'+name+'-'+precision+'.json'),row)
|
||||
print(json.dumps(dict(unit=name,precision=precision,checks=all(checks.values()),oracle=cmp)),flush=True)
|
||||
delay_nodes=np.array([-2,-1],np.int32)
|
||||
weak_edge=pd.DataFrame(dict(edge_id=[-1],source_neuron_idx=[-2],target_neuron_idx=[-1],raw_count=[1]),dtype='int64')
|
||||
r,cmp=oracle_trial('unit-delay_18',delay_nodes,weak_edge,c,precision,-2)
|
||||
h=r['history'];checks=dict(one_seed=np.array_equal(r['step'],[0]),
|
||||
zero_before_arrival=bool(np.all(h[:18,1,1]==0)),arrival_at_18=bool(h[18,1,1]>0),
|
||||
first_voltage_effect_at_19=bool(h[19,0,1]>h[18,0,1]))
|
||||
row=dict(fixture='delay_18',precision=precision,checks=checks,comparison=cmp)
|
||||
rows.append(row);save(EXP/'regression'/('unit-delay_18-'+precision+'.json'),row)
|
||||
save(EXP/'regression/unit-fixtures.json',rows)
|
||||
# Unit oracle failures prevent real author-motif execution; preserve all failed traces.
|
||||
assert all(all(r['checks'].values()) and r['comparison']['pass_oracle'] for r in rows),'Author operator unit gate failed'
|
||||
|
||||
|
||||
def main():
|
||||
check_prereg();assert not (EXP/'audit/run-start.json').exists(),'Refusing overwritten run'
|
||||
assert all(importlib.metadata.version(n)==v for n,v in PACKAGE_PINS.items()),'Dependency-pin mismatch'
|
||||
save(EXP/'environment/installed-packages.json',json.loads(subprocess.check_output([sys.executable,'-m','pip','list','--format=json'],text=True)))
|
||||
save(EXP/'audit/run-start.json',dict(python=sys.version,platform=platform.platform(),
|
||||
packages={n:importlib.metadata.version(n) for n in ('brian2','numpy','pandas','scipy','pyarrow','sympy','cython')},
|
||||
codegen='numpy',learning=False,user_authorized_new_model_hypotheses=True))
|
||||
t0=time.perf_counter();rows=[];validations=[]
|
||||
for name in CFG['motifs']:
|
||||
edges=pd.read_csv(AC/'motifs'/f'{name}-edges.csv').sort_values('edge_id')
|
||||
old=np.load(AC/'motifs'/f'{name}-state.npz');nodes=old['nodes'];seed=None if name=='no_input' else CFG['seed_node']
|
||||
weights=edges.raw_count.to_numpy(np.float32)*np.float32(CFG['model']['w_syn_mv_per_anatomical_synapse'])
|
||||
for policy in CFG['component_policies']:
|
||||
r=component(nodes,edges,weights,CFG,policy,seed)
|
||||
assert np.isfinite(r['history']).all()
|
||||
if policy=='frozen':
|
||||
ev=pd.read_parquet(AC/'motifs'/f'{name}-events.parquet')
|
||||
eq={k:bool(np.array_equal(old[k],r[k])) for k in old.files if k!='nodes'}
|
||||
eq['events']=bool(np.array_equal(ev.step,r['step']) and np.array_equal(ev.neuron_idx,r['neuron_idx']))
|
||||
assert all(eq.values()),eq
|
||||
validations.append(dict(motif=name,frozen_vs_4AC_exact=eq))
|
||||
save_result(EXP/'components'/(name+'-'+policy),r,nodes)
|
||||
late=r['step']>=round(CFG['late_start_ms']/CFG['dt_ms'])
|
||||
for node in nodes:
|
||||
select=r['neuron_idx']==node
|
||||
rows.append(dict(motif=name,policy=policy,precision='float32',neuron_idx=int(node),spikes=int(select.sum()),
|
||||
late_spikes=int((select&late).sum()),last_ms=float(r['step'][select][-1]*CFG['dt_ms']) if select.any() else None))
|
||||
pd.DataFrame(rows).to_csv(EXP/'components/summary.csv',index=False)
|
||||
save(EXP/'regression/frozen-components.json',validations)
|
||||
unit_fixtures()
|
||||
from interface_audit import audit_author_poi
|
||||
audit_author_poi()
|
||||
ors=[]
|
||||
for name in CFG['motifs']:
|
||||
edges=pd.read_csv(AC/'motifs'/f'{name}-edges.csv').sort_values('edge_id')
|
||||
nodes=np.load(AC/'motifs'/f'{name}-state.npz')['nodes'];seed=None if name=='no_input' else CFG['seed_node'];by_precision={}
|
||||
for precision in CFG['author_precisions']:
|
||||
r,cmp=oracle_trial(name,nodes,edges,CFG,precision,seed);by_precision[precision]=r
|
||||
repeat=run_brian(EXP/'external-resources/model.py',EXP/'oracle'/(name+'-'+precision+'-repeat'),nodes,edges,CFG,precision,seed)
|
||||
exact_repeat={k:bool(np.array_equal(r[k],repeat[k])) for k in r}
|
||||
save_result(EXP/'oracle'/(name+'-'+precision+'-repeat')/'brian-output',repeat,nodes)
|
||||
save(EXP/'regression'/(name+'-'+precision+'-repeat.json'),exact_repeat)
|
||||
assert all(exact_repeat.values()),exact_repeat
|
||||
late=r['step']>=round(CFG['late_start_ms']/CFG['dt_ms'])
|
||||
for node in nodes:
|
||||
chosen=r['neuron_idx']==node
|
||||
rows.append(dict(motif=name,policy='author_complete',precision=precision,neuron_idx=int(node),
|
||||
spikes=int(chosen.sum()),late_spikes=int((chosen&late).sum()),last_ms=float(r['step'][chosen][-1]*CFG['dt_ms']) if chosen.any() else None))
|
||||
ors.append(dict(motif=name,precision=precision,comparison=cmp))
|
||||
print(json.dumps(dict(motif=name,precision=precision,events=len(r['step']),late=int(late.sum()),oracle=cmp)),flush=True)
|
||||
save(EXP/'regression'/(name+'-precision-cross-comparison.json'),compare(by_precision['float32'],by_precision['float64'],'float32'))
|
||||
pd.DataFrame(rows).to_csv(EXP/'audit/all-motif-results.csv',index=False)
|
||||
save(EXP/'regression/author-motifs.json',ors)
|
||||
save(EXP/'performance/summary.json',dict(wall_seconds=time.perf_counter()-t0,backend='CPU NumPy + Brian2',not_runtime_benchmark=True))
|
||||
save(EXP/'audit/run-complete.json',dict(all_author_oracle_comparisons_pass=all(x['comparison']['pass_oracle'] for x in ors),
|
||||
all_frozen_components_match_archive=True,learning_achieved=False,whole_brain_alternative_run=False,
|
||||
author_original_source_sha256=digest(EXP/'external-resources/model.py')))
|
||||
|
||||
|
||||
if __name__=='__main__':main()
|
||||
@@ -0,0 +1,47 @@
|
||||
"""Unchanged deterministic-v2 five-trial regression; old outputs are read only."""
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import yaml
|
||||
from context import ROOT,EXP,CFG,save,check_prereg
|
||||
|
||||
|
||||
def main():
|
||||
check_prereg()
|
||||
assert not (EXP/'regression/frozen-five.json').exists()
|
||||
runtime=ROOT/'models/male-cns-lif/runtime-v2-deterministic-candidate'
|
||||
# Discover the exact prior-audit runtime path from its immutable config.
|
||||
prior=yaml.safe_load((ROOT/CFG['prior_model']/'config.yaml').read_text())
|
||||
runtime=ROOT/prior['runtime'];pc=yaml.safe_load((runtime/'phase4p.yaml').read_text())
|
||||
sys.path.insert(0,str(ROOT/'models/male-cns-lif/terminal-scope-diagnostic-v1'))
|
||||
from diagnostic import queues
|
||||
queues()
|
||||
os.environ['CUDA_VISIBLE_DEVICES']=pc['device']['visible_gpu_uuid']
|
||||
os.environ['CUDA_HOME']='/usr/local/cuda';os.environ['MAX_JOBS']='2';os.environ['TORCH_CUDA_ARCH_LIST']='8.9'
|
||||
os.environ['PATH']=str(ROOT/'models/male-cns-lif/.venv/bin')+':/usr/local/cuda/bin:'+os.environ['PATH']
|
||||
sys.path.insert(0,str(ROOT/'models/male-cns-lif'));sys.path.insert(0,str(runtime))
|
||||
import torch,phase4p
|
||||
free,total=torch.cuda.mem_get_info();assert free>2*1024**3
|
||||
torch.cuda.reset_peak_memory_stats();t0=time.perf_counter()
|
||||
rt=phase4p.CanonicalRuntime(pc,runtime,EXP)
|
||||
rows=[]
|
||||
for spec in pc['reference_trials']:
|
||||
queues();base=ROOT/'experiments/phase4p-deterministic-runtime/phase4c-v2/reference'/spec['name']
|
||||
ev=pd.read_parquet(base/'spike-events.parquet')
|
||||
expected=dict(step=ev.step.to_numpy(),neuron_idx=ev.neuron_idx.to_numpy(),
|
||||
counts=np.load(base/'per-neuron-spike-counts.int32.npy'),
|
||||
v=np.load(base/'final-voltage.float32.npy'),g=np.load(base/'final-g.float32.npy'))
|
||||
r=rt.run(np.flatnonzero(rt.pops[spec['population']]),spec['rate_hz'],spec['seed'],spec['duration_ms'])
|
||||
eq={k:bool(np.array_equal(r[k],v)) for k,v in expected.items()}
|
||||
row=dict(trial=spec['name'],exact=eq,wall_seconds=r['metrics']['wall_seconds'])
|
||||
rows.append(row);save(EXP/'regression'/('frozen-'+spec['name']+'.json'),row)
|
||||
print(row,flush=True)
|
||||
save(EXP/'regression/frozen-five.json',dict(pass_all=all(all(x['exact'].values()) for x in rows),trials=rows,
|
||||
oracle='frozen deterministic-v2, NOT author Brian2',wall_seconds=time.perf_counter()-t0,
|
||||
peak_allocated_vram=int(torch.cuda.max_memory_allocated()),gpu=torch.cuda.get_device_name(),comfyui_modified=False))
|
||||
assert all(all(x['exact'].values()) for x in rows)
|
||||
|
||||
|
||||
if __name__=='__main__':main()
|
||||
Reference in New Issue
Block a user