"""CPU synthetic fixtures only: no real brain, file receipts or science data.""" import copy import hashlib import json import unittest import numpy as np import learning_gates as g NODES = np.arange(6, dtype=np.int32) POPS = {'MBON': np.array([0, 1, 2], np.int32), 'legacyDownstream': NODES.copy(), 'nonMBONDownstream': np.array([3, 4, 5], np.int32), 'MBON01': np.array([0], np.int32), 'MBON12': np.array([1], np.int32)} C, N = g.M.CUES def sha(value): return hashlib.sha256(str(value).encode()).hexdigest() def registry(): blocks = {} counter = 0 for mode in g.M.MODES: for dan in g.M.DAN_TYPES: for cue in g.M.CUES: for rep in range(1, 6): block = f'{mode}/{dan}/{cue}/{rep}' base = 2**40 + counter * 10 blocks[block] = dict(mode=mode, dan_type=dan, compartment_verbatim=g.DAN_COMPARTMENTS[dan], conditioned_cue=cue, replicate=rep, training_sources=[dict(stream_id=f'{block}/{split}/{assay}', seed=base + j, split=split) for j, (split, assay) in enumerate((('training', 'normal'), ('training', 'delayed'), ('training', 'reversal'), ('design', 'order')))], evaluation_sources={cue: dict(stream_id=f'{block}/evaluation/{cue}', seed=base + 7 + j, split='evaluation') for j, cue in enumerate(g.M.CUES)}, evaluation_input_sha256={cue: sha(f'{block}/input/{cue}') for cue in g.M.CUES}) counter += 1 return dict(schema=g.REGISTRY_SCHEMA, protocol_sha256=sha('synthetic-unregistered-protocol'), population_binding_sha256=sha('synthetic-population-binding'), nodes_raw_sha256=g.M._hash(NODES), population_membership_sha256=g.M.validate_populations(NODES, POPS)['membership_sha256'], blocks=blocks) def authority(reg): return dict(schema=g.AUTHORITY_SCHEMA, registry_sha256=g.descriptor_sha256(reg), verification_receipt_sha256=sha('SYNTHETIC-NOT-A-REAL-VERIFICATION'), execution_inventory_sha256=sha('SYNTHETIC-NOT-EXECUTED'), population_binding_sha256=reg['population_binding_sha256']) def state(direction=0, *, downstream=True, g_effect=True, offset=0): counts = [1, 1, 0, 1, 0, 0] if direction: counts[1 if direction > 0 else 0] += abs(direction) if downstream: counts[3] += abs(direction) events = sorted((10 + k * 20 + offset, i) for i, count in enumerate(counts) for k in range(count)) conductance = np.zeros(6, np.float64) if direction and g_effect: conductance[0] = .002 * abs(direction) return dict(step=np.array([tick for tick, _ in events], np.int32), neuron_idx=np.array([i for _, i in events], np.int32), counts=np.array(counts, np.int32), v=np.full(6, -.052, np.float64), g=conductance, ref=np.zeros(6, np.int32)) def states(conditioned=C, strength=0, *, neutral_strength=0, downstream=True): neutral = next(cue for cue in g.M.CUES if cue != conditioned) return {conditioned: state(strength, downstream=downstream), neutral: state(neutral_strength, downstream=downstream)} def pair(reg, block, label, values, *, delta, checkpoint, policy='common_frozen_reset'): return dict(schema=g.PAIR_SCHEMA, block_id=block, snapshot_id=f'{block}/{label}', receipt_sha256=sha(f'{block}/{label}/pair'), probe_receipt_sha256={cue: sha(f'{block}/{label}/probe/{cue}') for cue in g.M.CUES}, evaluation_input_sha256=dict(reg['blocks'][block]['evaluation_input_sha256']), state_raw_sha256={cue: g.M.validate_state(values[cue], nodes=NODES, steps=1200)['hashes'] for cue in g.M.CUES}, states=values, state_policy=policy, source_checkpoint_sha256=checkpoint, delta_sha256=delta, probe_steps=1200, cs_start_tick=0, cs_stop_tick_exclusive=1000, US_requested=False, weight_updates_enabled=False, feedback_to_training_or_idle=False) def rebind(pair_value): pair_value['state_raw_sha256'] = {cue: g.M.validate_state(pair_value['states'][cue], nodes=NODES, steps=1200)['hashes'] for cue in g.M.CUES} def execution(block, arm, cue, *, initial=None): return dict(receipt_sha256=sha(f'{block}/{arm}/execution'), recording_verified=True, complete=True, completed_epochs=100, reinforcement_opportunities=50, terminal_pairings=50, epoch_duration_ms=120., cs_duration_ms=100., us_tick=900, initial_delta_sha256=initial or sha('zero-delta'), terminal_delta_sha256=sha(f'{block}/{arm}/delta'), terminal_checkpoint_sha256=sha(f'{block}/{arm}/terminal'), neural_state_policy='fresh_epoch_delta_only', weight_rule='frozen_temporal_eligibility_v2', reinforced_cue=cue) def common(reg, block): value = reg['blocks'][block] return dict(replicate_id=block, replicate_unit='independent_training', training_stream_ids=[x['stream_id'] for x in value['training_sources']], evaluation_stream_ids=[value['evaluation_sources'][cue]['stream_id'] for cue in g.M.CUES]) def blocks(reg, mode, dan, cue): return [key for key, value in reg['blocks'].items() if (value['mode'], value['dan_type'], value['conditioned_cue']) == (mode, dan, cue)] def reversal_rows(reg, mode='physiological', dan='PAM15', cue=C): rows = [] neutral = next(x for x in g.M.CUES if x != cue) direction = 1 if dan == 'PAM15' else -1 for block in blocks(reg, mode, dan, cue): acq = execution(block, 'acquisition', cue) executions = dict(acquisition=acq, reversal=execution(block, 'reversal', neutral, initial=acq['terminal_delta_sha256']), no_reversal=execution(block, 'no_reversal', cue, initial=acq['terminal_delta_sha256'])) row = common(reg, block) row['execution'] = executions row['pre'] = pair(reg, block, 'pre', states(cue), delta=acq['initial_delta_sha256'], checkpoint=sha('reset')) values = dict(acquisition=states(cue, direction), reversal=states(cue, direction, neutral_strength=2 * direction), no_reversal=states(cue, 2 * direction)) for arm, value in values.items(): e = executions[arm] row[arm] = pair(reg, block, arm, value, delta=e['terminal_delta_sha256'], checkpoint=e['terminal_checkpoint_sha256']) rows.append(row) return rows def idle(block, branch, cue): source = execution(block, 'paired' if branch == 'trained' else 'frozen', cue) if branch == 'frozen': source['terminal_delta_sha256'] = source['initial_delta_sha256'] checkpoints, previous = {}, source['terminal_checkpoint_sha256'] for second in (0, 1, 10): current = sha(f'{block}/{branch}/idle-checkpoint/{second}') checkpoints[str(second)] = dict(checkpoint_sha256=current, parent_checkpoint_sha256=previous, elapsed_ticks=second * 10000, absolute_tick=1200 + second * 10000, delta_sha256=source['terminal_delta_sha256'], carry_sha256={key: sha(f'{block}/{branch}/{second}/{key}') for key in g.CARRY}) previous = current return dict(source_checkpoint_sha256=source['terminal_checkpoint_sha256'], delta_sha256=source['terminal_delta_sha256'], idle_lineage_id=f'{block}/{branch}/idle10', idle_receipt_sha256=sha(f'{block}/{branch}/idle-receipt'), total_idle_ticks=100000, no_input=True, updates_disabled=True, reset_state=False, analytic_time_advance=False, one_second_is_prefix_of_ten_second=True, checkpoints=checkpoints, source_execution=source) def retention_rows(reg, mode='physiological', dan='PAM15', cue=C): rows = [] direction = 1 if dan == 'PAM15' else -1 for block in blocks(reg, mode, dan, cue): row = common(reg, block) row.update(execution={branch: idle(block, branch, cue) for branch in ('trained', 'frozen')}, pre={}, checkpoints={}) for branch in ('trained', 'frozen'): e = row['execution'][branch] row['pre'][branch] = pair(reg, block, 'pre-' + branch, states(cue), delta=e['source_execution']['initial_delta_sha256'], checkpoint=sha('reset')) for second in (0, 1, 10): row['checkpoints'][str(second)] = {} for branch in ('trained', 'frozen'): e = row['execution'][branch] row['checkpoints'][str(second)][branch] = pair(reg, block, f'{branch}/{second}', states(cue, direction if branch == 'trained' else 0), delta=e['delta_sha256'], checkpoint=e['checkpoints'][str(second)]['checkpoint_sha256'], policy='continuous_checkpoint') rows.append(row) return rows def acquisition_rows(reg, mode='physiological', dan='PAM15', cue=C, assay='normal'): rows = [] for block in blocks(reg, mode, dan, cue): row = common(reg, block) row.update(assay=assay, mode=mode, dan_type=dan, conditioned_cue=cue, arms={}) for arm in g.M.ARMS: strength = (1 if dan == 'PAM15' else -1) if arm == 'paired' else 0 metric = g.M.arm_metrics(states(cue), states(cue, strength), conditioned_cue=cue, nodes=NODES, populations=POPS) e = dict(complete=True, recording_verified=True, receipt_sha256=sha(f'{block}/{assay}/{arm}/execution'), assay=assay, epoch_duration_ms=180. if assay == 'delayed' else 120., cs_duration_ms=100., us_tick=1500 if assay == 'delayed' else 900, completed_epochs=150 if arm == 'unpaired' else 100, reinforcement_opportunities=0 if arm == 'CS-only' else 50, terminal_pairings=50) row['arms'][arm] = dict(metrics=metric, execution=e) rows.append(row) return rows class GatesTests(unittest.TestCase): def setUp(self): self.reg = registry() self.auth = authority(self.reg) def evaluate(self, assay, rows, *, mode='physiological', dan='PAM15', cue=C): kwargs = dict(mode=mode, dan_type=dan, conditioned_cue=cue, registry=self.reg, authority=self.auth) if assay in ('normal', 'delayed'): return g.evaluate_acquisition(rows, assay=assay, **kwargs) return getattr(g, 'evaluate_' + assay)(rows, nodes=NODES, populations=POPS, downstream_policy=g.DOWNSTREAM_POLICY, **kwargs) def test_registry_checks_real_seed_collision_and_exact_40_cells(self): for mutation in ('eval_train', 'eval_eval', 'train_train', 'duplicate_cell', '63bit', 'boolseed', 'extra_eval', 'missing_block'): reg = registry() values = list(reg['blocks'].values()) if mutation == 'eval_train': values[0]['evaluation_sources'][C]['seed'] = values[1]['training_sources'][0]['seed'] if mutation == 'eval_eval': values[1]['evaluation_sources'][C]['seed'] = values[0]['evaluation_sources'][C]['seed'] if mutation == 'train_train': values[1]['training_sources'][0]['stream_id'] = values[0]['training_sources'][0]['stream_id'] if mutation == 'duplicate_cell': values[1]['replicate'] = values[0]['replicate'] if mutation == '63bit': values[0]['training_sources'][0]['seed'] = 2**63 if mutation == 'boolseed': values[0]['training_sources'][0]['seed'] = True if mutation == 'extra_eval': values[0]['evaluation_sources']['extra'] = values[0]['evaluation_sources'][C] if mutation == 'missing_block': reg['blocks'].pop(next(iter(reg['blocks']))) with self.subTest(mutation=mutation), self.assertRaises(ValueError): g.validate_registry(reg, expected_sha256=g.descriptor_sha256(reg)) self.assertEqual(g.validate_registry(self.reg, expected_sha256=self.auth['registry_sha256'])['blocks'], 40) def test_registry_hash_and_population_mutation_rejected(self): self.reg['protocol_sha256'] = sha('other') with self.assertRaisesRegex(ValueError, 'changed'): self.evaluate('reversal', reversal_rows(self.reg)) self.reg = registry(); self.auth = authority(self.reg) bad = dict(POPS, nonMBONDownstream=np.array([3, 4], np.int32)) with self.assertRaises(ValueError): g.evaluate_reversal(reversal_rows(self.reg), mode='physiological', dan_type='PAM15', conditioned_cue=C, registry=self.reg, authority=self.auth, nodes=NODES, populations=bad, downstream_policy=g.DOWNSTREAM_POLICY) def test_all_four_assays_both_signs_reciprocal_cues_and_no_claims(self): for assay, factory in (('normal', acquisition_rows), ('delayed', acquisition_rows), ('reversal', reversal_rows), ('retention', retention_rows)): for dan in g.M.DAN_TYPES: for cue in g.M.CUES: rows = factory(self.reg, dan=dan, cue=cue, **({'assay': assay} if assay in ('normal', 'delayed') else {})) result = self.evaluate(assay, rows, dan=dan, cue=cue) with self.subTest(assay=assay, dan=dan, cue=cue): self.assertTrue(result['predicate_pass']) self.assertEqual(result['successful_same_replicate_conjunctions'], 5) self.assertFalse(result['scientific_learning_pass_claimed']) self.assertFalse(result['external_authority_verified']) self.assertFalse(result['data_backed_claimed']) json.dumps(result, allow_nan=False) def test_reversal_zero_is_failure_but_legacy_style_inequality_is_true(self): rows = reversal_rows(self.reg) for row in rows: row['reversal']['states'] = states(C, 1, neutral_strength=1); rebind(row['reversal']) result = self.evaluate('reversal', rows) self.assertFalse(result['predicate_pass']) self.assertTrue(all(row['legacy_style_sign_inequality_on_registered_S'] for row in result['replicates'])) def test_reversal_old_cue_only_and_mbon_only_cannot_satisfy_new_downstream_gate(self): for only_old_cue in (True, False): rows = reversal_rows(self.reg) for row in rows: if only_old_cue: row['reversal']['states'] = states(C, -1) else: row['reversal']['states'][N] = state(2, downstream=False) rebind(row['reversal']) result = self.evaluate('reversal', rows) self.assertFalse(result['predicate_pass']) if not only_old_cue: change = result['replicates'][0]['acquisition_to_reversal_changes_by_cue'][N]['populations'] self.assertTrue(change['legacyDownstream']['events_changed']) self.assertFalse(change['nonMBONDownstream']['events_changed']) def test_reversal_same_replicate_conjunction_and_exact_three(self): rows = reversal_rows(self.reg) rows[2]['no_reversal']['states'] = states(C, -1); rebind(rows[2]['no_reversal']) rows[3]['reversal']['states'] = states(C, 1, neutral_strength=1); rebind(rows[3]['reversal']) rows[4]['reversal']['states'][N] = state(2, downstream=False); rebind(rows[4]['reversal']) result = self.evaluate('reversal', rows) self.assertEqual(result['successful_same_replicate_conjunctions'], 2) self.assertFalse(result['predicate_pass']) rows[2] = reversal_rows(self.reg)[2] self.assertTrue(self.evaluate('reversal', rows)['predicate_pass']) def test_retention_same_replicate_at_both_times_not_separate_time_majorities(self): rows = retention_rows(self.reg) for index, times in ((2, ('10',)), (3, ('1',)), (4, ('1', '10'))): for second in times: p = rows[index]['checkpoints'][second]['trained'] p['states'] = states(); rebind(p) result = self.evaluate('retention', rows) self.assertEqual(result['successful_same_replicate_conjunctions'], 2) self.assertFalse(result['predicate_pass']) rows[2] = retention_rows(self.reg)[2] self.assertTrue(self.evaluate('retention', rows)['predicate_pass']) def test_retention_frozen_equality_opposite_sign_and_no_nonmbon_change_fail(self): for variant in ('equal_frozen', 'opposite', 'mbon_only'): rows = retention_rows(self.reg) for row in rows: for second in ('1', '10'): pair_value = row['checkpoints'][second]['trained'] if variant == 'equal_frozen': pair_value = row['checkpoints'][second]['frozen']; pair_value['states'] = states(C, 1) elif variant == 'opposite': pair_value['states'] = states(C, -1) else: pair_value['states'] = states(C, 1, downstream=False) rebind(pair_value) self.assertFalse(self.evaluate('retention', rows)['predicate_pass']) def test_retention_chain_full_state_clock_and_probe_policy_mutations_rejected(self): for variant in ('clock', 'delta', 'parent', 'queue', 'reset', 'analytic', 'prefix', 'missing_time', 'input', 'feedback', 'newUS', 'short_probe', 'fresh_probe', 'raw_state'): rows = retention_rows(self.reg) execution_value = rows[0]['execution']['trained'] cp = execution_value['checkpoints']['10'] p = rows[0]['checkpoints']['10']['trained'] if variant == 'clock': cp['absolute_tick'] += 1 if variant == 'delta': cp['delta_sha256'] = sha('changed') if variant == 'parent': cp['parent_checkpoint_sha256'] = execution_value['source_checkpoint_sha256'] if variant == 'queue': cp['carry_sha256'].pop('apl_pending') if variant == 'reset': execution_value['reset_state'] = True if variant == 'analytic': execution_value['analytic_time_advance'] = True if variant == 'prefix': execution_value['one_second_is_prefix_of_ten_second'] = False if variant == 'missing_time': rows[0]['checkpoints'].pop('0') if variant == 'input': p['evaluation_input_sha256'][C] = sha('other') if variant == 'feedback': p['feedback_to_training_or_idle'] = True if variant == 'newUS': p['US_requested'] = True if variant == 'short_probe': p['probe_steps'] = 1000 if variant == 'fresh_probe': p['state_policy'] = 'common_frozen_reset' if variant == 'raw_state': p['states'][C]['v'][0] += .001 with self.subTest(variant=variant), self.assertRaises(ValueError): self.evaluate('retention', rows) def test_reversal_delta_lineage_terminal_inventory_and_receipt_mutations_rejected(self): for variant in ('resetdelta', 'early', 'epochs', 'cue', 'receipt', 'snapshot_receipt', 'probeAB', 'evaldraw', 'wrongcell', 'trainstream'): rows = reversal_rows(self.reg) e = rows[0]['execution']['reversal'] if variant == 'resetdelta': e['initial_delta_sha256'] = sha('zero') if variant == 'early': e['terminal_pairings'] = 20 if variant == 'epochs': e['completed_epochs'] = 99 if variant == 'cue': e['reinforced_cue'] = C if variant == 'receipt': e['receipt_sha256'] = rows[0]['execution']['acquisition']['receipt_sha256'] if variant == 'snapshot_receipt': rows[0]['reversal']['receipt_sha256'] = rows[0]['acquisition']['receipt_sha256'] if variant == 'probeAB': rows[0]['reversal']['probe_receipt_sha256'][N] = rows[0]['reversal']['probe_receipt_sha256'][C] if variant == 'evaldraw': rows[0]['replicate_unit'] = 'evaluation' if variant == 'wrongcell': rows[1]['replicate_id'] = rows[0]['replicate_id'] if variant == 'trainstream': rows[0]['training_stream_ids'] = rows[1]['training_stream_ids'] with self.subTest(variant=variant), self.assertRaises(ValueError): self.evaluate('reversal', rows) def test_acquisition_controls_and_frozen_gate_are_preserved(self): for variant in ('missing_control', 'delayed_timing', 'metric_mutation', 'population'): rows = acquisition_rows(self.reg, assay='delayed') if variant == 'missing_control': rows[0]['arms'].pop('DAN-only') if variant == 'delayed_timing': rows[0]['arms']['frozen']['execution']['us_tick'] = 900 if variant == 'metric_mutation': rows[0]['arms']['paired']['metrics']['rawS'] = 999. if variant == 'population': rows[0]['arms']['paired']['metrics']['cues'][C]['nodes_raw_sha256'] = sha('other') with self.subTest(variant=variant), self.assertRaises(ValueError): self.evaluate('delayed', rows) def normal_reports(self): return [self.evaluate('normal', acquisition_rows(self.reg, mode, dan, cue), mode=mode, dan=dan, cue=cue) for mode in g.M.MODES for dan in g.M.DAN_TYPES for cue in g.M.CUES] def test_cohort_exact_eight_cells_missing_not_run_and_mutation_rejected(self): reports = self.normal_reports() result = g.summarize_cohort(reports, registry=self.reg, authority=self.auth, required_assays=('normal',)) self.assertTrue(result['all_functional_predicates_pass']) self.assertFalse(result['legacy_H_and_integrity_gates_included']) missing = g.summarize_cohort(reports[:-1], registry=self.reg, authority=self.auth, required_assays=('normal',)) self.assertIsNone(missing['all_functional_predicates_pass']) self.assertEqual(sum(x['status'] == 'NOT-RUN' for x in missing['table']), 1) for variant in ('duplicate', 'predicate', 'conjunction', 'controlgain', 'unsupportedclaim'): changed = copy.deepcopy(reports) if variant == 'duplicate': changed[-1] = changed[0] if variant == 'predicate': changed[0]['predicate_pass'] = False if variant == 'conjunction': changed[0]['replicates'][0]['conjunction_pass'] = False if variant == 'controlgain': next(iter(changed[0]['legacy_control_MBON_gains'].values()))[changed[0]['replicates'][0]['replicate_id']] = 900. if variant == 'unsupportedclaim': changed[0]['scientific_learning_pass_claimed'] = True with self.subTest(variant=variant), self.assertRaises(ValueError): g.summarize_cohort(changed, registry=self.reg, authority=self.auth, required_assays=('normal',)) def test_legacy_H_mapping_absent_and_both_DAN_control_sources_explicit(self): reports = self.normal_reports() self.assertEqual(g.legacy_h_cohort_audit(reports, registry=self.reg, authority=self.auth, declared_mapping=None)['status'], 'NOT-TESTABLE') good = g.legacy_h_cohort_audit(reports, registry=self.reg, authority=self.auth, declared_mapping=g.H_MAPPING) self.assertEqual(len(good['groups']), 4) self.assertTrue(good['both_pass']) rows = acquisition_rows(self.reg, dan='PPL103') for row in rows: row['arms']['random reward']['metrics'] = g.M.arm_metrics(states(), states(C, -2), conditioned_cue=C, nodes=NODES, populations=POPS) replacement = self.evaluate('normal', rows, dan='PPL103') reports = [replacement if (x['mode'], x['dan_type'], x['conditioned_cue']) == ('physiological', 'PPL103', C) else x for x in reports] result = g.legacy_h_cohort_audit(reports, registry=self.reg, authority=self.auth, declared_mapping=g.H_MAPPING) group = next(x for x in result['groups'] if x['mode'] == 'physiological' and x['conditioned_cue'] == C) self.assertTrue(group['control_sources']['PAM15']['predicate_pass']) self.assertFalse(group['control_sources']['PPL103']['predicate_pass']) self.assertFalse(result['both_pass']) def test_full_cohort_recomputes_followup_predicates_and_keeps_assay_majorities_separate(self): reports = [] for mode in g.M.MODES: for dan in g.M.DAN_TYPES: for cue in g.M.CUES: for assay, factory in (('normal', acquisition_rows), ('delayed', acquisition_rows), ('reversal', reversal_rows), ('retention', retention_rows)): rows = factory(self.reg, mode, dan, cue, **({'assay': assay} if assay in ('normal', 'delayed') else {})) reports.append(self.evaluate(assay, rows, mode=mode, dan=dan, cue=cue)) result = g.summarize_cohort(reports, registry=self.reg, authority=self.auth, required_assays=g.ASSAYS) self.assertEqual(len(result['table']), 32) self.assertTrue(result['all_functional_predicates_pass']) for variant in ('reversal_sign', 'reversal_predicate', 'retention_sign', 'retention_predicate', 'retention_time0'): changed = copy.deepcopy(reports) rev = next(x for x in changed if x['assay'] == 'reversal')['replicates'][0] ret = next(x for x in changed if x['assay'] == 'retention')['replicates'][0] if variant == 'reversal_sign': rev['rawS']['reversal'] = 999. if variant == 'reversal_predicate': rev['predicates']['strict_three_signed_relations'] = False if variant == 'retention_sign': ret['times']['10']['rawS_trained'] = 999. if variant == 'retention_predicate': ret['times']['10']['predicates']['beyond_time_matched_frozen'] = False if variant == 'retention_time0': ret['times']['0']['contributes_to_gate'] = True with self.subTest(variant=variant), self.assertRaises(ValueError): g.summarize_cohort(changed, registry=self.reg, authority=self.auth, required_assays=g.ASSAYS) def test_frozen_retention_source_cannot_claim_updated_delta(self): rows = retention_rows(self.reg) rows[0]['execution']['frozen']['source_execution']['initial_delta_sha256'] = sha('another-zero') with self.assertRaisesRegex(ValueError, 'Frozen training'): self.evaluate('retention', rows) def test_inputs_not_mutated_and_output_does_not_alias_inputs(self): rows = reversal_rows(self.reg) before = copy.deepcopy(rows) result = self.evaluate('reversal', rows) for old, new in zip(before, rows): for snapshot in ('pre', 'acquisition', 'reversal', 'no_reversal'): for cue in g.M.CUES: for key in g.M.STATE_KEYS: np.testing.assert_array_equal(old[snapshot]['states'][cue][key], new[snapshot]['states'][cue][key]) result['replicates'][0]['metrics']['acquisition']['rawS'] = 888. self.assertEqual(self.evaluate('reversal', rows)['replicates'][0]['rawS']['acquisition'], 1.) if __name__ == '__main__': unittest.main()