397 lines
25 KiB
Python
397 lines
25 KiB
Python
"""Complete original Phase 4L representation metrics on fixed CAL inputs only.
|
|
|
|
No simulation, fitting, heldout trajectory access, or candidate generation is
|
|
performed here. The original 4L pair-specific within-odor baseline is retained;
|
|
the weaker Phase 4P r4 aggregate gate is deliberately not used.
|
|
"""
|
|
import csv
|
|
import hashlib
|
|
import itertools
|
|
import json
|
|
from pathlib import Path
|
|
import re
|
|
|
|
import numpy as np
|
|
|
|
|
|
STATE_DTYPES = {"step": np.int32, "neuron_idx": np.int32, "counts": np.int32,
|
|
"v": np.float64, "g": np.float64, "ref": np.int32}
|
|
REQUEST_FIELDS = ("tick", "neuron_idx", "attempted", "accepted", "rejected")
|
|
APL_FILES = {"apl-drive.float32.npy": 2, "apl-state.float32.npy": 2,
|
|
"apl-inhibition.float32.npy": 4064}
|
|
ORIGINAL_CAL_IDS = {
|
|
"OMSUIQOIVADKIM-RXMQYKEDSA-N", "LABTWGUMFABVFG-ARJAWSKDSA-N",
|
|
"QNVRIHYSUZMSGM-ZCFIWIBFSA-N", "VLSVVMPLPMNWBH-UHFFFAOYSA-N",
|
|
"XHIUFYZDQBSEMF-UHFFFAOYSA-N", "OMSUIQOIVADKIM-YFKPBYRVSA-N"}
|
|
ORIGINAL_MODES = ("physiological", "matched_energy")
|
|
ORIGINAL_SEEDS = (51101, 51102, 51103, 51104, 51105)
|
|
HEX_SHA = re.compile(r"[0-9a-f]{64}\Z")
|
|
_DEFAULT_OUTPUT = object()
|
|
|
|
|
|
def require(condition, message):
|
|
if not condition:
|
|
raise ValueError(message)
|
|
|
|
|
|
def read_json(path):
|
|
with Path(path).open(encoding="utf-8") as stream:
|
|
return json.load(stream)
|
|
|
|
|
|
def file_hash(path):
|
|
digest = hashlib.sha256()
|
|
with Path(path).open("rb") as stream:
|
|
for block in iter(lambda: stream.read(8 << 20), b""):
|
|
digest.update(block)
|
|
return digest.hexdigest()
|
|
|
|
|
|
def raw_hash(value):
|
|
return hashlib.sha256(np.ascontiguousarray(value).tobytes()).hexdigest()
|
|
|
|
|
|
def arrays_bitwise_equal(first, second):
|
|
"""Unlike array_equal, also reject different dtypes and signed-zero bits."""
|
|
return bool(first.dtype == second.dtype and first.shape == second.shape and
|
|
np.array_equal(np.ascontiguousarray(first).view(np.uint8),
|
|
np.ascontiguousarray(second).view(np.uint8)))
|
|
|
|
|
|
def pair_metrics(a, b):
|
|
"""Exact definitions copied from phase4i.pair_metrics/safe_corr/cosine."""
|
|
x, y = a.astype(np.float64), b.astype(np.float64)
|
|
aa, bb = a > 0, b > 0
|
|
union = int(np.count_nonzero(aa | bb))
|
|
intersection = int(np.count_nonzero(aa & bb))
|
|
denominator = float(np.linalg.norm(x) * np.linalg.norm(y))
|
|
cosine = 1.0 if denominator == 0 and np.array_equal(x, y) else (
|
|
0.0 if denominator == 0 else float(np.dot(x, y) / denominator))
|
|
correlation = (1.0 if np.array_equal(x, y) else 0.0) if np.std(x) == 0 or np.std(y) == 0 else float(np.corrcoef(x, y)[0, 1])
|
|
return {"active_intersection": intersection, "active_union": union,
|
|
"active_jaccard": 1.0 if union == 0 else float(intersection / union),
|
|
"cosine_similarity": cosine, "correlation": correlation,
|
|
"euclidean_distance": float(np.linalg.norm(x - y))}
|
|
|
|
|
|
def evaluate_gates(summary, gates):
|
|
"""Each conjunct in original phase4l.py:239, kept visible for audit."""
|
|
return {
|
|
"mean_sparsity_in_closed_interval": bool(float(gates["sparse_min_fraction"]) <=
|
|
summary["KC_active_fraction_mean"] <= float(gates["sparse_max_fraction"])),
|
|
"maximum_strictly_below_half": bool(summary["KC_active_fraction_max"] < 0.5),
|
|
"minimum_strictly_positive": bool(summary["KC_active_fraction_min"] > 0),
|
|
"same_jaccard_gt_different": bool(summary["same_odor_jaccard_mean"] > summary["different_odor_jaccard_mean"]),
|
|
"different_euclidean_gt_same": bool(summary["different_odor_euclidean_mean"] > summary["same_odor_euclidean_mean"]),
|
|
"at_least_original_pair_count": bool(summary["pairwise_separation_pass_count"] >= int(gates["minimum_separable_odor_pairs"]))}
|
|
|
|
|
|
def sample_cv(values):
|
|
"""Original pandas Series.std uses ddof=1. Undefined diagnostic -> null."""
|
|
values = np.asarray(values, dtype=np.float64)
|
|
mean = float(values.mean())
|
|
return None if mean == 0 else float(values.std(ddof=1) / mean)
|
|
|
|
|
|
def summarize_mode(mode, odor_ids, seeds, runs, gates):
|
|
within, within_j, between, between_j, between_cos, between_corr = [], [], [], [], [], []
|
|
pairs = []
|
|
for odor in odor_ids:
|
|
arrays = [runs[(mode, odor, seed)]["kc_counts"] for seed in seeds]
|
|
for first, second in itertools.combinations(arrays, 2):
|
|
metric = pair_metrics(first, second)
|
|
within.append(metric["euclidean_distance"])
|
|
within_j.append(metric["active_jaccard"])
|
|
for first_odor, second_odor in itertools.combinations(odor_ids, 2):
|
|
distances = []
|
|
for seed in seeds:
|
|
metric = pair_metrics(runs[(mode, first_odor, seed)]["kc_counts"],
|
|
runs[(mode, second_odor, seed)]["kc_counts"])
|
|
distances.append(metric["euclidean_distance"])
|
|
between.append(metric["euclidean_distance"])
|
|
between_j.append(metric["active_jaccard"])
|
|
between_cos.append(metric["cosine_similarity"])
|
|
between_corr.append(metric["correlation"])
|
|
pair_within = []
|
|
for odor in (first_odor, second_odor):
|
|
arrays = [runs[(mode, odor, seed)]["kc_counts"] for seed in seeds]
|
|
pair_within.extend(pair_metrics(first, second)["euclidean_distance"]
|
|
for first, second in itertools.combinations(arrays, 2))
|
|
cross_mean, within_mean = float(np.mean(distances)), float(np.mean(pair_within))
|
|
pairs.append({"odor_id_a": first_odor, "odor_id_b": second_odor,
|
|
"between_same_seed_mean": cross_mean, "within_this_pair_mean": within_mean,
|
|
"between_distance_count": len(distances), "within_distance_count": len(pair_within),
|
|
"gate": bool(cross_mean > within_mean)})
|
|
selected = [runs[(mode, odor, seed)] for odor in odor_ids for seed in seeds]
|
|
fractions = [int(np.count_nonzero(run["kc_counts"])) / len(run["kc_counts"]) for run in selected]
|
|
summary = {"KC_active_fraction_mean": float(np.mean(fractions)),
|
|
"KC_active_fraction_min": float(np.min(fractions)), "KC_active_fraction_max": float(np.max(fractions)),
|
|
"same_odor_jaccard_mean": float(np.mean(within_j)),
|
|
"different_odor_jaccard_mean": float(np.mean(between_j)),
|
|
"same_odor_euclidean_mean": float(np.mean(within)),
|
|
"different_odor_euclidean_mean": float(np.mean(between)),
|
|
"different_odor_cosine_mean": float(np.mean(between_cos)),
|
|
"different_odor_correlation_mean": float(np.mean(between_corr)),
|
|
"within_variance": float(np.var(within)), "between_distance_mean": float(np.mean(between)),
|
|
"pairwise_separation_pass_count": sum(row["gate"] for row in pairs), "pair_count": len(pairs),
|
|
"expected_event_count_cv": sample_cv([run["expected_external_events"] for run in selected]),
|
|
"actual_event_count_cv": sample_cv([run["external_events"] for run in selected]),
|
|
"within_distance_count": len(within), "between_distance_count": len(between),
|
|
"pairwise_baseline": "Each pair's two odors: 10+10 within-seed distances; never pooled across all odors",
|
|
"pairs": pairs}
|
|
summary["conditions"] = evaluate_gates(summary, gates)
|
|
summary["gate"] = bool(all(summary["conditions"].values()))
|
|
return summary
|
|
|
|
|
|
def validate_state(state, neurons, steps):
|
|
require(set(state) == set(STATE_DTYPES), "state.npz must have exactly the six declared arrays")
|
|
for name, dtype in STATE_DTYPES.items():
|
|
value = state[name]
|
|
require(value.dtype == dtype and value.ndim == 1, "Invalid state dtype/rank: " + name)
|
|
require(bool(np.isfinite(value).all()), "Nonfinite state: " + name)
|
|
if name not in ("step", "neuron_idx"):
|
|
require(value.shape == (neurons,), "Invalid state length: " + name)
|
|
ticks, nodes = state["step"], state["neuron_idx"]
|
|
require(ticks.shape == nodes.shape and bool(np.all((ticks >= 0) & (ticks < steps))) and
|
|
bool(np.all((nodes >= 0) & (nodes < neurons))), "Invalid sparse event indices")
|
|
keys = ticks.astype(np.int64) * neurons + nodes
|
|
require(bool(np.all(np.diff(keys) > 0)), "Sparse events not sorted unique by tick/global neuron")
|
|
require(np.array_equal(np.bincount(nodes, minlength=neurons), state["counts"]), "Counts disagree with sparse events")
|
|
require(bool(np.all((state["ref"] >= 0) & (state["ref"] <= 22))), "Final refractory outside frozen range")
|
|
return keys
|
|
|
|
|
|
def read_jsonl(path, steps, kind):
|
|
with Path(path).open(encoding="utf-8") as source:
|
|
rows = [json.loads(line) for line in source if line.strip()]
|
|
require(len(rows) == steps and [row.get("tick") for row in rows] == list(range(steps)),
|
|
"Missing/duplicate/reordered ticks in " + kind)
|
|
for row in rows:
|
|
names = ("v_sha256", "g_sha256", "ref_sha256") if kind == "state hashes" else (
|
|
"ordered_edge_sha256", "accepted_mask_sha256", "joint_sha256")
|
|
for name in names:
|
|
require(isinstance(row.get(name), str) and HEX_SHA.fullmatch(row[name]), "Invalid SHA256 in " + kind)
|
|
if kind == "queue":
|
|
require(type(row.get("count")) is int and type(row.get("accepted_count")) is int and
|
|
0 <= row["accepted_count"] <= row["count"], "Invalid queue counts")
|
|
return rows
|
|
|
|
|
|
def read_trial(output, case, spec, kc_globals, neurons, edges):
|
|
summary = read_json(output / "summary.json")
|
|
steps = spec["steps"]
|
|
require(summary.get("complete") is True and summary.get("pass_all") is True and
|
|
summary.get("all_finite") is True and summary.get("steps") == steps and
|
|
summary.get("completed_ticks") == steps and summary.get("nodes") == neurons and
|
|
summary.get("edges") == edges and summary.get("precision") == "float64" and
|
|
summary.get("apl_precision") == "float32" and summary.get("case") == spec["name"] and
|
|
summary.get("record_history") is False and summary.get("learning") is False,
|
|
"Incomplete/failed/wrong-scope GPU summary")
|
|
with np.load(output / "state.npz", allow_pickle=False) as source:
|
|
state = {name: source[name].copy() for name in source.files}
|
|
event_keys = validate_state(state, neurons, steps)
|
|
hashes = read_jsonl(output / "tick-state-hashes.jsonl", steps, "state hashes")
|
|
for name in ("v", "g", "ref"):
|
|
require(hashes[-1][name + "_sha256"] == raw_hash(state[name]), "Final state does not match last tick hash: " + name)
|
|
queues = read_jsonl(output / "queue.jsonl", steps, "queue")
|
|
apl = {}
|
|
for name, width in APL_FILES.items():
|
|
value = np.load(output / name, mmap_mode="r", allow_pickle=False)
|
|
require(value.dtype == np.float32 and value.shape == (steps, width) and
|
|
bool(np.isfinite(value).all()) and bool(np.all(value >= 0)), "Invalid full APL trace: " + name)
|
|
apl[name] = value
|
|
with (output / "requests.csv").open(encoding="utf-8", newline="") as source:
|
|
reader = csv.DictReader(source)
|
|
require(tuple(reader.fieldnames or ()) == REQUEST_FIELDS, "Request CSV schema mismatch")
|
|
request_rows = [{name: int(row[name]) for name in REQUEST_FIELDS} for row in reader]
|
|
with np.load(case / "requests.npz", allow_pickle=False) as source:
|
|
require(set(source.files) == {"tick", "neuron_idx"}, "Input request schema mismatch")
|
|
expected_ticks, expected_nodes = source["tick"], source["neuron_idx"]
|
|
require(expected_ticks.dtype == np.int32 and expected_nodes.dtype == np.int32 and
|
|
expected_ticks.ndim == 1 and expected_ticks.shape == expected_nodes.shape,
|
|
"Input request dtype/shape mismatch")
|
|
require(len(request_rows) == len(expected_ticks), "Requested event coverage mismatch")
|
|
require([row["tick"] for row in request_rows] == expected_ticks.tolist() and
|
|
[row["neuron_idx"] for row in request_rows] == expected_nodes.tolist(), "Requests differ from frozen input")
|
|
require(all(row["attempted"] == 1 and row["accepted"] in (0, 1) and
|
|
row["rejected"] == 1 - row["accepted"] for row in request_rows), "Invalid request acceptance bookkeeping")
|
|
requested_keys = expected_ticks.astype(np.int64) * neurons + expected_nodes
|
|
require(bool(np.all(np.diff(requested_keys) > 0)) and bool(np.all((expected_ticks >= 0) & (expected_ticks < steps))) and
|
|
bool(np.all((expected_nodes >= 0) & (expected_nodes < neurons))), "Input requests not sorted unique/in range")
|
|
actual_accepted = np.array([row["accepted"] for row in request_rows], dtype=np.uint8)
|
|
require(np.array_equal(np.isin(requested_keys, event_keys, assume_unique=True), actual_accepted.astype(bool)),
|
|
"Request accepted/rejected values disagree with actual spikes")
|
|
accepted = int(actual_accepted.sum())
|
|
require(summary.get("requested") == len(request_rows) and summary.get("accepted_requests") == accepted and
|
|
summary.get("rejected_requests") == len(request_rows) - accepted and
|
|
summary.get("total_spikes") == len(state["step"]), "Summary counts disagree with raw output")
|
|
component = read_json(output / "apl-component.json")
|
|
require(component.get("ok") is True and component.get("n_neurons") == neurons and
|
|
component.get("n_kc") == len(kc_globals) and component.get("n_apl") == 2 and
|
|
component.get("dt_ms") == .1 and summary.get("apl_component") == component,
|
|
"APL component provenance mismatch")
|
|
bundle = Path(spec["apl_bundle_dir"])
|
|
require(component.get("files_sha256") == {name: file_hash(bundle / name) for name in
|
|
("input_matrix.npy", "output_matrix.npy", "kc_globals.npy", "apl_globals.npy")}, "APL bundle hashes changed")
|
|
inputs = summary.get("input_sha256", {})
|
|
require(inputs.get("spec.json") == file_hash(case / "spec.json") and
|
|
inputs.get("requests.npz") == file_hash(case / "requests.npz"), "Case input provenance mismatch")
|
|
kc_counts = state["counts"][kc_globals].copy()
|
|
kc_event_mask = np.isin(state["neuron_idx"], kc_globals)
|
|
record = {"case": spec["name"], "mode": spec["mode"], "odor_id": spec["odor_id"],
|
|
"odor_name": spec["odor_name"], "seed": spec["seed"], "split": "calibration",
|
|
"KC_active": int(np.count_nonzero(kc_counts)), "KC_active_fraction": float(np.count_nonzero(kc_counts) / len(kc_globals)),
|
|
"KC_spikes": int(kc_counts.sum()), "KC_first_latency_ms":
|
|
float(state["step"][kc_event_mask][0] * .1) if bool(np.any(kc_event_mask)) else None,
|
|
"external_events": accepted, "expected_external_events": float(spec["expected_external_events"]),
|
|
"total_PN_rate_hz": float(spec["rate_sum_hz"]), "finite": True,
|
|
"total_spikes": len(state["step"]), "requested": len(request_rows)}
|
|
require(np.isfinite(record["expected_external_events"]) and record["expected_external_events"] > 0,
|
|
"CAL expected external events must be finite and positive")
|
|
return {"state": state, "hashes": hashes, "queues": queues, "apl": apl,
|
|
"requests": request_rows, "component": component, "kc_counts": kc_counts,
|
|
"expected_external_events": record["expected_external_events"], "external_events": accepted,
|
|
"record": record, "output": output}
|
|
|
|
|
|
def exact_repeat(first, repeat):
|
|
checks = {"state/" + name: arrays_bitwise_equal(first["state"][name], repeat["state"][name]) for name in STATE_DTYPES}
|
|
checks.update({name: arrays_bitwise_equal(first["apl"][name], repeat["apl"][name]) for name in APL_FILES})
|
|
checks.update(tick_state_hashes=first["hashes"] == repeat["hashes"],
|
|
queues=first["queues"] == repeat["queues"], requests=first["requests"] == repeat["requests"],
|
|
apl_component=first["component"] == repeat["component"])
|
|
return {"pass_all": bool(all(checks.values())), "checks": checks,
|
|
"scope": "All six state arrays, every tick all-neuron hashes, APL raw float32 traces, queues, requests and APL environment"}
|
|
|
|
|
|
def validate_inventory(records, cfg):
|
|
require(tuple(cfg["calibration"]["modes"]) == ORIGINAL_MODES and
|
|
tuple(cfg["calibration"]["seeds"]) == ORIGINAL_SEEDS and
|
|
cfg["calibration"]["duration_ms"] == 100.0 and cfg["calibration"]["odor_count"] == 6 and
|
|
cfg["calibration"]["split"] == "calibration" and
|
|
cfg["calibration"]["repeat_first_seed_each_odor"] is True, "Frozen CAL design changed")
|
|
gates = cfg["gates"]
|
|
require(gates["sparse_min_fraction"] == .03 and gates["sparse_max_fraction"] == .09 and
|
|
gates["minimum_separable_odor_pairs"] == 3 and gates["max_fraction_strict"] == .5 and
|
|
gates["min_fraction_strict"] == 0 and all(gates[key] is True for key in
|
|
("same_jaccard_gt_between", "between_euclidean_gt_within", "both_modes_required", "finite_required", "repeat_exact_required")),
|
|
"Original representation gates or additional integrity gates changed")
|
|
expected = {(mode, index, seed) for mode in ORIGINAL_MODES for index in range(6) for seed in ORIGINAL_SEEDS}
|
|
require(len(records) == 60 and {(row["mode"], row["odor_index"], row["seed"]) for row in records} == expected,
|
|
"CAL inventory must cover exactly 2 modes x 6 odors x 5 seeds")
|
|
odors = []
|
|
for index in range(6):
|
|
ids = {row["odor_id"] for row in records if row["odor_index"] == index}
|
|
require(len(ids) == 1, "Odor identity changes across seeds/modes")
|
|
odors.append(next(iter(ids)))
|
|
require(set(odors) == ORIGINAL_CAL_IDS, "CAL identities differ from the original frozen six")
|
|
for row in records:
|
|
require(row["name"] == f"cal-{row['mode']}-{row['odor_index']:02d}-s{row['seed']}", "Unexpected CAL case name")
|
|
return odors
|
|
|
|
|
|
def summarize(out_json=_DEFAULT_OUTPUT):
|
|
"""Recompute; default writes exclusively, out_json=None is strictly read-only."""
|
|
from context import EXP, ROOT, CFG, check
|
|
target = EXP / "calibration/summary.json" if out_json is _DEFAULT_OUTPUT else (
|
|
None if out_json is None else Path(out_json))
|
|
require(target is None or not target.exists(), "Refusing existing calibration summary")
|
|
failures, runs, run_records, repeats, provenance = [], {}, [], [], []
|
|
modes, odors = {}, []
|
|
prerequisites_ok = False
|
|
encoder = None
|
|
records = []
|
|
try:
|
|
check()
|
|
records = read_json(EXP / "protocol/calibration-cases.json")
|
|
odors = validate_inventory(records, CFG)
|
|
encoder = read_json(EXP / "protocol/encoder-audit.json")
|
|
require(encoder["panel_count"] == 12 and encoder["cal_count"] == 6 and
|
|
encoder["seeds"] == list(ORIGINAL_SEEDS) and encoder["requested_before_any_dynamics"] is True and
|
|
encoder["formal_AB_run"] is False and encoder["heldout_run"] is False, "Invalid frozen encoder scope")
|
|
# Only static original-panel metadata is read, never heldout trajectories.
|
|
import pandas as pd
|
|
original = pd.read_parquet(ROOT / "experiments/phase4l-olfactory-grounding/odor-encoder/frozen-odor-panel.parquet")
|
|
require(len(original) == 12 and set(original[original.split == "calibration"].odor_id) == ORIGINAL_CAL_IDS,
|
|
"Original static panel identity mismatch")
|
|
require(float(np.median(original.mapped_response_sum)) == encoder["median_mapped_response_sum"],
|
|
"Matched-energy reference is not the original frozen 12-panel median")
|
|
kc_globals = np.load(EXP / "apl-bundle/kc_globals.npy", allow_pickle=False)
|
|
require(kc_globals.dtype == np.int32 and kc_globals.shape == (4064,) and
|
|
bool(np.all(np.diff(kc_globals) > 0)) and bool(np.all((kc_globals >= 0) & (kc_globals < CFG["neurons"]))),
|
|
"Invalid frozen KC identities")
|
|
prerequisites_ok = True
|
|
except Exception as error:
|
|
failures.append({"stage": "prerequisites", "type": type(error).__name__, "message": str(error)})
|
|
if prerequisites_ok:
|
|
for row in records:
|
|
name = row["name"]
|
|
first = None
|
|
try:
|
|
case = EXP / "cases" / name
|
|
spec = read_json(case / "spec.json")
|
|
require(spec["name"] == name and spec["kind"] == "calibration" and spec["steps"] == 1000 and
|
|
spec["duration_ms"] == 100.0 and spec["dt_ms"] == .1 and spec["training"] is False and
|
|
all(spec[key] == row[key] for key in ("mode", "odor_id", "odor_index", "seed")),
|
|
"Case spec differs from fixed CAL inventory")
|
|
first = read_trial(EXP / "gpu" / (name + "-r0"), case, spec, kc_globals, CFG["neurons"], CFG["edges"])
|
|
require(first["record"]["requested"] == row["requests"], "Frozen inventory request count mismatch")
|
|
runs[(row["mode"], row["odor_id"], row["seed"])] = {key: first[key] for key in
|
|
("kc_counts", "expected_external_events", "external_events")}
|
|
run_records.append(first["record"])
|
|
provenance.append({"case": name, "repeat": 0, "state_npz_sha256": file_hash(first["output"] / "state.npz"),
|
|
"summary_sha256": file_hash(first["output"] / "summary.json")})
|
|
except Exception as error:
|
|
failures.append({"stage": "raw_trial", "case": name, "repeat": 0,
|
|
"type": type(error).__name__, "message": str(error)})
|
|
if row["seed"] == ORIGINAL_SEEDS[0]:
|
|
try:
|
|
require(first is not None, "Baseline unavailable for exact repeat")
|
|
repeat = read_trial(EXP / "gpu" / (name + "-r1"), case, spec, kc_globals, CFG["neurons"], CFG["edges"])
|
|
repeat_report = {"case": name, **exact_repeat(first, repeat)}
|
|
repeats.append(repeat_report)
|
|
provenance.append({"case": name, "repeat": 1, "state_npz_sha256": file_hash(repeat["output"] / "state.npz"),
|
|
"summary_sha256": file_hash(repeat["output"] / "summary.json")})
|
|
require(repeat_report["pass_all"], "Exact repeat differs: " + ", ".join(
|
|
key for key, value in repeat_report["checks"].items() if not value))
|
|
except Exception as error:
|
|
failures.append({"stage": "exact_repeat", "case": name, "repeat": 1,
|
|
"type": type(error).__name__, "message": str(error)})
|
|
if len(runs) == 60:
|
|
for mode in ORIGINAL_MODES:
|
|
try:
|
|
modes[mode] = summarize_mode(mode, odors, ORIGINAL_SEEDS, runs, CFG["gates"])
|
|
except Exception as error:
|
|
failures.append({"stage": "representation", "mode": mode,
|
|
"type": type(error).__name__, "message": str(error)})
|
|
integrity_pass = bool(prerequisites_ok and len(runs) == 60 and not any(row["stage"] in
|
|
("prerequisites", "raw_trial") for row in failures))
|
|
repeat_pass = bool(len(repeats) == 12 and all(row["pass_all"] for row in repeats) and
|
|
not any(row["stage"] == "exact_repeat" for row in failures))
|
|
representation_pass = bool(len(modes) == 2 and all(row["gate"] for row in modes.values()))
|
|
result = {"phase": "4AG", "scope": "Original fixed six CAL odors only; not heldout generalization or learning",
|
|
"pass_all": bool(integrity_pass and repeat_pass and representation_pass),
|
|
"numerical_integrity_pass": integrity_pass, "exact_repeat_pass": repeat_pass,
|
|
"original_representation_gate_pass": representation_pass,
|
|
"expected_base_trials": 60, "valid_base_trials": len(runs), "expected_repeats": 12,
|
|
"checked_repeats": len(repeats), "seeds": list(ORIGINAL_SEEDS), "duration_ms": 100.0,
|
|
"odor_ids": odors, "modes": modes, "runs": run_records, "repeats": repeats,
|
|
"failures": failures, "input_output_provenance": provenance, "encoder_audit": encoder,
|
|
"metric_source": "phase4l.representation (full original gate), phase4i.pair_metrics",
|
|
"finite_gate_note": "Added integrity prerequisite; original 4L recorded finite but omitted it from its gate",
|
|
"repeat_gate_note": "Additional exact repeat requirement; original metrics and numerical thresholds unchanged",
|
|
"shared_apl_component_oracle_limit": True, "independent_oracle_equivalence_claim": False,
|
|
"candidate_generated": False, "training": False, "heldout_dynamic_access": False}
|
|
if target is not None:
|
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
with target.open("x", encoding="utf-8") as output:
|
|
json.dump(result, output, ensure_ascii=False, indent=2, allow_nan=False)
|
|
output.write("\n")
|
|
return result
|
|
|
|
|
|
if __name__ == "__main__":
|
|
print(json.dumps({key: value for key, value in summarize().items() if key in
|
|
("pass_all", "numerical_integrity_pass", "exact_repeat_pass", "original_representation_gate_pass")}), flush=True)
|