"""Pure synthetic checks for stateless pre-first event validation.""" import unittest import numpy as np from validate_events import COLUMN_DTYPES, N_GLOBAL, validate_batch def fixture(): reference = dict(edge_ids=np.array([7, 20, 99], np.int64), srcglobals=np.array([3, 4, 3], np.int32), dstglobals=np.array([10, 10, 20], np.int32), weights=np.array([.001, -.002, .003], np.float64), target_globals=np.array([10, 20, 30], np.int32), target_first=np.array([30, -1, 0], np.int32), source_event_keys=np.array([3, 4, 4 * N_GLOBAL + 3], np.int64)) # Arbitrary order, repeated edge at a later tick, negative and positive. values = dict(edge_id=[99, 20, 7, 99], source_neuron_idx=[3, 4, 3, 3], target_neuron_idx=[20, 10, 10, 20], source_spike_tick=[4, 0, 0, 0], arrival_tick=[22, 18, 18, 18], weight_sign=[1, -1, 1, 1], ref_at_arrival=[0] * 4, weight_SI=[.003, -.002, .001, .003], accepted=[True] * 4) columns = {name: np.array(values[name], dtype) for name, dtype in COLUMN_DTYPES.items()} return columns, reference class EventValidationTests(unittest.TestCase): def test_unordered_rows_real_edges_signed_weights_and_never_first(self): columns, ref = fixture() original = {k: v.copy() for k, v in columns.items()} result = validate_batch(columns, **ref) self.assertEqual(result['count'], 4) np.testing.assert_array_equal(result['per_target_count'], [2, 2, 0]) self.assertEqual(result['per_target_count'].dtype, np.int64) self.assertFalse(result['unique_cause_claim']) self.assertFalse(result['cross_batch_completeness_checked']) for key in columns: np.testing.assert_array_equal(columns[key], original[key]) def test_empty_valid_but_missing_wrong_dtype_or_shape_still_rejected(self): columns, ref = fixture() empty = {name: value[:0] for name, value in columns.items()} result = validate_batch(empty, **ref) self.assertEqual(result['count'], 0) np.testing.assert_array_equal(result['per_target_count'], [0, 0, 0]) bad = empty.copy() bad['arrival_tick'] = np.empty(0, np.int64) with self.assertRaises(ValueError): validate_batch(bad, **ref) bad = empty.copy() del bad['accepted'] with self.assertRaises(ValueError): validate_batch(bad, **ref) bad = empty.copy() bad['weight_sign'] = np.empty((0, 1), np.int32) with self.assertRaises(ValueError): validate_batch(bad, **ref) def test_every_edge_field_and_weight_bit_is_checked(self): for field, value in [('edge_id', 8), ('source_neuron_idx', 4), ('target_neuron_idx', 10), ('weight_sign', -1), ('weight_SI', np.nextafter(.003, np.inf))]: with self.subTest(field=field): columns, ref = fixture() columns[field][0] = value with self.assertRaises(ValueError): validate_batch(columns, **ref) columns, ref = fixture() ref['weights'][2] = 0. columns['weight_SI'][[0, 3]] = -0. with self.assertRaisesRegex(ValueError, 'bits mismatch'): validate_batch(columns, **ref) columns['weight_SI'][[0, 3]] = 0. with self.assertRaisesRegex(ValueError, 'zero-weight'): validate_batch(columns, **ref) def test_guard_strict_prefirst_and_source_delay(self): changes = [('accepted', False), ('ref_at_arrival', 1), ('source_spike_tick', -1), ('source_spike_tick', 3), ('arrival_tick', 21)] for field, value in changes: with self.subTest(field=field): columns, ref = fixture() columns[field][0] = value with self.assertRaises(ValueError): validate_batch(columns, **ref) for first in (22, 21): columns, ref = fixture() ref['target_first'][1] = first with self.assertRaisesRegex(ValueError, 'strictly before'): validate_batch(columns, **ref) def test_source_event_membership_and_target_population(self): columns, ref = fixture() ref['source_event_keys'] = ref['source_event_keys'][:2] with self.assertRaisesRegex(ValueError, 'source spike'): validate_batch(columns, **ref) columns, ref = fixture() ref['target_globals'] = np.array([10, 30], np.int32) ref['target_first'] = np.array([30, 0], np.int32) with self.assertRaisesRegex(ValueError, 'target global'): validate_batch(columns, **ref) def test_duplicate_rows_rejected_without_sort_assumption(self): columns, ref = fixture() for value in columns.values(): value[0] = value[3] with self.assertRaisesRegex(ValueError, 'Duplicate edge event'): validate_batch(columns, **ref) def test_invalid_reference_schema_order_duplicate_range_nonfinite(self): variants = [dict(edge_ids=np.array([7, 20, 99], np.int32)), dict(edge_ids=np.array([7, 99, 20], np.int64)), dict(edge_ids=np.array([7, 20, 20], np.int64)), dict(srcglobals=np.array([3, 4, N_GLOBAL], np.int32)), dict(weights=np.array([.001, -.002, np.inf])), dict(target_first=np.array([30, -2, 0], np.int32)), dict(source_event_keys=np.array([3, 3, 4], np.int64)), dict(source_event_keys=np.array([4, 3], np.int64)), dict(source_event_keys=np.array([-1, 3], np.int64))] for change in variants: with self.subTest(change=change): columns, ref = fixture() with self.assertRaises(ValueError): validate_batch(columns, **(ref | change)) def test_optional_design_and_local_row_fields_are_not_assumed(self): columns, ref = fixture() columns['incoming_edge_row'] = np.array([1000, 2, 1, 1000], np.int64) columns['target_is_design'] = np.array([True, False, True, False]) self.assertEqual(validate_batch(columns, **ref)['count'], 4) bad = columns.copy() bad['accepted'] = columns['accepted'].astype(np.uint8) with self.assertRaises(ValueError): validate_batch(bad, **ref) if __name__ == '__main__': unittest.main(verbosity=2)