563 lines
39 KiB
Python
563 lines
39 KiB
Python
"""Pure, pre-registration conditioning gates; never an execution authority.
|
|
|
|
The numeric rules come from PROTOCOL_DRAFT sections 6-8 and the fixed
|
|
EVALUATION_POLICY_DRAFT. Two explicit pre-outcome operationalizations require
|
|
the caller's policy descriptor: reversal/retention downstream means the exact
|
|
nonMBONDownstream set, with legacyDownstream also reported; the old H mean
|
|
predicate is evaluated twice per (mode, original cue), using each DAN branch's
|
|
own complete control vectors, and both results must pass that separate audit.
|
|
Neither choice is described as the original H data layout.
|
|
|
|
All evaluation functions are CPU/pure: descriptors and raw arrays are checked,
|
|
but disk receipts, anatomical identities, seed derivation, actual execution,
|
|
registration authority and full numerical/integrity audits remain EXTERNAL.
|
|
A supplied SHA or Boolean is not proof of any of those facts. Consequently no
|
|
return value claims scientific learning, execution approval or a candidate.
|
|
The only file read is the pinned Python dependency at module import time.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import json
|
|
from collections.abc import Mapping
|
|
from pathlib import Path
|
|
import types
|
|
|
|
import numpy as np
|
|
|
|
|
|
METRICS_SHA256 = '47319e21db3c8b030b1bafbf0703cd86fb588d23bb29436accbacdba73d08c3c'
|
|
_path = Path(__file__).resolve().parent.parent / 'olfactory-conditioning-lab-v1' / 'metrics.py'
|
|
_source = _path.read_bytes()
|
|
if hashlib.sha256(_source).hexdigest() != METRICS_SHA256:
|
|
raise ValueError('Frozen metrics source SHA256 differs')
|
|
M = types.ModuleType('conditioning_execution_frozen_metrics')
|
|
M.__file__ = str(_path)
|
|
exec(compile(_source, str(_path), 'exec'), M.__dict__)
|
|
del _source
|
|
|
|
SCHEMA = 'conditioning-learning-gates-v1'
|
|
REGISTRY_SCHEMA = 'conditioning-gate-registry-descriptor-v1'
|
|
AUTHORITY_SCHEMA = 'conditioning-data-authority-descriptor-v1'
|
|
PAIR_SCHEMA = 'conditioning-probe-pair-descriptor-v1'
|
|
DOWNSTREAM_POLICY = 'nonMBONDownstream-pre-outcome-operationalization-v1'
|
|
H_MAPPING = 'each-DAN-control-source-separately-both-pass-v1'
|
|
ASSAYS = ('normal', 'delayed', 'reversal', 'retention')
|
|
DAN_COMPARTMENTS = {'PAM15': "y5B'2a", 'PPL103': "y2a'1"}
|
|
CARRY = ('v', 'g', 'ref', 'spike_ring', 'apl_state', 'apl_pending', 'absolute_tick', 'delta', 'relevant_rng_state')
|
|
EXTERNAL = ('registration/source authority', 'anatomical membership', 'full seed derivation and alias inventory',
|
|
'actual input/receipt files and completed trajectories', 'continuous state/queue/RNG restoration',
|
|
'full finite/integrity/resource audits')
|
|
|
|
|
|
def require(ok, message):
|
|
if not ok:
|
|
raise ValueError(message)
|
|
|
|
|
|
def _keys(value, fields, label):
|
|
require(isinstance(value, Mapping) and set(value) == set(fields), 'Wrong ' + label + ' fields')
|
|
|
|
|
|
def _sha(value, label):
|
|
require(M._sha(value), 'Invalid SHA256: ' + label)
|
|
return value
|
|
|
|
|
|
def _token(value, label):
|
|
return M._token(value, label)
|
|
|
|
|
|
def descriptor_sha256(value):
|
|
"""Canonical descriptor hash; this does NOT establish external authority."""
|
|
return hashlib.sha256(json.dumps(value, sort_keys=True, separators=(',', ':'),
|
|
ensure_ascii=False, allow_nan=False).encode('utf-8')).hexdigest()
|
|
|
|
|
|
def _scope():
|
|
return dict(external_authority_verified=False, scientific_learning_pass_claimed=False,
|
|
candidate_generated=False, execution_authorized=False, data_backed_claimed=False,
|
|
external_requirements=list(EXTERNAL), frozen_metrics_source_sha256=METRICS_SHA256)
|
|
|
|
|
|
def validate_registry(registry, *, expected_sha256):
|
|
"""Check all 40 declared blocks and actual 63-bit seeds, never just labels.
|
|
|
|
training_sources lists independent training/design source streams, excluding
|
|
counterfactual aliases. The external registrar must prove that this list is
|
|
complete and agrees with the frozen full trial/seed inventory. Evaluation is
|
|
one original A/B input pair per block, reused within that block across assays.
|
|
"""
|
|
_keys(registry, ('schema', 'protocol_sha256', 'population_binding_sha256',
|
|
'nodes_raw_sha256', 'population_membership_sha256', 'blocks'), 'registry')
|
|
require(registry['schema'] == REGISTRY_SCHEMA, 'Wrong registry schema')
|
|
require(descriptor_sha256(registry) == _sha(expected_sha256, 'external registry'), 'Registry descriptor changed')
|
|
for field in ('protocol_sha256', 'population_binding_sha256', 'nodes_raw_sha256', 'population_membership_sha256'):
|
|
_sha(registry[field], field)
|
|
require(isinstance(registry['blocks'], Mapping) and len(registry['blocks']) == 40, 'Exactly 40 registered blocks required')
|
|
identities, streams, seeds = set(), set(), set()
|
|
for block_id, block in registry['blocks'].items():
|
|
_token(block_id, 'block_id')
|
|
_keys(block, ('mode', 'dan_type', 'compartment_verbatim', 'conditioned_cue', 'replicate',
|
|
'training_sources', 'evaluation_sources', 'evaluation_input_sha256'), 'block')
|
|
require(block['mode'] in M.MODES and block['dan_type'] in DAN_COMPARTMENTS and
|
|
block['compartment_verbatim'] == DAN_COMPARTMENTS[block['dan_type']] and
|
|
block['conditioned_cue'] in M.CUES and type(block['replicate']) is int and 1 <= block['replicate'] <= 5,
|
|
'Unknown cell/compartment/replicate')
|
|
identity = (block['mode'], block['dan_type'], block['conditioned_cue'], block['replicate'])
|
|
require(identity not in identities, 'Duplicate cell/replicate')
|
|
identities.add(identity)
|
|
require(type(block['training_sources']) is list and block['training_sources'], 'Missing independent training/design sources')
|
|
_keys(block['evaluation_sources'], M.CUES, 'evaluation sources')
|
|
_keys(block['evaluation_input_sha256'], M.CUES, 'evaluation input hashes')
|
|
for cue in M.CUES:
|
|
_sha(block['evaluation_input_sha256'][cue], 'evaluation input')
|
|
entries = [(x, ('training', 'design')) for x in block['training_sources']]
|
|
entries += [(block['evaluation_sources'][cue], ('evaluation',)) for cue in M.CUES]
|
|
for source, allowed in entries:
|
|
_keys(source, ('stream_id', 'seed', 'split'), 'independent source')
|
|
_token(source['stream_id'], 'stream_id')
|
|
require(source['split'] in allowed and type(source['seed']) is int and 0 <= source['seed'] < 2**63,
|
|
'Wrong source split or full63bit seed')
|
|
require(source['stream_id'] not in streams and source['seed'] not in seeds,
|
|
'Independent source ID/seed collision, including training/evaluation overlap')
|
|
streams.add(source['stream_id']); seeds.add(source['seed'])
|
|
require(len(identities) == 40, 'Incomplete eight-cell/five-replicate inventory')
|
|
return dict(schema=SCHEMA, registry_sha256=expected_sha256, blocks=40,
|
|
independent_declared_streams=len(streams), **_scope())
|
|
|
|
|
|
def _context(registry, authority, nodes=None, populations=None):
|
|
_keys(authority, ('schema', 'registry_sha256', 'verification_receipt_sha256',
|
|
'execution_inventory_sha256', 'population_binding_sha256'), 'external data authority descriptor')
|
|
require(authority['schema'] == AUTHORITY_SCHEMA, 'Wrong data authority schema')
|
|
for field in ('registry_sha256', 'verification_receipt_sha256', 'execution_inventory_sha256', 'population_binding_sha256'):
|
|
_sha(authority[field], field)
|
|
validate_registry(registry, expected_sha256=authority['registry_sha256'])
|
|
require(authority['population_binding_sha256'] == registry['population_binding_sha256'], 'Population authority binding differs')
|
|
if nodes is not None:
|
|
membership = M.validate_populations(nodes, populations)
|
|
require(M._hash(nodes) == registry['nodes_raw_sha256'] and
|
|
membership['membership_sha256'] == registry['population_membership_sha256'], 'Raw population binding differs')
|
|
|
|
|
|
def _cell_rows(rows, registry, mode, dan_type, conditioned_cue):
|
|
require(mode in M.MODES and dan_type in DAN_COMPARTMENTS and conditioned_cue in M.CUES, 'Unknown cell')
|
|
require(type(rows) is list and len(rows) == 5, 'Exactly five independent training rows required')
|
|
expected = {key for key, value in registry['blocks'].items()
|
|
if (value['mode'], value['dan_type'], value['conditioned_cue']) == (mode, dan_type, conditioned_cue)}
|
|
require(len(expected) == 5 and {row['replicate_id'] for row in rows} == expected, 'Missing/duplicate/wrong-cell training rows')
|
|
for row in rows:
|
|
block = registry['blocks'][row['replicate_id']]
|
|
require(row['replicate_unit'] == 'independent_training', 'Evaluation draws are not training replicates')
|
|
require(row['training_stream_ids'] == [x['stream_id'] for x in block['training_sources']], 'Training source lineage differs')
|
|
require(row['evaluation_stream_ids'] == [block['evaluation_sources'][cue]['stream_id'] for cue in M.CUES],
|
|
'Both registered evaluation inputs required; no extra evaluation replication')
|
|
return expected
|
|
|
|
|
|
def _pair(pair, block_id, registry, nodes, populations, used, *, policy, delta=None, checkpoint=None):
|
|
fields = ('schema', 'block_id', 'snapshot_id', 'receipt_sha256', 'probe_receipt_sha256',
|
|
'evaluation_input_sha256', 'state_raw_sha256', 'states', 'state_policy', 'source_checkpoint_sha256',
|
|
'delta_sha256', 'probe_steps', 'cs_start_tick', 'cs_stop_tick_exclusive', 'US_requested',
|
|
'weight_updates_enabled', 'feedback_to_training_or_idle')
|
|
_keys(pair, fields, 'probe pair')
|
|
require(pair['schema'] == PAIR_SCHEMA and pair['block_id'] == block_id and pair['state_policy'] == policy,
|
|
'Probe identity/state policy differs')
|
|
_token(pair['snapshot_id'], 'snapshot_id')
|
|
for field in ('receipt_sha256', 'source_checkpoint_sha256', 'delta_sha256'):
|
|
_sha(pair[field], field)
|
|
require(type(pair['probe_steps']) is int and pair['probe_steps'] == 1200 and
|
|
type(pair['cs_start_tick']) is int and pair['cs_start_tick'] == 0 and
|
|
type(pair['cs_stop_tick_exclusive']) is int and pair['cs_stop_tick_exclusive'] == 1000 and
|
|
pair['US_requested'] is False and pair['weight_updates_enabled'] is False and
|
|
pair['feedback_to_training_or_idle'] is False, 'Probe timing/US/update/feedback differs')
|
|
require(delta is None or pair['delta_sha256'] == delta, 'Probe delta source differs')
|
|
require(checkpoint is None or pair['source_checkpoint_sha256'] == checkpoint, 'Probe checkpoint source differs')
|
|
for field in ('probe_receipt_sha256', 'evaluation_input_sha256', 'state_raw_sha256', 'states'):
|
|
_keys(pair[field], M.CUES, field)
|
|
require(pair['evaluation_input_sha256'] == registry['blocks'][block_id]['evaluation_input_sha256'],
|
|
'Probe must use the exact registered evaluation inputs')
|
|
for cue in M.CUES:
|
|
_sha(pair['probe_receipt_sha256'][cue], 'probe receipt')
|
|
checked = M.validate_state(pair['states'][cue], nodes=nodes, steps=1200)
|
|
require(pair['state_raw_sha256'][cue] == checked['hashes'], 'Probe raw arrays differ from descriptor hashes')
|
|
# Re-referencing one baseline is permitted, but a new snapshot cannot claim
|
|
# that it executed a probe by reusing another snapshot's receipt.
|
|
fingerprint = descriptor_sha256({key: value for key, value in pair.items() if key != 'states'})
|
|
identity = pair['snapshot_id']
|
|
require(identity not in used['snapshots'] or used['snapshots'][identity] == fingerprint, 'Snapshot identity reused with changed data')
|
|
for receipt in (pair['receipt_sha256'], *pair['probe_receipt_sha256'].values()):
|
|
require(receipt not in used['receipts'] or used['receipts'][receipt] == identity, 'Probe execution receipt reused across snapshots')
|
|
used['receipts'][receipt] = identity
|
|
require(len({pair['receipt_sha256'], *pair['probe_receipt_sha256'].values()}) == 3, 'A/B probes require distinct actual receipts')
|
|
used['snapshots'][identity] = fingerprint
|
|
return pair['states']
|
|
|
|
|
|
def _arm_execution(value, *, assay, arm, used):
|
|
_keys(value, ('receipt_sha256', 'recording_verified', 'complete', 'completed_epochs',
|
|
'reinforcement_opportunities', 'terminal_pairings', 'epoch_duration_ms', 'cs_duration_ms', 'us_tick',
|
|
'initial_delta_sha256', 'terminal_delta_sha256', 'terminal_checkpoint_sha256',
|
|
'neural_state_policy', 'weight_rule', 'reinforced_cue'), 'training execution descriptor')
|
|
for key in ('receipt_sha256', 'initial_delta_sha256', 'terminal_delta_sha256', 'terminal_checkpoint_sha256'):
|
|
_sha(value[key], key)
|
|
require(value['receipt_sha256'] not in used, 'Training receipt reused across arms/replicates')
|
|
used.add(value['receipt_sha256'])
|
|
require(value['complete'] is True and value['recording_verified'] is True,
|
|
'Caller must supply a completed, externally verified execution descriptor')
|
|
for field, expected in (('completed_epochs', 150 if arm == 'unpaired' else 100),
|
|
('reinforcement_opportunities', 0 if arm == 'CS-only' else 50), ('terminal_pairings', 50),
|
|
('us_tick', 1500 if assay == 'delayed' else 900)):
|
|
require(type(value[field]) is int and value[field] == expected, 'Wrong terminal inventory: ' + field)
|
|
require(value['epoch_duration_ms'] == (180. if assay == 'delayed' else 120.) and value['cs_duration_ms'] == 100. and
|
|
value['neural_state_policy'] == 'fresh_epoch_delta_only' and value['weight_rule'] == 'frozen_temporal_eligibility_v2',
|
|
'Frozen training timing/state/update policy differs')
|
|
|
|
|
|
def _report(assay, rows, registry, authority, mode, dan_type, conditioned_cue):
|
|
rows = sorted(rows, key=lambda row: row['replicate_id'])
|
|
count = sum(row['conjunction_pass'] for row in rows)
|
|
return dict(schema=SCHEMA, status='CONDITIONAL-PREDICATES-PASS' if count >= 3 else 'CONDITIONAL-PREDICATES-FAIL',
|
|
assay=assay, mode=mode, dan_type=dan_type, conditioned_cue=conditioned_cue,
|
|
predicate_pass=count >= 3, successful_same_replicate_conjunctions=count, required_majority=3,
|
|
training_replicates=5, replicates=rows, registry_sha256=authority['registry_sha256'],
|
|
external_data_authority_descriptor_sha256=descriptor_sha256(authority),
|
|
downstream_policy=DOWNSTREAM_POLICY, policy_scope='pre-outcome additional operationalization', **_scope())
|
|
|
|
|
|
def evaluate_acquisition(replicates, *, assay, mode, dan_type, conditioned_cue, registry, authority):
|
|
"""Reuse the pinned normal/delayed gate, preserving every six-control rule.
|
|
|
|
replicates has the original frozen metrics schema. recording_verified is the
|
|
external caller's descriptor assertion, NOT a verification done by this call.
|
|
"""
|
|
_context(registry, authority)
|
|
_cell_rows(replicates, registry, mode, dan_type, conditioned_cue)
|
|
registered = {row['replicate_id']: row['training_stream_ids'] for row in replicates}
|
|
for row in replicates:
|
|
for arm in row['arms'].values():
|
|
for cue in M.CUES:
|
|
summary = arm['metrics']['cues'][cue]
|
|
require(summary['nodes_raw_sha256'] == registry['nodes_raw_sha256'] and
|
|
summary['membership']['membership_sha256'] == registry['population_membership_sha256'], 'Population metric binding differs')
|
|
frozen = M.evaluate_acquisition(replicates, assay=assay, mode=mode, dan_type=dan_type,
|
|
conditioned_cue=conditioned_cue, registered_training_streams=registered)
|
|
result = _report(assay, frozen['replicates'], registry, authority, mode, dan_type, conditioned_cue)
|
|
result.update(frozen_acquisition_result=frozen, acquisition_controls=list(M.CONTROLS),
|
|
downstream_policy='unchanged acquisition requires both legacyDownstream and nonMBONDownstream positive gains',
|
|
acquisition_metric_inputs=json.loads(json.dumps(replicates, allow_nan=False)),
|
|
legacy_control_MBON_gains={name: {row['replicate_id']: row['arms'][name]['metrics']['separation']['MBON']['gain']
|
|
for row in replicates} for name in ('D=0', 'random reward', 'frozen')})
|
|
return result
|
|
|
|
|
|
def evaluate_reversal(replicates, *, mode, dan_type, conditioned_cue, registry, authority,
|
|
nodes, populations, downstream_policy):
|
|
"""Original pre/orientation, strict nonzero sign crossing, fixed 50 count."""
|
|
require(downstream_policy == DOWNSTREAM_POLICY, 'Explicit preregistered downstream operationalization required')
|
|
_context(registry, authority, nodes, populations)
|
|
_cell_rows(replicates, registry, mode, dan_type, conditioned_cue)
|
|
reports, execution_used = [], set()
|
|
used = dict(snapshots={}, receipts={})
|
|
neutral = next(cue for cue in M.CUES if cue != conditioned_cue)
|
|
for row in replicates:
|
|
_keys(row, ('replicate_id', 'replicate_unit', 'training_stream_ids', 'evaluation_stream_ids',
|
|
'pre', 'acquisition', 'reversal', 'no_reversal', 'execution'), 'reversal row')
|
|
block_id = row['replicate_id']
|
|
_keys(row['execution'], ('acquisition', 'reversal', 'no_reversal'), 'reversal executions')
|
|
for arm, execution in row['execution'].items():
|
|
_arm_execution(execution, assay='normal', arm=arm, used=execution_used)
|
|
require(execution['reinforced_cue'] == (neutral if arm == 'reversal' else conditioned_cue), 'Reversal cue assignment differs')
|
|
acq = row['execution']['acquisition']
|
|
for arm in ('reversal', 'no_reversal'):
|
|
require(row['execution'][arm]['initial_delta_sha256'] == acq['terminal_delta_sha256'], 'Reversal must fork exact acquisition delta')
|
|
pre = _pair(row['pre'], block_id, registry, nodes, populations, used, policy='common_frozen_reset', delta=acq['initial_delta_sha256'])
|
|
metrics, states = {}, {}
|
|
for arm in ('acquisition', 'reversal', 'no_reversal'):
|
|
execution = row['execution'][arm]
|
|
states[arm] = _pair(row[arm], block_id, registry, nodes, populations, used, policy='common_frozen_reset',
|
|
delta=execution['terminal_delta_sha256'], checkpoint=execution['terminal_checkpoint_sha256'])
|
|
metrics[arm] = M.arm_metrics(pre, states[arm], conditioned_cue=conditioned_cue, nodes=nodes, populations=populations)
|
|
changes = {cue: M.functional_summary(states['acquisition'][cue], states['reversal'][cue],
|
|
nodes=nodes, populations=populations, steps=1200) for cue in M.CUES}
|
|
s = {arm: metric['rawS'] for arm, metric in metrics.items()}
|
|
expected = (s['acquisition'] > 0 and s['no_reversal'] > 0 and s['reversal'] < 0) if dan_type == 'PAM15' else (
|
|
s['acquisition'] < 0 and s['no_reversal'] < 0 and s['reversal'] > 0)
|
|
mbon, down = (changes[neutral]['populations'][name] for name in ('MBON', 'nonMBONDownstream'))
|
|
predicates = dict(strict_three_signed_relations=bool(expected),
|
|
former_neutral_MBON_count_or_defined_first_latency=bool(mbon['spike_count_l1'] > 0 or mbon['defined_first_latency_changed']),
|
|
former_neutral_nonMBONDownstream_event_or_count=bool(down['events_changed'] or down['spike_count_l1'] > 0))
|
|
reports.append(dict(replicate_id=block_id, rawS=s, predicates=predicates, conjunction_pass=all(predicates.values()),
|
|
metrics=metrics, acquisition_to_reversal_changes_by_cue=changes,
|
|
legacy_style_sign_inequality_on_registered_S=bool(np.sign(s['acquisition']) != np.sign(s['reversal'])),
|
|
exact_historical_H_readout_equivalence_claimed=False))
|
|
return _report('reversal', reports, registry, authority, mode, dan_type, conditioned_cue)
|
|
|
|
|
|
def _idle(branch, used, *, arm, conditioned_cue):
|
|
_keys(branch, ('source_checkpoint_sha256', 'delta_sha256', 'idle_lineage_id', 'idle_receipt_sha256',
|
|
'total_idle_ticks', 'no_input', 'updates_disabled', 'reset_state', 'analytic_time_advance',
|
|
'one_second_is_prefix_of_ten_second', 'checkpoints', 'source_execution'), 'idle branch')
|
|
_arm_execution(branch['source_execution'], assay='normal', arm=arm, used=used)
|
|
source = branch['source_execution']
|
|
require(source['terminal_checkpoint_sha256'] == branch['source_checkpoint_sha256'] and
|
|
source['terminal_delta_sha256'] == branch['delta_sha256'] and source['reinforced_cue'] == conditioned_cue,
|
|
'Idle source must bind the complete terminal normal paired/frozen execution')
|
|
require(arm != 'frozen' or source['initial_delta_sha256'] == source['terminal_delta_sha256'],
|
|
'Frozen training cannot change delta')
|
|
for field in ('source_checkpoint_sha256', 'delta_sha256', 'idle_receipt_sha256'):
|
|
_sha(branch[field], field)
|
|
_token(branch['idle_lineage_id'], 'idle lineage')
|
|
require(branch['idle_receipt_sha256'] not in used and branch['idle_lineage_id'] not in used, 'Idle branch/receipt reused')
|
|
used.update((branch['idle_receipt_sha256'], branch['idle_lineage_id']))
|
|
require(type(branch['total_idle_ticks']) is int and branch['total_idle_ticks'] == 100000 and
|
|
branch['no_input'] is True and branch['updates_disabled'] is True and branch['reset_state'] is False and
|
|
branch['analytic_time_advance'] is False and branch['one_second_is_prefix_of_ten_second'] is True,
|
|
'Retention must be actual continuous 10-second idle with a 1-second prefix')
|
|
_keys(branch['checkpoints'], ('0', '1', '10'), 'idle checkpoints')
|
|
previous, start = None, None
|
|
for second in (0, 1, 10):
|
|
checkpoint = branch['checkpoints'][str(second)]
|
|
_keys(checkpoint, ('checkpoint_sha256', 'parent_checkpoint_sha256', 'elapsed_ticks',
|
|
'absolute_tick', 'delta_sha256', 'carry_sha256'), 'idle checkpoint')
|
|
for field in ('checkpoint_sha256', 'parent_checkpoint_sha256', 'delta_sha256'):
|
|
_sha(checkpoint[field], field)
|
|
require(type(checkpoint['elapsed_ticks']) is int and checkpoint['elapsed_ticks'] == second * 10000 and
|
|
type(checkpoint['absolute_tick']) is int and checkpoint['absolute_tick'] >= 0,
|
|
'Retention clock differs')
|
|
start = checkpoint['absolute_tick'] if second == 0 else start
|
|
require(checkpoint['absolute_tick'] == start + second * 10000 and checkpoint['delta_sha256'] == branch['delta_sha256'],
|
|
'Absolute clock or idle delta changed')
|
|
require(checkpoint['parent_checkpoint_sha256'] == (branch['source_checkpoint_sha256'] if previous is None else previous),
|
|
'Idle checkpoint chain does not preserve the same 1-to-10-second trajectory')
|
|
_keys(checkpoint['carry_sha256'], CARRY, 'complete carried-state hashes')
|
|
for field, value in checkpoint['carry_sha256'].items():
|
|
_sha(value, field)
|
|
previous = checkpoint['checkpoint_sha256']
|
|
|
|
|
|
def evaluate_retention(replicates, *, mode, dan_type, conditioned_cue, registry, authority,
|
|
nodes, populations, downstream_policy):
|
|
"""One conjunction per training replicate at BOTH 1 and 10 real seconds."""
|
|
require(downstream_policy == DOWNSTREAM_POLICY, 'Explicit preregistered downstream operationalization required')
|
|
_context(registry, authority, nodes, populations)
|
|
_cell_rows(replicates, registry, mode, dan_type, conditioned_cue)
|
|
reports, idle_used = [], set()
|
|
used = dict(snapshots={}, receipts={})
|
|
for row in replicates:
|
|
_keys(row, ('replicate_id', 'replicate_unit', 'training_stream_ids', 'evaluation_stream_ids',
|
|
'pre', 'checkpoints', 'execution'), 'retention row')
|
|
_keys(row['pre'], ('trained', 'frozen'), 'retention pre')
|
|
_keys(row['execution'], ('trained', 'frozen'), 'retention branches')
|
|
_keys(row['checkpoints'], ('0', '1', '10'), 'retention time inventory')
|
|
block_id = row['replicate_id']
|
|
pre = {}
|
|
for branch in ('trained', 'frozen'):
|
|
execution = row['execution'][branch]
|
|
_idle(execution, idle_used, arm='paired' if branch == 'trained' else 'frozen', conditioned_cue=conditioned_cue)
|
|
pre[branch] = _pair(row['pre'][branch], block_id, registry, nodes, populations, used, policy='common_frozen_reset',
|
|
delta=execution['source_execution']['initial_delta_sha256'])
|
|
times, complete = {}, []
|
|
for second in (0, 1, 10):
|
|
pairs = row['checkpoints'][str(second)]
|
|
_keys(pairs, ('trained', 'frozen'), 'time-matched trained/frozen probes')
|
|
states, metrics = {}, {}
|
|
for branch in ('trained', 'frozen'):
|
|
execution = row['execution'][branch]
|
|
checkpoint = execution['checkpoints'][str(second)]
|
|
states[branch] = _pair(pairs[branch], block_id, registry, nodes, populations, used,
|
|
policy='continuous_checkpoint', delta=execution['delta_sha256'], checkpoint=checkpoint['checkpoint_sha256'])
|
|
metrics[branch] = M.arm_metrics(pre[branch], states[branch], conditioned_cue=conditioned_cue, nodes=nodes, populations=populations)
|
|
differences = {cue: M.functional_summary(states['frozen'][cue], states['trained'][cue],
|
|
nodes=nodes, populations=populations, steps=1200) for cue in M.CUES}
|
|
trained, frozen = (metrics[branch]['rawS'] for branch in ('trained', 'frozen'))
|
|
mbon, down = (differences[conditioned_cue]['populations'][name] for name in ('MBON', 'nonMBONDownstream'))
|
|
predicates = dict(raw_signed_direction=bool(trained > 0 if dan_type == 'PAM15' else trained < 0),
|
|
beyond_time_matched_frozen=bool(trained > frozen if dan_type == 'PAM15' else trained < frozen),
|
|
conditioned_MBON_count_or_defined_first_latency=bool(mbon['spike_count_l1'] > 0 or mbon['defined_first_latency_changed']),
|
|
conditioned_nonMBONDownstream_event_or_count=bool(down['events_changed'] or down['spike_count_l1'] > 0))
|
|
times[str(second)] = dict(rawS_trained=trained, rawS_frozen=frozen, rawS_trained_minus_frozen=trained - frozen,
|
|
metrics=metrics, frozen_to_trained_differences_by_cue=differences, predicates=predicates,
|
|
contributes_to_gate=second in (1, 10), conjunction_pass=all(predicates.values()))
|
|
if second in (1, 10):
|
|
complete.append(all(predicates.values()))
|
|
reports.append(dict(replicate_id=block_id, times=times, conjunction_pass=all(complete),
|
|
legacy_style_last_MBON_separation_exceeds_pre=bool(times['10']['metrics']['trained']['separation']['MBON']['gain'] > 0),
|
|
legacy_reset_retention_equivalence_claimed=False))
|
|
return _report('retention', reports, registry, authority, mode, dan_type, conditioned_cue)
|
|
|
|
|
|
def _checked_difference(summary, before, after, cue):
|
|
"""Rebind reported differences to raw vectors in their two source metrics.
|
|
|
|
The event-change bit comes from raw-event comparison in evaluate_*; a summary
|
|
does not contain the event files. It remains externally bound evidence, never
|
|
an independent disk/event verification performed by this cohort function.
|
|
"""
|
|
left, right = before['cues'][cue], after['cues'][cue]
|
|
require(summary['membership'] == left['membership'] == right['membership'] and
|
|
summary['nodes_raw_sha256'] == left['nodes_raw_sha256'] == right['nodes_raw_sha256'] and
|
|
summary['pre_state_raw_sha256'] == left['post_state_raw_sha256'] and
|
|
summary['post_state_raw_sha256'] == right['post_state_raw_sha256'], 'Difference source binding changed')
|
|
for name in M.POPULATIONS:
|
|
value, a, b = summary['populations'][name], left['populations'][name], right['populations'][name]
|
|
for field in ('counts', 'first_latency_ms_per_neuron', 'final_v', 'final_g', 'final_ref'):
|
|
require(value['pre_' + field] == a['post_' + field] and value['post_' + field] == b['post_' + field],
|
|
'Difference population vectors changed')
|
|
count = sum(abs(x - y) for x, y in zip(value['pre_counts'], value['post_counts']))
|
|
first = []
|
|
for phase in ('pre', 'post'):
|
|
defined = [x for x in value[phase + '_first_latency_ms_per_neuron'] if x is not None]
|
|
first.append(min(defined) if defined else None)
|
|
changed = first[0] is not None and first[1] is not None and first[0] != first[1]
|
|
require(type(value['spike_count_l1']) is int and value['spike_count_l1'] == count and
|
|
value['defined_first_latency_changed'] is changed and type(value['events_changed']) is bool,
|
|
'Difference predicate contradicts its raw count/defined-latency vectors')
|
|
require(count == 0 or value['events_changed'], 'Changed counts require changed events')
|
|
same_events = all(summary['pre_state_raw_sha256'][key] == summary['post_state_raw_sha256'][key]
|
|
for key in ('step', 'neuron_idx'))
|
|
require(not same_events or not value['events_changed'], 'Identical complete event arrays cannot change population events')
|
|
return summary['populations']
|
|
|
|
|
|
def _validate_followup_report(row, assay, dan, cue):
|
|
neutral = next(x for x in M.CUES if x != cue)
|
|
if assay == 'reversal':
|
|
_keys(row['metrics'], ('acquisition', 'reversal', 'no_reversal'), 'reversal report metrics')
|
|
metrics = {name: M._bound_metric(value, cue) for name, value in row['metrics'].items()}
|
|
for odor in M.CUES:
|
|
require(len({tuple(sorted(metric['cues'][odor]['pre_state_raw_sha256'].items())) for metric in metrics.values()}) == 1,
|
|
'Reversal reports changed original pre baseline')
|
|
s = {name: value['rawS'] for name, value in metrics.items()}
|
|
require(row['rawS'] == s, 'Reversal raw signed result differs from source metrics')
|
|
_keys(row['acquisition_to_reversal_changes_by_cue'], M.CUES, 'reversal cue changes')
|
|
groups = {odor: _checked_difference(row['acquisition_to_reversal_changes_by_cue'][odor],
|
|
metrics['acquisition'], metrics['reversal'], odor) for odor in M.CUES}
|
|
mbon, down = (groups[neutral][name] for name in ('MBON', 'nonMBONDownstream'))
|
|
signed = (s['acquisition'] > 0 and s['no_reversal'] > 0 and s['reversal'] < 0) if dan == 'PAM15' else (
|
|
s['acquisition'] < 0 and s['no_reversal'] < 0 and s['reversal'] > 0)
|
|
predicates = dict(strict_three_signed_relations=bool(signed),
|
|
former_neutral_MBON_count_or_defined_first_latency=bool(mbon['spike_count_l1'] > 0 or mbon['defined_first_latency_changed']),
|
|
former_neutral_nonMBONDownstream_event_or_count=bool(down['events_changed'] or down['spike_count_l1'] > 0))
|
|
require(row['predicates'] == predicates and all(type(x) is bool for x in row['predicates'].values()),
|
|
'Reversal predicates differ from source metrics')
|
|
return all(predicates.values())
|
|
_keys(row['times'], ('0', '1', '10'), 'retention report times')
|
|
baselines, conjunctions = {}, []
|
|
for second in ('0', '1', '10'):
|
|
value = row['times'][second]
|
|
_keys(value['metrics'], ('trained', 'frozen'), 'retention report metrics')
|
|
metrics = {name: M._bound_metric(metric, cue) for name, metric in value['metrics'].items()}
|
|
for branch, metric in metrics.items():
|
|
baseline = {odor: metric['cues'][odor]['pre_state_raw_sha256'] for odor in M.CUES}
|
|
require(branch not in baselines or baselines[branch] == baseline, 'Retention changed original pre baseline across time')
|
|
baselines[branch] = baseline
|
|
trained, frozen = (metrics[branch]['rawS'] for branch in ('trained', 'frozen'))
|
|
require(value['rawS_trained'] == trained and value['rawS_frozen'] == frozen and
|
|
value['rawS_trained_minus_frozen'] == trained - frozen, 'Retention signed contrast differs from source metrics')
|
|
_keys(value['frozen_to_trained_differences_by_cue'], M.CUES, 'retention cue differences')
|
|
groups = {odor: _checked_difference(value['frozen_to_trained_differences_by_cue'][odor],
|
|
metrics['frozen'], metrics['trained'], odor) for odor in M.CUES}
|
|
mbon, down = (groups[cue][name] for name in ('MBON', 'nonMBONDownstream'))
|
|
predicates = dict(raw_signed_direction=bool(trained > 0 if dan == 'PAM15' else trained < 0),
|
|
beyond_time_matched_frozen=bool(trained > frozen if dan == 'PAM15' else trained < frozen),
|
|
conditioned_MBON_count_or_defined_first_latency=bool(mbon['spike_count_l1'] > 0 or mbon['defined_first_latency_changed']),
|
|
conditioned_nonMBONDownstream_event_or_count=bool(down['events_changed'] or down['spike_count_l1'] > 0))
|
|
require(value['predicates'] == predicates and all(type(x) is bool for x in value['predicates'].values()) and
|
|
value['conjunction_pass'] is all(predicates.values()) and value['contributes_to_gate'] is (second != '0'),
|
|
'Retention predicates differ from source metrics')
|
|
if second != '0':
|
|
conjunctions.append(all(predicates.values()))
|
|
return all(conjunctions)
|
|
|
|
|
|
def summarize_cohort(reports, *, registry, authority, required_assays):
|
|
"""Exact eight-cell inventory; each assay keeps its own same-replicate 3/5.
|
|
|
|
Use ('normal',) for the staged acquisition decision or ASSAYS for the full
|
|
functional table. The function does not silently intersect successful seeds
|
|
across assays, pool cells, or incorporate legacy H/integrity gates. Those are
|
|
separately registered coordinator decisions. Missing assays remain NOT-RUN.
|
|
"""
|
|
_context(registry, authority)
|
|
require(tuple(required_assays) in (('normal',), ASSAYS), 'Only staged normal or full fixed assay scope supported')
|
|
expected = {(m, d, c, a) for m in M.MODES for d in M.DAN_TYPES for c in M.CUES for a in required_assays}
|
|
actual, table = set(), []
|
|
for report in reports:
|
|
require(report.get('schema') == SCHEMA and report.get('registry_sha256') == authority['registry_sha256'] and
|
|
report.get('external_data_authority_descriptor_sha256') == descriptor_sha256(authority), 'Report authority binding differs')
|
|
identity = (report['mode'], report['dan_type'], report['conditioned_cue'], report['assay'])
|
|
require(identity in expected and identity not in actual, 'Duplicate/unexpected cohort cell/assay')
|
|
expected_ids = {key for key, value in registry['blocks'].items()
|
|
if (value['mode'], value['dan_type'], value['conditioned_cue']) == identity[:3]}
|
|
rows = report['replicates']
|
|
require(len(rows) == 5 and {row['replicate_id'] for row in rows} == expected_ids and
|
|
all(type(row['conjunction_pass']) is bool for row in rows), 'Cohort replicate coverage differs')
|
|
if report['assay'] in ('normal', 'delayed'):
|
|
inputs = report.get('acquisition_metric_inputs')
|
|
require(isinstance(inputs, list), 'Missing original normal/delayed metric inputs')
|
|
rebuilt = evaluate_acquisition(inputs, assay=identity[3], mode=identity[0], dan_type=identity[1],
|
|
conditioned_cue=identity[2], registry=registry, authority=authority)
|
|
require(rebuilt == report,
|
|
'Normal/delayed report differs from frozen gate reconstruction')
|
|
else:
|
|
for row in rows:
|
|
require(report['downstream_policy'] == DOWNSTREAM_POLICY, 'Follow-up downstream policy changed')
|
|
conjunction = _validate_followup_report(row, report['assay'], report['dan_type'], report['conditioned_cue'])
|
|
require(row['conjunction_pass'] is conjunction, 'Replicate conjunction differs from its predicates')
|
|
count = sum(row['conjunction_pass'] for row in rows)
|
|
require(report['successful_same_replicate_conjunctions'] == count and report['predicate_pass'] is (count >= 3),
|
|
'Cohort summary disagrees with replicate conjunctions')
|
|
require(all(report.get(key) is False for key in ('external_authority_verified', 'scientific_learning_pass_claimed',
|
|
'candidate_generated', 'execution_authorized', 'data_backed_claimed')), 'Conditional report contains an unsupported authority claim')
|
|
actual.add(identity)
|
|
table.append(dict(mode=identity[0], dan_type=identity[1], conditioned_cue=identity[2], assay=identity[3],
|
|
status='CONDITIONAL-PASS' if count >= 3 else 'CONDITIONAL-FAIL', successful_replicates=count))
|
|
for identity in sorted(expected - actual):
|
|
table.append(dict(mode=identity[0], dan_type=identity[1], conditioned_cue=identity[2], assay=identity[3], status='NOT-RUN', successful_replicates=None))
|
|
complete = actual == expected
|
|
return dict(schema=SCHEMA, status='COMPLETE' if complete else 'NOT-RUN', required_assays=list(required_assays),
|
|
complete_inventory=complete, all_functional_predicates_pass=(all(row['status'] == 'CONDITIONAL-PASS' for row in table) if complete else None),
|
|
table=sorted(table, key=lambda row: (row['mode'], row['dan_type'], row['conditioned_cue'], row['assay'])),
|
|
legacy_H_and_integrity_gates_included=False, **_scope())
|
|
|
|
|
|
def legacy_h_cohort_audit(normal_reports, *, registry, authority, declared_mapping):
|
|
"""Four groups, two frozen H calls each; no branch/control pooling.
|
|
|
|
Both stricter control-source mapping and both_pass aggregate are new explicit
|
|
pre-outcome operationalizations, NOT a reproduced complete original H gate.
|
|
"""
|
|
if declared_mapping is None:
|
|
return dict(schema=SCHEMA, status='NOT-TESTABLE', both_pass=None,
|
|
reason='Missing explicit pre-outcome control-source mapping', **_scope())
|
|
require(declared_mapping == H_MAPPING, 'Unknown declared H control-source mapping')
|
|
inventory = summarize_cohort(normal_reports, registry=registry, authority=authority, required_assays=('normal',))
|
|
if not inventory['complete_inventory']:
|
|
return dict(schema=SCHEMA, status='NOT-RUN', both_pass=None, reason='Complete eight normal cells required', **_scope())
|
|
lookup = {(r['mode'], r['dan_type'], r['conditioned_cue']): r for r in normal_reports}
|
|
groups = []
|
|
for mode in M.MODES:
|
|
for cue in M.CUES:
|
|
reports = {dan: lookup[(mode, dan, cue)] for dan in M.DAN_TYPES}
|
|
outcomes = {}
|
|
for source in M.DAN_TYPES:
|
|
# The normal report must retain all raw arm metrics; gains are
|
|
# taken from their validated source payload, not a selected mean.
|
|
gains = {}
|
|
for dan, label in (('PAM15', 'appetitive-conditioning'), ('PPL103', 'aversive-conditioning')):
|
|
gains[label] = [row['population_separation_gains']['MBON'] for row in reports[dan]['replicates']]
|
|
controls = reports[source].get('legacy_control_MBON_gains')
|
|
require(isinstance(controls, Mapping) and set(controls) == {'D=0', 'random reward', 'frozen'},
|
|
'Normal report lacks complete branch-bound legacy control gains')
|
|
for name, label in (('D=0', 'd-zero-control'), ('random reward', 'random-reward-control'), ('frozen', 'frozen-brain-control')):
|
|
require(set(controls[name]) == {row['replicate_id'] for row in reports[source]['replicates']}, 'Legacy control replicate mapping differs')
|
|
gains[label] = [controls[name][row['replicate_id']] for row in reports[source]['replicates']]
|
|
outcomes[source] = M.legacy_h_mean_control_audit(gains)
|
|
groups.append(dict(mode=mode, conditioned_cue=cue, control_sources=outcomes,
|
|
both_pass=all(value['predicate_pass'] for value in outcomes.values())))
|
|
return dict(schema=SCHEMA, status='CONDITIONAL-AUDIT', declared_mapping=declared_mapping, groups=groups,
|
|
both_pass=all(group['both_pass'] for group in groups), original_H_data_layout_reproduced=False,
|
|
new_stricter_control_source_mapping=True, **_scope())
|