Files
digital-drosophila/models/male-cns-lif/apl-timing-lab-v1/tests/test_compare_timing.py
T
2026-09-30 12:08:25 +08:00

170 lines
9.0 KiB
Python

"""Synthetic array-only comparator tests: no backend or scientific inputs."""
from pathlib import Path
import copy
import sys
import unittest
import numpy as np
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
import compare_timing as compare
def traces(delay, steps, width=3):
generated = np.arange(steps * width, dtype=np.float32).reshape(steps, width) * np.float32(.125)
full = np.concatenate((np.zeros((delay, width), dtype=np.float32), generated))
return generated, full[:steps].copy(), full[steps:].copy()
def sequence_result(generated, applied, pending, delay):
checks, differences = {}, {}
compare._timing_sequence_checks(generated, applied, pending, delay, 'test', checks, differences)
return checks, differences
def metadata_run(delay, steps, cpu=False):
generated, applied, pending = traces(delay, steps, 4064)
state = np.tile(np.array([.25, 1.125], np.float32), (steps, 1))
timing = dict(apl_delay_steps=delay, steps=steps, pending_generated_count=min(steps, delay),
generated_rows=steps, applied_generated_rows=max(steps-delay, 0), pending_shape=[delay, 4064],
pending_initial_zero_prefix=max(delay-steps, 0),
pending_application_tick_start=steps, pending_application_tick_stop_exclusive=steps+delay,
pending_generated_tick_start=max(steps-delay, 0), pending_generated_tick_stop_exclusive=steps,
pending_raw_sha256=compare.raw_hash(pending, '<f4'),
final_apl_state_float32=state[-1].tolist(),
final_apl_state_u32_bits=state[-1].view(np.uint32).tolist(), final_apl_state_nonzero=True,
brain_tail_advanced=False, future_apl_decay_or_feedback_generated=False,
finite_generated_sequence_only=True, closed_loop_branch_integrals_claimed_equal=False,
biological_delay_measurement_claimed=False, brain_steps_after_window=0,
APL_generation_steps_after_window=0, future_generated_decay_tail_included=False)
summary = dict(apl_timing=copy.deepcopy(timing), pending_generated_count=min(steps, delay),
pending_initial_zero_prefix=max(delay-steps, 0), pending_tail_shape=[delay, 4064],
future_generated_decay_tail_included=False, APL_worker_ticks=steps)
return dict(apl={'inhibition': generated, 'applied': applied, 'state': state}, pending=pending,
timing=timing, summary=summary)
class TimingComparisonTests(unittest.TestCase):
def test_delay_zero_and_18_partition(self):
for delay in (0, 18):
for steps in (1, 7, 18, 19, 54):
with self.subTest(delay=delay, steps=steps):
checks, differences = sequence_result(*traces(delay, steps), delay)
self.assertTrue(all(checks.values()), differences)
def test_one_tick_shift_rejected_independently(self):
generated, applied, pending = traces(18, 40)
applied[18:] = generated[1:23]
checks, _ = sequence_result(generated, applied, pending, 18)
self.assertFalse(checks['test.fixed_delay_applied.bitwise_exact'])
def test_pending_wrong_chronology_rejected(self):
generated, applied, pending = traces(18, 40)
checks, _ = sequence_result(generated, applied, pending[::-1].copy(), 18)
self.assertFalse(checks['test.fixed_delay_pending.bitwise_exact'])
def test_initial_zero_must_be_positive_bits(self):
generated, applied, pending = traces(18, 4)
pending.view(np.uint32)[0, 0] = 0x80000000
checks, _ = sequence_result(generated, applied, pending, 18)
self.assertFalse(checks['test.fixed_delay_pending.bitwise_exact'])
generated, applied, pending = traces(18, 22)
applied.view(np.uint32)[0, 0] = 0x80000000
checks, _ = sequence_result(generated, applied, pending, 18)
self.assertFalse(checks['test.fixed_delay_applied.bitwise_exact'])
def test_generated_signed_zero_preserved(self):
generated, applied, pending = traces(0, 1)
generated.view(np.uint32)[0, 0] = 0x80000000
applied[:] = generated
checks, _ = sequence_result(generated, applied, pending, 0)
self.assertTrue(all(checks.values()))
applied.view(np.uint32)[0, 0] = 0
checks, _ = sequence_result(generated, applied, pending, 0)
self.assertFalse(checks['test.fixed_delay_applied.bitwise_exact'])
def test_schema_and_delay_fail_closed(self):
generated, applied, pending = traces(18, 4)
for delay in (True, 1, 18.0):
with self.assertRaises(ValueError):
sequence_result(generated, applied, pending, delay)
with self.assertRaises(ValueError):
sequence_result(generated.astype(np.float64), applied, pending, 18)
with self.assertRaises(ValueError):
sequence_result(generated, applied, pending[-4:], 18)
def test_nonfinite_or_negative_generated_fails_even_if_shift_matches(self):
for value in (np.inf, np.nan, -1):
generated, applied, pending = traces(0, 2)
generated[0, 0] = value
applied[:] = generated
checks, _ = sequence_result(generated, applied, pending, 0)
self.assertFalse(checks['test.timing_finite_nonnegative'])
def test_both_backend_metadata_valid(self):
for role in ('gpu', 'cpu', 'first_gpu', 'second_gpu'):
for delay, steps in ((0, 25), (18, 7), (18, 25)):
run = metadata_run(delay, steps, cpu=role == 'cpu')
checks = {}
compare._timing_metadata_checks(run, role, delay, steps, checks)
self.assertTrue(all(checks.values()), checks)
def test_metadata_rejects_wrong_count_tail_advance_and_final_state(self):
for role in ('gpu', 'cpu'):
for change in ('count', 'tail', 'final', 'prefix', 'hash'):
run = metadata_run(18, 25, cpu=role == 'cpu')
if change == 'count':
run['timing']['pending_generated_count'] = 17
elif change == 'tail':
run['timing']['brain_steps_after_window' if role == 'cpu' else 'brain_tail_advanced'] = 1
elif change == 'final':
run['timing']['final_apl_state_u32_bits'][0] += 1
elif change == 'prefix':
run['timing']['pending_initial_zero_prefix'] = False
else:
run['timing']['pending_raw_sha256'] = '0' * 64
checks = {}
compare._timing_metadata_checks(run, role, 18, 25, checks)
self.assertFalse(all(checks.values()), (role, change))
def test_g_write_uses_applied_and_is_unconditional_during_ref(self):
nodes, kc = np.array([10, 12], np.int32), np.array([10], np.int32)
v = np.full((3, 2), -.06, np.float64)
ref = np.array([[22, 0], [21, 0], [20, 0]], np.int32)
before = np.array([[0, -.001], [-0., .001], [.002, -.003]], np.float64)
applied = np.array([[.25], [0], [1]], np.float32)
after = before.copy()
after[:, 0] -= applied[:, 0].astype(np.float64) * np.float64(.001)
run = dict(pre={'v': v, 'g': before, 'ref': ref}, histories={'v': v.copy(), 'g': after, 'ref': ref.copy()},
apl={'applied': applied, 'inhibition': np.full((3, 1), 99, np.float32)})
checks, differences = {}, {}
compare._check_apl_application(run, 'test', nodes, kc, checks, differences)
self.assertTrue(all(checks.values()), differences)
run['histories']['g'] = before.copy() # Hypothetical refractory guard must fail.
checks, differences = {}, {}
compare._check_apl_application(run, 'test', nodes, kc, checks, differences)
self.assertFalse(checks['test.apl_unconditional_g_write.bitwise_exact'])
def test_pair_comparison_includes_applied_and_pending(self):
def output():
generated, applied, pending = traces(18, 20)
return dict(state={'step': np.empty(0, np.int32), 'neuron_idx': np.empty(0, np.int32),
'counts': np.zeros(1, np.int32), 'ref': np.zeros(1, np.int32),
'v': np.zeros(1, np.float64), 'g': np.zeros(1, np.float64)},
apl={'drive': np.zeros((20, 2), np.float32), 'state': np.zeros((20, 2), np.float32),
'inhibition': np.pad(generated, ((0, 0), (0, 4061))),
'applied': np.pad(applied, ((0, 0), (0, 4061)))},
pending=np.pad(pending, ((0, 0), (0, 4061))),
hashes=[], queues=[], requests=[], component={}, histories={}, pre={})
left, right = output(), output()
right['apl']['applied'][0, 0] = 1
right['pending'][0, 0] += 1
checks, differences = {}, {}
compare._pair_outputs(left, right, checks, differences, 1, 20, False)
self.assertFalse(checks['apl.applied.bitwise_exact'])
self.assertFalse(checks['apl.pending.bitwise_exact'])
if __name__ == '__main__':
unittest.main()