46 lines
3.1 KiB
Python
46 lines
3.1 KiB
Python
"""Post-calibration set intersection audit. Never selects a replacement policy."""
|
|
from pathlib import Path
|
|
from itertools import combinations
|
|
import json
|
|
import pandas as pd
|
|
|
|
root=Path('/data/digital-drosophila/experiments/phase4u-odor-generalization')
|
|
data=json.loads((root/'calibration/selection.json').read_text());grid=data['grid']
|
|
pre=json.loads((root/'protocol/preregistered.json').read_text())
|
|
names={p['odor_id']:p['odor_name'] for p in pre['panel']}
|
|
ids=[p['odor_id'] for p in grid[0]['odors']]
|
|
sets={oid:{i for i,z in enumerate(grid) if next(x for x in z['odors'] if x['odor_id']==oid)['pass']} for oid in ids}
|
|
assert all(sets.values()),'At least one odor has no individually passing grid point; different mechanism'
|
|
empty=[]
|
|
for size in range(2,len(ids)+1):
|
|
for subset in combinations(ids,size):
|
|
if not set.intersection(*(sets[o] for o in subset)):
|
|
empty.append({'odor_ids':list(subset),'odor_names':[names[o] for o in subset]})
|
|
if empty:break
|
|
coverage=[]
|
|
for z in grid:
|
|
coverage.append({'window_ms':z['window_ms'],'US_ms':z['US_ms'],'passing_odors':sum(x['pass'] for x in z['odors']),
|
|
'failing_odors':[names[x['odor_id']] for x in z['odors'] if not x['pass']]})
|
|
report={'scope':'Post-calibration descriptive intersections within the preregistered finite grid only.',
|
|
'candidate_grid_cells':len(grid),'max_passing_odors':max(x['passing_odors'] for x in coverage),
|
|
'all_six_intersection_count':len(set.intersection(*sets.values())),
|
|
'per_odor':[{'odor_id':o,'odor_name':names[o],'passing_count':len(sets[o]),
|
|
'passing_cells':[{'window_ms':grid[i]['window_ms'],'US_ms':grid[i]['US_ms']} for i in sorted(sets[o])]} for o in ids],
|
|
'minimum_empty_intersection_subset_size':size,'minimum_conflicts':empty,
|
|
'coverage':coverage,'new_parameter_selected':False,'new_odor_test_run':False,
|
|
'limits':['No claim of impossibility outside this finite grid, encoder and brain model.',
|
|
'No reward-triggering, per-odor timing, threshold or trace modifications performed.',
|
|
'Per-odor cells are descriptive, not approved odor-specific policies.']}
|
|
(root/'audit/coverage-intersections.json').write_text(json.dumps(report,indent=2)+'\n')
|
|
f=pd.DataFrame(coverage);matrix=f.pivot(index='window_ms',columns='US_ms',values='passing_odors')
|
|
lines=['# 校准覆盖交集审计','',
|
|
'每个单元格是通过的校准气味数(满分 6)。这是事后描述,不选择替代参数。','',
|
|
'| window / US ms | '+' | '.join(map(str,matrix.columns))+' |',
|
|
'|---|'+'---:|'*len(matrix.columns)]
|
|
for w,row in matrix.iterrows():lines.append('| '+str(w)+' | '+' | '.join(map(str,row.astype(int)))+' |')
|
|
lines+=['',f'最小无共同解子集包含 {size} 个气味:','']
|
|
for x in empty:lines.append('- '+' / '.join(x['odor_names']))
|
|
lines+=['','局限:仅对预注册有限网格成立。没有证明所有时序模型无解;也没有证明真实果蝇无法学习这些气味。']
|
|
(root/'audit/COVERAGE.md').write_text('\n'.join(lines)+'\n',encoding='utf-8')
|
|
print(json.dumps({k:v for k,v in report.items() if k!='coverage'},indent=2))
|