Files
digital-drosophila/models/male-cns-lif/conditioning-execution-v1/learning_gates.py
T
2026-09-30 19:49:02 +08:00

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())