Seal AO interface failure and verified isolated postprocessing diagnosis

This commit is contained in:
Digital Drosophila Archive
2026-09-30 17:57:02 +08:00
parent 4ff241bdc9
commit 8e9f8f4d56
126 changed files with 92124 additions and 3 deletions
@@ -0,0 +1,171 @@
import copy
from collections import Counter
import unittest
from unittest.mock import patch
import seed_registry
import trial_plan as m
class TrialPlan(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.original = m.build_trial_plan(461)
def test_complete_reconstruction_and_budget(self):
self.assertTrue(m.validate_trial_plan(self.original))
budget = self.original["budget"]
self.assertEqual(budget["training_control_reversal_epochs"], 68000)
self.assertEqual(budget["training_control_reversal_steps"], 99600000)
self.assertEqual(budget["minimum_biological_seconds_excluding_evaluation"], 10760)
self.assertEqual(budget["minimum_steps_excluding_evaluation"], 107600000)
self.assertFalse(budget["evaluation_probe_inventory_complete"])
self.assertIsNone(budget["evaluation_epochs"])
self.assertFalse(self.original["plan"]["training_executed"])
def test_exact_cells_five_training_replicates_not_evaluation_streams(self):
cells = {}
for block in self.original["plan"]["blocks"]:
key = block["mode"], block["dan_type"], block["initial_reinforced_cue"]
cells.setdefault(key, []).append(block["replicate"])
self.assertEqual(len(block["evaluation_streams"]), 2)
self.assertEqual(len(cells), 8)
self.assertTrue(all(replicates == [1, 2, 3, 4, 5] for replicates in cells.values()))
def test_all_epoch_control_roles_and_matched_delayed_slots(self):
for block in self.original["plan"]["blocks"]:
for suite in block["suites"]:
assay = suite["assay"]
arms = {a["arm"]: a for a in suite["arms"]}
self.assertEqual(tuple(arms), m.REVERSAL_ARMS if assay == "reversal" else m.ARMS)
expected_steps, expected_us = (1800, 1500) if assay == "delayed" else (1200, 900)
for arm, spec in arms.items():
epochs = spec["epochs"]
self.assertEqual(len(epochs), 150 if arm == "unpaired" else 100)
cues = Counter(e["cs"]["cue"] for e in epochs if e["cs"] is not None)
self.assertEqual(cues, Counter() if arm == "dan_only" else Counter(A=50, B=50))
us = [e for e in epochs if e["us"]["requested"]]
self.assertEqual(len(us), 0 if arm == "cs_only" else 50)
self.assertEqual(len({e["us"]["inventory_id"] for e in us}), len(us))
for i, e in enumerate(epochs):
self.assertEqual(e["epoch_index"], i)
self.assertEqual(e["steps"], expected_steps)
self.assertEqual(e["us"]["nominal_slot_tick"], expected_us)
self.assertEqual(e["us"]["tick"], expected_us if e["us"]["requested"] else None)
self.assertIsNone(e["us"]["rng_seed"])
self.assertEqual(e["us"]["rng_draws"], 0)
self.assertEqual(e["update_enabled"], arm != "frozen")
self.assertEqual(e["D_command"], 1.0 if e["us"]["requested"] and arm != "d_zero" else 0.0)
if arm == "random_reward":
self.assertEqual(Counter(e["cs"]["cue"] for e in us), Counter(A=25, B=25))
if arm == "unpaired":
self.assertTrue(all(e["cs"] is None for e in us))
if arm == "dan_only":
self.assertEqual(sum(not e["us"]["requested"] for e in epochs), 50)
def test_alias_binding_same_occurrence_even_unpaired_insertion(self):
for block in self.original["plan"]["blocks"]:
for suite in block["suites"]:
base = suite["arms"][0]
lookup = {(e["cs"]["cue"], e["cs"]["occurrence"]): e["cs"]
for e in base["epochs"]}
base_order = [(e["cs"]["cue"], e["cs"]["occurrence"]) for e in base["epochs"]]
for arm in suite["arms"][1:]:
actual_order = []
for e in arm["epochs"]:
if e["cs"] is None:
continue
cs = e["cs"]
source = lookup[(cs["cue"], cs["occurrence"])]
self.assertEqual(cs["seed"], source["seed"])
self.assertEqual(cs["source_stream_id"], source["stream_id"])
self.assertNotEqual(cs["stream_id"], source["stream_id"])
actual_order.append((cs["cue"], cs["occurrence"]))
if arm["arm"] != "dan_only":
self.assertEqual(actual_order, base_order)
def test_reversal_not_extra_acquisition_or_new_compartment(self):
for b in self.original["plan"]["blocks"]:
reversal, continuation = b["suites"][2]["arms"]
original = b["initial_reinforced_cue"]
other = b["initial_neutral_cue"]
for spec, cue in ((reversal, other), (continuation, original)):
self.assertEqual(spec["delta_start"], {"kind": "fork", "source": b["block_id"] + "/normal/paired/terminal-delta"})
self.assertEqual({e["cs"]["cue"] for e in spec["epochs"] if e["us"]["requested"]}, {cue})
def test_split_and_replicate_seeds_are_independent_fullwidth(self):
manifest = self.original["seed_manifest"]
all_seeds = [r["seed"] for r in manifest["independent_streams"]]
self.assertEqual(len(all_seeds), len(set(all_seeds)))
by_block = {}
by_split = {}
for row in manifest["independent_streams"]:
by_block.setdefault(row["key"][0], set()).add(row["seed"])
by_split.setdefault(row["split"], set()).add(row["seed"])
self.assertEqual(len(by_block), 40)
blocks = list(by_block)
for i, block in enumerate(blocks):
for other in blocks[i + 1:]:
self.assertFalse(by_block[block] & by_block[other])
self.assertFalse(by_split["training"] & by_split["evaluation"])
self.assertTrue(any(seed > 2**32 for seed in all_seeds))
for block in self.original["plan"]["blocks"]:
for probe in block["evaluation_streams"]:
self.assertIn(probe["seed"], by_split["evaluation"])
self.assertEqual(probe["seed_bits"], 63)
def test_generic_valid_alias_can_still_be_protocol_illegal(self):
for source, destination in (
(("b-rep1", "normal", "CS", "A", "00", "paired"), ("b-rep2", "normal", "CS", "A", "00", "frozen")),
(("b", "normal", "US", "A", "00", "paired"), ("b", "normal", "CS", "A", "00", "frozen")),
(("b", "normal", "CS", "A", "00", "paired"), ("b", "delayed", "CS", "A", "00", "frozen")),
(("b", "normal", "CS", "A", "00", "paired"), ("b", "normal", "CS", "B", "00", "frozen")),
(("b", "normal", "CS", "A", "00", "paired"), ("b", "normal", "CS", "A", "01", "frozen")),
(("b", "normal", "CS", "A", "00", "paired"), ("b", "normal", "CS", "A", "00", "undeclared-control")),
):
r = seed_registry.SeedRegistry(1)
r.add("training", source)
r.alias("training", destination, "training", source, reason="Synthetic prohibited crossing")
self.assertTrue(seed_registry.validate_manifest(r.manifest()))
with self.assertRaises(ValueError):
m._validate_alias_permissions(r.manifest())
def test_mutations_fail_even_after_forging_payload_sha(self):
def first(p):
return p["plan"]["blocks"][0]["suites"][0]["arms"][0]["epochs"][0]
mutations = (
lambda p: p["plan"]["blocks"].pop(),
lambda p: p["plan"]["blocks"].__setitem__(1, copy.deepcopy(p["plan"]["blocks"][0])),
lambda p: p["plan"]["blocks"][0]["suites"][0]["arms"][0]["epochs"].pop(),
lambda p: first(p)["cs"].update(seed=first(p)["cs"]["seed"] & 0xffffffff),
lambda p: first(p)["cs"].update(seed_bits=32),
lambda p: first(p)["cs"].update(odor="post-outcome-selected odor"),
lambda p: p["plan"].update(execution_authorized=0),
lambda p: p["budget"].update(evaluation_epochs=0),
)
for index, mutate in enumerate(mutations):
payload = copy.deepcopy(self.original)
mutate(payload)
payload["payload_sha256"] = m._sha({k: v for k, v in payload.items() if k != "payload_sha256"})
with self.subTest(mutation=index), self.assertRaises(ValueError):
m.validate_trial_plan(payload)
def test_deterministic_small_shuffle_and_fullwidth_seed(self):
a = m._Shuffle(23).shuffled(range(30))
self.assertEqual(a, m._Shuffle(23).shuffled(range(30)))
self.assertNotEqual(a, m._Shuffle(23 + 2**32).shuffled(range(30)))
self.assertEqual(sorted(a), list(range(30)))
with self.assertRaises(ValueError):
m._Shuffle(True)
def test_integer_rejection_advances_counter_not_seed(self):
shuffle = m._Shuffle(23 + 2**32)
with patch.object(m, "_sha", side_effect=["f" * 64, "0" * 64]) as hashed:
self.assertEqual(shuffle.below(3), 0)
self.assertEqual(shuffle.counter, 2)
self.assertEqual(hashed.call_args_list[0].args[0], [m.SHUFFLE_VERSION, 23 + 2**32, 0])
self.assertEqual(hashed.call_args_list[1].args[0], [m.SHUFFLE_VERSION, 23 + 2**32, 1])
if __name__ == "__main__":
unittest.main()