Archive MaleCNS research sources, provenance, bibliography and immutable data inventory
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
65004cfd7a8cc638a8c2176962a6deee344df530d16b9899f80272fbe5830288 phase4y-hypotheses.md
|
||||
636139eaeafa981a0c54c59a7c69e2b52b8493aa798448fa86ae6d2c6baba319 phase4y.py
|
||||
52f586ad0b4a12dbeb918f9453f9861a8dfc209118333eae5f46d27b7bce6011 phase4y.yaml
|
||||
c800c4f30767e8492853b311fb36f06f020b0aee73abcfb14561300cc9a87952 phase4y_describe.py
|
||||
53f8088d7572d5b63bc1ab623341bbab33dfdeeaf06421b775b2eaea1053ce0e manifest.json
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"bytes": 1899,
|
||||
"path": "phase4y-hypotheses.md",
|
||||
"sha256": "65004cfd7a8cc638a8c2176962a6deee344df530d16b9899f80272fbe5830288"
|
||||
},
|
||||
{
|
||||
"bytes": 13859,
|
||||
"path": "phase4y.py",
|
||||
"sha256": "636139eaeafa981a0c54c59a7c69e2b52b8493aa798448fa86ae6d2c6baba319"
|
||||
},
|
||||
{
|
||||
"bytes": 2107,
|
||||
"path": "phase4y.yaml",
|
||||
"sha256": "52f586ad0b4a12dbeb918f9453f9861a8dfc209118333eae5f46d27b7bce6011"
|
||||
},
|
||||
{
|
||||
"bytes": 9823,
|
||||
"path": "phase4y_describe.py",
|
||||
"sha256": "c800c4f30767e8492853b311fb36f06f020b0aee73abcfb14561300cc9a87952"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
# Independent DPM hypothesis branch
|
||||
|
||||
The user delegated selection after explicitly being asked about an independent physiological-hypothesis branch. The frozen reference remains read-only. No branch is automatically promoted.
|
||||
|
||||
Three preregistered conditions multiply only actual DPM outgoing effective currents by +1, 0 or -1. +1 is the frozen mathematical reference. 0 tests removal of the fast-current pathway, not removal of DPM neurons or all DPM functions. -1 is a sign-sensitivity experiment, not proof of GABA/serotonin receptor action on every target. No slow-receptor time constants, co-transmission model or dopamine concentration is invented.
|
||||
|
||||
The raw DPM bodies 11734 and 12569 have dopamine predictions and empty ground_truth, as verified in Phase4X. Prediction labels and the original sign array remain intact.
|
||||
|
||||
[Haynes et al., eLife 2015](https://pmc.ncbi.nlm.nih.gov/articles/PMC4305081/), doi:10.7554/eLife.03868, reports DPM GABA/5-HT signaling and inhibition in its tested preparation. It motivates the sign-sensitivity hypothesis but does not validate a global sign flip on these MaleCNS bodies. Full text was read during Phase4W; re-access during this phase returned a CAPTCHA. Prior literature and prediction caveats remain in the sealed Phase4W/4X notes.
|
||||
|
||||
The comparison asks whether an input dependency seen in isolated replay survives endogenous whole-brain feedback. It does not ask which condition looks most brain-like, has more activity or trains best. All results and failures are retained. There is no learning, candidate gate, reward sweep, extra stimulation or game interface.
|
||||
|
||||
Limitations: six previously studied odors, two fixed seeds (only one new), 1s observation, unchanged simplified LIF and frozen APL overlay. Formal biological transmitter identity, reward valence, receptor-specific physiology, association learning and long-term stability remain unproven.
|
||||
@@ -0,0 +1,161 @@
|
||||
"""Isolated in-memory hypothesis overlays; all frozen model/data files read-only."""
|
||||
from pathlib import Path
|
||||
import argparse,json,time
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import torch,yaml
|
||||
import phase4p,phase4p_finalize,phase4s,phase4v,phase4x
|
||||
from phase4p_revalidate import DeterministicAPLSim,door_inputs,make_drive
|
||||
from phase4r import save,digest,arrhash
|
||||
from phase4h_audit import verify_checksum
|
||||
from transmitter_roles_v1 import roles
|
||||
|
||||
def checks(cfg):
|
||||
xc=yaml.safe_load((Path(cfg['prior_model'])/'phase4x.yaml').read_text());out=phase4x.checks(xc)
|
||||
for key in ('prior_root','prior_model'):
|
||||
root=Path(cfg[key]);out['X_'+key]=verify_checksum(root,root/'checksums.sha256')
|
||||
assert all(x['passed'] for x in out.values());return out
|
||||
|
||||
class CountObserver:
|
||||
def __init__(self,rt,step):self.rt=rt;self.ext=rt.ext;self.step=step;self.before_late=None
|
||||
def __getattr__(self,name):return getattr(self.ext,name)
|
||||
def state_step(self,*args):
|
||||
if self.rt.step_index==self.step:self.before_late=self.rt.counts.cpu().numpy().copy()
|
||||
return self.ext.state_step(*args)
|
||||
|
||||
def run(cfg,exp,model):
|
||||
for d in ('protocol','audit','events','states','observations','comparisons','regression','performance'):(exp/d).mkdir(parents=True,exist_ok=True)
|
||||
assert not (exp/'protocol/preregistered.json').exists(),'Refusing overwrite'
|
||||
save(exp/'audit/preflight.json',checks(cfg))
|
||||
vc=yaml.safe_load((Path(cfg['input_model'])/'phase4v.yaml').read_text())
|
||||
oc,panel,vectors,selection,mapping=door_inputs();panel=panel[panel.split=='calibration'].reset_index(drop=True);assert len(panel)==6
|
||||
save(exp/'protocol/preregistered.json',{'config':cfg,'input_config':vc,'panel':panel.to_dict('records'),
|
||||
'sources':{p.name:digest(p) for p in model.iterdir() if p.is_file()},'before_simulation':True,
|
||||
'no_candidate_selection':True,'new_seed_is_one_replication_not_generalization_gate':True})
|
||||
pcfg=yaml.safe_load((Path(cfg['runtime_root'])/'phase4p.yaml').read_text());rt=phase4p.CanonicalRuntime(pcfg,Path(cfg['runtime_root']),exp);sim=DeterministicAPLSim(rt)
|
||||
role=roles(sim.meta);dpm=np.flatnonzero(role.annotation_DPM).astype(np.int32)
|
||||
assert sorted(sim.meta.iloc[dpm].body_id.astype(int).tolist())==cfg['perturbation']['expected_bodies']
|
||||
ids=np.flatnonzero(np.isin(sim.sources,dpm)).astype(np.int64);eg=torch.as_tensor(ids,dtype=torch.int64,device=sim.device)
|
||||
assert not set(ids)&set(sim.apl_out_ids)
|
||||
sim.meta.iloc[dpm].to_parquet(exp/'audit/DPM-source-metadata.parquet',index=False)
|
||||
pd.DataFrame({'edge_id':ids,'source_neuron_idx':sim.sources[ids],'target_neuron_idx':rt.indices_np[ids],
|
||||
'raw_synapse_count':rt.raw_np[ids],'frozen_effective_weight':sim.base[eg].cpu().numpy()}).to_parquet(exp/'audit/perturbed-existing-edges.parquet',index=False)
|
||||
original_install=sim.install;original_base_hash=arrhash(sim.base.cpu().numpy());weightchecks=[]
|
||||
original_install(None);original_overlay=rt.weights.cpu().numpy().copy()
|
||||
factor=1.
|
||||
def install(E):
|
||||
assert E is None,'Learning forbidden in diagnostic branch'
|
||||
original_install(None)
|
||||
rt.weights[eg]=sim.base[eg]*float(factor)
|
||||
sim.install=install
|
||||
for spec in cfg['conditions']:
|
||||
factor=spec['factor'];sim.install(None);got=rt.weights.cpu().numpy()
|
||||
expected=original_overlay.copy();expected[ids]=original_overlay[ids]*np.float32(factor)
|
||||
assert np.array_equal(got,expected)
|
||||
changed=np.flatnonzero(got!=original_overlay)
|
||||
assert np.all(np.isin(changed,ids))
|
||||
weightchecks.append({'condition':spec['name'],'factor':factor,'changed_effective_edges':len(changed),
|
||||
'only_declared_DPM_edges':True,'edge_array_length':len(got),'raw_weight_changes':0})
|
||||
save(exp/'regression/overlay-scope.json',weightchecks)
|
||||
populations={'all':np.arange(sim.n),'KC':sim.kc,'PN_annotation':sim.pn,'MBON':sim.mbon,'DPM':dpm,
|
||||
'DAN_annotation':np.flatnonzero(role.annotation_DAN),'MBON_downstream':sim.downstream}
|
||||
for cls in ('descending','motor'):
|
||||
populations[cls]=np.flatnonzero(sim.meta.annotation_superclass.eq(cls).to_numpy())
|
||||
save(exp/'audit/populations.json',{k:{'count':len(v),'definition':('exact annotation_superclass' if k in ('descending','motor') else 'frozen IDs or exact annotation evidence')} for k,v in populations.items()})
|
||||
target=np.unique(sim.identity.loc[sim.identity.compartment.eq(vc['target_compartment']),'source_neuron_idx'].to_numpy(np.int32))
|
||||
reg=[];stats=[];watchrows=[];poprows=[];comparisons=[];inputs=[]
|
||||
start=time.perf_counter();torch.cuda.reset_peak_memory_stats()
|
||||
def trial(targets,forced,name):
|
||||
observer=CountObserver(rt,round(cfg['late_start_ms']/sim.dt));rt.ext=observer
|
||||
try:r=phase4v.observed(sim,targets,forced,target,vc)
|
||||
finally:rt.ext=observer.ext
|
||||
assert r['finite'],'Nonfinite state; stop branch analysis'
|
||||
r['late_counts']=r['counts']-observer.before_late
|
||||
assert np.all(r['late_counts']>=0)
|
||||
return r
|
||||
for oi in cfg['odor_indices']:
|
||||
odor=panel.iloc[oi];targets,rates=make_drive(oc,panel,vectors,selection,mapping,odor.odor_id,'physiological')
|
||||
for seed in cfg['seeds']:
|
||||
actual=vc['seed_namespace']+oi*vc['odor_seed_offset']+seed
|
||||
forced=phase4v.forcing(sim,targets,rates,actual,vc,'offset')
|
||||
inputs.append({'odor':oi,'seed':seed,'actual_seed':actual,'forcing_hash':arrhash(forced.cpu().numpy()),'targets_rates_hash':arrhash(targets,rates)})
|
||||
reference=None
|
||||
for spec in cfg['conditions']:
|
||||
factor=spec['factor'];condition=spec['name'];name=f'{oi}-{seed}-{condition}'
|
||||
r=trial(targets,forced,name)
|
||||
phase4s.persist_run(exp,name,r)
|
||||
np.savez_compressed(exp/'states'/f'{name}.npz',**{k:r[k] for k in ('counts','late_counts','final_v','final_g','trace')})
|
||||
pd.DataFrame(r['samples']).to_parquet(exp/'observations'/f'{name}.parquet',index=False)
|
||||
if condition=='frozen_reference':
|
||||
reference=r
|
||||
if seed==cfg['old_replay_seed']:
|
||||
oldname=f'{oi}-{seed}-offset';old=np.load(Path(cfg['input_experiment'])/'states'/f'{oldname}.npz')
|
||||
eq={k:bool(np.array_equal(old[k],r[k])) for k in ('counts','final_v','final_g','trace')}
|
||||
ev=pd.read_parquet(Path(cfg['input_experiment'])/'events'/f'{oldname}.parquet')
|
||||
eq['KC_events']=arrhash(ev.step.to_numpy(),ev.neuron_idx.to_numpy())==arrhash(r['kc_event_step'],r['kc_event_node'])
|
||||
assert all(eq.values());reg.append({'trial':name,'test':'frozen_V_reproduction',**eq})
|
||||
assert reference is not None
|
||||
if oi==cfg['odor_indices'][0]:
|
||||
rr=trial(targets,forced,name)
|
||||
keys=('counts','late_counts','final_v','final_g','kc_event_step','kc_event_node')
|
||||
eq={k:bool(np.array_equal(r[k],rr[k])) for k in keys};assert all(eq.values())
|
||||
reg.append({'trial':name,'test':'exact_repeat',**eq})
|
||||
es=r['kc_event_step'];en=r['kc_event_node'];late=es>=round(cfg['late_start_ms']/sim.dt)
|
||||
stats.append({'odor_index':oi,'odor_name':odor.odor_name,'seed':seed,'condition':condition,
|
||||
'whole_brain_spikes':int(r['counts'].astype(np.int64).sum()),'whole_brain_active':int(np.count_nonzero(r['counts'])),
|
||||
'late_whole_brain_spikes':int(r['late_counts'].astype(np.int64).sum()),'KC_spikes':len(es),'late_KC_spikes':int(late.sum()),
|
||||
'late_KC_active':int(np.unique(en[late]).size),'finite':r['finite'],'wall_seconds':r['wall_seconds']})
|
||||
for node in cfg['watch_neuron_idx']:
|
||||
t=es[en==node];watchrows.append({'odor_index':oi,'seed':seed,'condition':condition,'neuron_idx':node,
|
||||
'total_spikes':len(t),'late_spikes':int((t>=round(cfg['late_start_ms']/sim.dt)).sum()),
|
||||
'first_ms':float(t.min()*sim.dt) if len(t) else None,'last_ms':float(t.max()*sim.dt) if len(t) else None})
|
||||
for pop,idx in populations.items():
|
||||
poprows.append({'odor_index':oi,'seed':seed,'condition':condition,'population':pop,'neurons':len(idx),
|
||||
'total_spikes':int(r['counts'][idx].astype(np.int64).sum()),'active':int(np.count_nonzero(r['counts'][idx])),
|
||||
'late_spikes':int(r['late_counts'][idx].astype(np.int64).sum()),'late_active':int(np.count_nonzero(r['late_counts'][idx]))})
|
||||
comparisons.append({'odor_index':oi,'seed':seed,'condition':condition,
|
||||
'neurons_with_changed_counts':int(np.count_nonzero(r['counts']!=reference['counts'])),
|
||||
'max_abs_finalV_difference':float(np.abs(r['final_v'].astype(float)-reference['final_v']).max()),
|
||||
'KC_event_sequence_equal':arrhash(es,en)==arrhash(reference['kc_event_step'],reference['kc_event_node'])})
|
||||
save(exp/'regression/trials.json',reg)
|
||||
print(json.dumps({'odor_complete':oi,'seed':seed,'trials':len(stats)}),flush=True)
|
||||
sim.install=original_install;original_install(None)
|
||||
assert arrhash(sim.base.cpu().numpy())==original_base_hash
|
||||
assert np.array_equal(rt.weights.cpu().numpy(),original_overlay)
|
||||
save(exp/'regression/restore.json',{'immutable_memory_base_hash_exact':True,'frozen_APL_overlay_restored_exact':True})
|
||||
assert phase4s.evidence_regression(cfg,exp,rt,sim)
|
||||
for name,data in [('trials',stats),('watched-KC',watchrows),('populations',poprows),('paired-differences',comparisons)]:pd.DataFrame(data).to_parquet(exp/'comparisons'/f'{name}.parquet',index=False)
|
||||
save(exp/'protocol/input-hashes.json',inputs)
|
||||
save(exp/'audit/results.json',{'phase':'4Y','status':'COMPLETE-EXPERIMENTAL-HYPOTHESIS-AUDIT','trials':len(stats),'repeat_trials':6,
|
||||
'old_reference_reproductions':6,'frozen_reference_trials':5,'reference_pass':True,'all_finite':True,
|
||||
'DPM_neurons':len(dpm),'DPM_existing_edges':len(ids),'weight_training':False,'candidate_selected':False,
|
||||
'frozen_baseline_changed':False,'biological_sign_proven':False,'new_seed_count':1,'old_seed_count':1})
|
||||
save(exp/'performance/summary.json',{'wall_seconds_excluding_build':time.perf_counter()-start,'peak_VRAM_bytes':torch.cuda.max_memory_allocated(),
|
||||
'GPU':torch.cuda.get_device_name(0),'diagnostic_observation_overhead_included':True})
|
||||
|
||||
def seal(cfg,exp,model):
|
||||
pre=json.loads((exp/'protocol/preregistered.json').read_text());now=checks(cfg);hist=phase4p_finalize.integrity()
|
||||
ok=hist['all_static_checks_passed'] and now==json.loads((exp/'audit/preflight.json').read_text()) and all(digest(model/n)==h for n,h in pre['sources'].items())
|
||||
save(exp/'audit/frozen-integrity.json',{'pass':ok,'history':hist,'current':now});assert ok
|
||||
result=json.loads((exp/'audit/results.json').read_text());result['integrity_pass']=True;save(exp/'audit/results.json',result)
|
||||
w=pd.read_parquet(exp/'comparisons/watched-KC.parquet');t=pd.read_parquet(exp/'comparisons/trials.parquet')
|
||||
lines=['# Phase 4Y — independent DPM fast-current hypotheses','',
|
||||
'Status: COMPLETE-EXPERIMENTAL-HYPOTHESIS-AUDIT. No candidate selection, training, game integration or frozen baseline changes.',
|
||||
'All conditions use identical PN forcing for 120ms, then off to 1000ms. Six old odors, one old seed and one new seed. Not a population-level independent generalization gate.',
|
||||
'Factors 0 and -1 are diagnostic model interventions, not corrections to raw labels or validated receptor-specific physiology. Only actual outgoing DPM effective currents change in RAM. Graph and raw counts stay intact.','',
|
||||
'| KC | condition | late spike range (12 trials) |','|---:|---|---:|']
|
||||
for (node,condition),g in w.groupby(['neuron_idx','condition']):lines.append(f'| {node} | {condition} | {g.late_spikes.min()}–{g.late_spikes.max()} |')
|
||||
lines+=['','| condition | mean whole-brain spikes | mean late KC spikes | mean late active KC |','|---|---:|---:|---:|']
|
||||
for condition,g in t.groupby('condition'):lines.append(f'| {condition} | {g.whole_brain_spikes.mean():.2f} | {g.late_KC_spikes.mean():.2f} | {g.late_KC_active.mean():.2f} |')
|
||||
lines+=['','Exact controls: six Phase4V original trials, six repeats, five frozen reference trials, unchanged in-memory base hash and restored frozen overlay.',
|
||||
'Finite states do not establish biological stability. No calibrated runaway threshold, biological sign conclusion, receptor kinetics or learning success is inferred.',
|
||||
'Whole-brain intervention outcomes may differ from prior isolated replay because all feedback responds. Report this difference, do not select a condition by favorable activity.']
|
||||
(exp/'REPORT.md').write_text('\n'.join(lines)+'\n',encoding='utf-8')
|
||||
for root in (model,exp):
|
||||
rows=[{'path':p.relative_to(root).as_posix(),'sha256':digest(p),'bytes':p.stat().st_size} for p in sorted(root.rglob('*')) if p.is_file() and p.name not in ('manifest.json','checksums.sha256')]
|
||||
save(root/'manifest.json',{'files':rows});rows.append({'path':'manifest.json','sha256':digest(root/'manifest.json')})
|
||||
(root/'checksums.sha256').write_text(''.join(x['sha256']+' '+x['path']+'\n' for x in rows));q=verify_checksum(root,root/'checksums.sha256');assert q['passed'];print(json.dumps(q),flush=True)
|
||||
|
||||
if __name__=='__main__':
|
||||
p=argparse.ArgumentParser();p.add_argument('task',choices=['run','seal']);p.add_argument('--config',required=True);a=p.parse_args()
|
||||
cfg=yaml.safe_load(Path(a.config).read_text());(run if a.task=='run' else seal)(cfg,Path(cfg['experiment_root']),Path(cfg['model_root']))
|
||||
@@ -0,0 +1,39 @@
|
||||
phase: 4Y
|
||||
experiment_root: /data/digital-drosophila/experiments/phase4y-dpm-hypothesis
|
||||
model_root: /data/digital-drosophila/models/male-cns-lif/dpm-hypothesis-v1
|
||||
prior_root: /data/digital-drosophila/experiments/phase4x-transmitter-provenance
|
||||
prior_model: /data/digital-drosophila/models/male-cns-lif/transmitter-audit-v1
|
||||
runtime_root: /data/digital-drosophila/models/male-cns-lif/deterministic-runtime-v2
|
||||
phase4p_root: /data/digital-drosophila/experiments/phase4p-deterministic-runtime
|
||||
input_model: /data/digital-drosophila/models/male-cns-lif/csus-audit-v1
|
||||
input_experiment: /data/digital-drosophila/experiments/phase4v-csus-semantics
|
||||
odor_indices: [0,1,2,3,4,5]
|
||||
seeds: [131101,141101]
|
||||
old_replay_seed: 131101
|
||||
conditions:
|
||||
- {name: frozen_reference, factor: 1.0}
|
||||
- {name: DPM_fast_current_off, factor: 0.0}
|
||||
- {name: DPM_fast_current_reversed, factor: -1.0}
|
||||
perturbation:
|
||||
exact_type: DPM
|
||||
expected_bodies: [11734,12569]
|
||||
scope: all and only outgoing effective currents of the two annotation-DPM neurons
|
||||
no_fast_current: diagnostic ablation; not deletion of neurons or anatomical edges and not a complete slow-modulator model
|
||||
reversed: sign-sensitivity hypothesis; not verified receptor-specific GABA/serotonin physiology
|
||||
input_condition: same original 120ms PN forcing, then off to 1000ms
|
||||
late_start_ms: 500
|
||||
watch_neuron_idx: [59832,78836,82312]
|
||||
repeat_check: first odor and both seeds, all three conditions
|
||||
scope:
|
||||
authorization: user delegated independent hypothesis-branch decision after explicit request for authorization
|
||||
diagnostic_only: true
|
||||
baseline_read_only: true
|
||||
weight_training: false
|
||||
slow_receptor_parameters_invented: false
|
||||
candidate_selection: false
|
||||
game_or_RL: false
|
||||
reserved_novel_odors: untouched
|
||||
interpretation:
|
||||
report: all conditions and both seeds; do not pick the most active or best-looking result
|
||||
causal_scope: effect of intervention on frozen mathematical model, not proof of biological neurotransmitter identity
|
||||
stability: report nonfinite state, brain spike counts, active fractions, and KC late activity; no biological runaway threshold inferred
|
||||
@@ -0,0 +1,106 @@
|
||||
"""Descriptive paired effects only; no condition selection or parameter fitting."""
|
||||
from pathlib import Path
|
||||
import json
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import yaml
|
||||
from phase4r import save
|
||||
|
||||
cfg=yaml.safe_load(Path(__file__).with_name('phase4y.yaml').read_text());exp=Path(cfg['experiment_root'])
|
||||
assert not (exp/'checksums.sha256').exists(),'Sealed output is immutable'
|
||||
w=pd.read_parquet(exp/'comparisons/watched-KC.parquet');t=pd.read_parquet(exp/'comparisons/trials.parquet')
|
||||
p=pd.read_parquet(exp/'comparisons/populations.parquet');x=pd.read_parquet(Path(cfg['prior_root'])/'replay/results.parquet')
|
||||
inputrows=[]
|
||||
for _,r in t.iterrows():
|
||||
obs=pd.read_parquet(exp/'observations'/f'{r.odor_index}-{r.seed}-{r.condition}.parquet').iloc[-1]
|
||||
inputrows.append({'odor_index':int(r.odor_index),'seed':int(r.seed),'condition':r.condition,
|
||||
'requested':int(obs.PN_requested_cumulative),'accepted_new_spikes':int(obs.PN_accepted_cumulative),
|
||||
'preexisting_spikes':int(obs.PN_preexisting_cumulative),'refractory_blocked':int(obs.PN_blocked_cumulative)})
|
||||
inp=pd.DataFrame(inputrows);assert (inp.groupby(['odor_index','seed']).requested.nunique()==1).all()
|
||||
assert (inp.requested==inp.accepted_new_spikes+inp.preexisting_spikes+inp.refractory_blocked).all()
|
||||
inp.to_parquet(exp/'comparisons/input-request-vs-acceptance.parquet',index=False)
|
||||
save(exp/'audit/input-matching.json',{'scheduled_requests_matched_all_pairs':True,'partition_exact':True,
|
||||
'pairs_with_changed_accepted_count':int((inp.groupby(['odor_index','seed']).accepted_new_spikes.nunique()>1).sum()),
|
||||
'interpretation':'identical exogenous requests, not guaranteed identical accepted PN spikes; acceptance depends on endogenous refractory/spike state through the unchanged force interface'})
|
||||
# Reporting-only schema correction. Preserve original tables and the registered run source.
|
||||
# These exact superclass values are used by the frozen Phase4C propagation summaries.
|
||||
meta_path=Path('/data/digital-drosophila/datasets/male-cns-v1.0/processed/neuron-only/neuron-index/unified-metadata.parquet')
|
||||
sup=pd.read_parquet(meta_path,columns=['neuron_idx','annotation_superclass']).sort_values('neuron_idx')
|
||||
definitions={'descending':['descending_neuron','descending_neuron_tbc'],'motor':['vnc_motor','cb_motor']}
|
||||
corrected=[]
|
||||
for _,r in t.iterrows():
|
||||
name=f'{r.odor_index}-{r.seed}-{r.condition}'
|
||||
with np.load(exp/'states'/f'{name}.npz') as z:
|
||||
for pop,values in definitions.items():
|
||||
idx=np.flatnonzero(sup.annotation_superclass.isin(values).to_numpy());assert len(idx)>0
|
||||
c=z['counts'][idx];l=z['late_counts'][idx]
|
||||
corrected.append({'odor_index':int(r.odor_index),'seed':int(r.seed),'condition':r.condition,'population':pop,
|
||||
'neurons':len(idx),'total_spikes':int(c.astype(np.int64).sum()),'active':int(np.count_nonzero(c)),
|
||||
'late_spikes':int(l.astype(np.int64).sum()),'late_active':int(np.count_nonzero(l))})
|
||||
p=pd.concat([p[~p.population.isin(definitions)],pd.DataFrame(corrected)],ignore_index=True)
|
||||
p.to_parquet(exp/'comparisons/populations-corrected.parquet',index=False)
|
||||
save(exp/'audit/population-schema-correction.json',{'scope':'report aggregation only, no dynamics or registered conditions changed',
|
||||
'issue':'initial literal superclass descending/motor matches zero rows; those zero-population rows are invalid readouts, not evidence of silence',
|
||||
'definitions_from':'frozen phase4c.py summary definitions, verified against actual metadata',
|
||||
'corrected_values':definitions,'corrected_counts':{k:int(sup.annotation_superclass.isin(v).sum()) for k,v in definitions.items()},
|
||||
'source':str(meta_path),'original_tables_preserved':True,'recomputed_from':'saved complete per-neuron total and late counts','simulation_rerun':False})
|
||||
(exp/'READ-ME-FIRST.md').write_text(
|
||||
'# Phase 4Y evidence routing\n\n'
|
||||
'Use audit/MECHANISM.md and comparisons/populations-corrected.parquet for population readouts.\n\n'
|
||||
'The original comparisons/populations.parquet descending/motor rows used invalid literal superclass names and matched zero neurons. '
|
||||
'Those rows are preserved for audit but must not be interpreted as no activity. Corrected rows use the frozen Phase4C definitions '
|
||||
'(descending_neuron/descending_neuron_tbc; vnc_motor/cb_motor) and were recomputed from saved complete counts, without rerunning dynamics. '
|
||||
'See audit/population-schema-correction.json. Other original population rows are unchanged.\n\n'
|
||||
'All three hypotheses are reported. None is promoted to a learned or biological baseline.\n',encoding='utf-8')
|
||||
effects=[];isolated=[]
|
||||
for (oi,seed,node),g in w.groupby(['odor_index','seed','neuron_idx']):
|
||||
baseline=int(g.loc[g.condition.eq('frozen_reference'),'late_spikes'].iloc[0])
|
||||
for _,r in g.iterrows():
|
||||
effects.append({'odor_index':int(oi),'seed':int(seed),'neuron_idx':int(node),'condition':r.condition,
|
||||
'baseline_late_spikes':baseline,'condition_late_spikes':int(r.late_spikes),'paired_change':int(r.late_spikes)-baseline})
|
||||
if seed==cfg['old_replay_seed']:
|
||||
name=f'{oi}-{seed}-offset';z=x[(x.trial==name)&(x.neuron_idx==node)]
|
||||
ib=int(z.loc[z['mode'].eq('base'),'late_spikes'].iloc[0]);io=int(z.loc[z['mode'].eq('omit_DPM_annotation'),'late_spikes'].iloc[0])
|
||||
off=int(g.loc[g.condition.eq('DPM_fast_current_off'),'late_spikes'].iloc[0]);assert ib==baseline
|
||||
isolated.append({'odor_index':int(oi),'neuron_idx':int(node),'baseline_late_spikes':baseline,
|
||||
'isolated_DPM_off_late_spikes':io,'whole_brain_DPM_off_late_spikes':off,
|
||||
'whole_minus_isolated':off-io,'interpretation':'different feedback assumptions; not a numerical regression failure'})
|
||||
pd.DataFrame(effects).to_parquet(exp/'comparisons/watched-paired-effects.parquet',index=False)
|
||||
pd.DataFrame(isolated).to_parquet(exp/'comparisons/isolated-vs-whole-brain.parquet',index=False)
|
||||
new=[];loss=[]
|
||||
metadata=pd.read_parquet(Path(cfg['prior_root'])/'roles/neuron-evidence-axes.parquet').set_index('neuron_idx')
|
||||
for (oi,seed),group in t.groupby(['odor_index','seed']):
|
||||
def late_set(condition):
|
||||
e=pd.read_parquet(exp/'events'/f'{oi}-{seed}-{condition}.parquet');return set(map(int,e.loc[e.step>=5000,'neuron_idx']))
|
||||
ref=late_set('frozen_reference')
|
||||
for condition in ('DPM_fast_current_off','DPM_fast_current_reversed'):
|
||||
s=late_set(condition)
|
||||
for label,nodes in [('new',s-ref),('lost',ref-s)]:
|
||||
for node in sorted(nodes):
|
||||
row={'odor_index':int(oi),'seed':int(seed),'condition':condition,'change':label,'neuron_idx':node,
|
||||
'body_id':int(metadata.loc[node,'body_id']),'annotation_type':metadata.loc[node,'annotation_type']}
|
||||
(new if label=='new' else loss).append(row)
|
||||
pd.DataFrame(new+loss,columns=['odor_index','seed','condition','change','neuron_idx','body_id','annotation_type']).to_parquet(exp/'comparisons/late-KC-membership-changes.parquet',index=False)
|
||||
ep=pd.DataFrame(effects);summary=[]
|
||||
for (node,condition),g in ep.groupby(['neuron_idx','condition']):
|
||||
summary.append({'neuron_idx':int(node),'condition':condition,'n':len(g),'late_min':int(g.condition_late_spikes.min()),
|
||||
'late_max':int(g.condition_late_spikes.max()),'change_min':int(g.paired_change.min()),'change_max':int(g.paired_change.max()),
|
||||
'decreases':int((g.paired_change<0).sum()),'increases':int((g.paired_change>0).sum()),'unchanged':int((g.paired_change==0).sum())})
|
||||
save(exp/'audit/descriptive-effects.json',{'scope':'post-run descriptive summaries; no promotion or new gate',
|
||||
'watched':summary,'new_late_KC_memberships':len(new),'lost_late_KC_memberships':len(loss)})
|
||||
lines=['# Phase 4Y — paired whole-brain effects','',
|
||||
'All branches are diagnostic hypotheses; none is selected as a better biological model.','',
|
||||
'| KC | condition | late spike range | paired change range | decreases / unchanged / increases |','|---:|---|---:|---:|---:|']
|
||||
for r in summary:lines.append(f"| {r['neuron_idx']} | {r['condition']} | {r['late_min']}–{r['late_max']} | {r['change_min']}–{r['change_max']} | {r['decreases']} / {r['unchanged']} / {r['increases']} |")
|
||||
lines+=['','| KC | isolated DPM-off range | whole-brain DPM-off range (same old seed) |','|---:|---:|---:|']
|
||||
for node,g in pd.DataFrame(isolated).groupby('neuron_idx'):
|
||||
lines.append(f'| {node} | {g.isolated_DPM_off_late_spikes.min()}–{g.isolated_DPM_off_late_spikes.max()} | {g.whole_brain_DPM_off_late_spikes.min()}–{g.whole_brain_DPM_off_late_spikes.max()} |')
|
||||
lines+=['','| population | condition | mean late spikes | mean late active neurons |','|---|---|---:|---:|']
|
||||
for (pop,condition),g in p.groupby(['population','condition']):
|
||||
lines.append(f'| {pop} | {condition} | {g.late_spikes.mean():.2f} | {g.late_active.mean():.2f} |')
|
||||
lines+=['','Whole-brain feedback is allowed to respond; isolated replay holds the other recorded arrivals fixed. A difference between them is the target of the experiment, not grounds for choosing one result.',
|
||||
'Input matching is at the scheduled request level. Accepted new PN spikes can differ through feedback-dependent refractory state; see input-request-vs-acceptance.parquet. Do not claim identical delivered PN spike trains.',
|
||||
'Descending/motor rows here use populations-corrected.parquet, reconstructed from saved full counts and verified actual superclass names. The original zero-population rows are retained but invalid; see audit/population-schema-correction.json.',
|
||||
'No learning, no reward events, no parameter fitting, no promotion; smaller late activity is not itself success. Biological signs and long-term stability remain unproven.']
|
||||
(exp/'audit/MECHANISM.md').write_text('\n'.join(lines)+'\n',encoding='utf-8')
|
||||
print('\n'.join(lines[:20]))
|
||||
Reference in New Issue
Block a user