wuff-mann commited on
Commit
7a7c3b2
·
verified ·
1 Parent(s): eeebbfa

Archive CIDM-v2 reproducible b2_r9_activation_timing.py

Browse files
cidm_v2/source/B2_R9_P0/b2_r9_activation_timing.py ADDED
@@ -0,0 +1,295 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """CIDM-v2 B2-R9/P0: causal activation timing under equal per-step or total RMS-energy.
2
+
3
+ Frozen original backbone and pretrained B2-R6 heads; ground truth enters ONLY scoring.
4
+ The fixed lead-dependent schedules are retrospective ablations, NOT a learned controller
5
+ or a validated deployment policy. All B0 checkpoint selection predates this study.
6
+ """
7
+ from __future__ import annotations
8
+ import csv,hashlib,io,json,math,zipfile
9
+ from pathlib import Path
10
+ import torch
11
+ from benchmark import Config,Windows,UnifiedModel,make_synthetic_cache
12
+ from b2_r1_flux_control import check_zip
13
+ from b2_r6_causal_spatial_controller import sha,make_cache_checked,orthogonal_matrix,effect_basis,TASK_LIST,LEADS
14
+ from b2_r7_frozen_controller_gain_audit import load_heads,write_csv,write_json
15
+ from b2_r8_equal_exposure_audit import (validate_r7,validate_r6,rollout_budget,
16
+ R6_REAL_ZIP_SHA,R7_REAL_ZIP_SHA,PARENT_SHA)
17
+
18
+ R8_REAL_ZIP_SHA='ad8334d8ac0632ccd29965305bc613d6628fdcc89b34906ba30ca64604947159'
19
+ PRIMARY_BUDGET=.003
20
+ SECONDARY_BUDGET=.001
21
+ ONSETS=(1,4,7)
22
+ STEPS=12
23
+ VAL_INDICES=list(range(16,32))
24
+
25
+
26
+ def validate_r8(archive):
27
+ if sha(archive)!=R8_REAL_ZIP_SHA:raise RuntimeError('B2_R8_REAL_ZIP_SHA_MISMATCH')
28
+ with zipfile.ZipFile(archive) as z:
29
+ if z.testzip():raise RuntimeError('B2_R8_ZIP_CRC_FAIL')
30
+ rep=json.loads(z.read('report.json'))
31
+ if rep.get('status')!='FROZEN_EQUAL_RMS_AUDIT_COMPLETED_NO_TRAINING':
32
+ raise RuntimeError('B2_R8_NOT_FROZEN_AUDIT_COMPLETE')
33
+ if set(x['task'] for x in rep.get('cases',[]))!=set(TASK_LIST):
34
+ raise RuntimeError('B2_R8_NOT_THREE_TASKS')
35
+ manifest=json.loads(z.read('FILE_SHA256_MANIFEST.json'))
36
+ for ent in manifest:
37
+ if hashlib.sha256(z.read(ent['path'])).hexdigest()!=ent['sha256']:
38
+ raise RuntimeError('B2_R8_INNER_SHA_MISMATCH '+ent['path'])
39
+ rows=list(csv.DictReader(io.StringIO(z.read('sample_mse.csv').decode())))
40
+ if len(rows)!=5040:raise RuntimeError('B2_R8_EXPECTED_5040_SAMPLES')
41
+ per={(row['task'],int(row['window_index']),row['method'],int(row['lead'])):float(row['mse_physical']) for row in rows}
42
+ return rep,per
43
+
44
+
45
+ def rms(x):
46
+ return x.square().mean(dim=(1,2,3),keepdim=True).sqrt()
47
+
48
+
49
+ def spec_name(kind,budget,onset,mode):
50
+ if kind=='baseline':return 'baseline'
51
+ return f'{kind}__b{budget*100:.3f}pct__onset{onset}__{mode}'
52
+
53
+
54
+ def schedule_fraction(budget,onset,mode):
55
+ if mode=='fixed_step' or onset==1:return float(budget)
56
+ if mode=='matched_energy':return float(budget)*math.sqrt(STEPS/(STEPS-onset+1))
57
+ raise ValueError(mode)
58
+
59
+
60
+ @torch.no_grad()
61
+ def rollout_timing(core,head,h,*,Q=None,rotate=False,budget=.003,onset=1,mode='fixed_step'):
62
+ mm=core.model;one_d=(h.ndim==4)
63
+ hh=h.unsqueeze(-2) if one_d else h
64
+ B,L,C,H,W=hh.shape;coeff=mm.coefficients()
65
+ z=mm.encode(hh.reshape(B,L*C,H,W));prev=hh[:,-1];initial=prev
66
+ skew=mm.skew-mm.skew.T
67
+ preds=[]; per_lead={}; exposure=hh.new_zeros((B,));min_raw=float('inf')
68
+ step_fraction=schedule_fraction(budget,onset,mode)
69
+ for lead in range(1,STEPS+1):
70
+ f=coeff['flux']*mm.flux(z)
71
+ l=coeff['local']*torch.tanh(mm.local(z))
72
+ s=.035*torch.einsum('ij,bjhw->bihw',skew,z)
73
+ base=z+f+l+s
74
+ if head is None or lead<onset:
75
+ change=torch.zeros_like(base);rel=base.new_zeros((B,))
76
+ coeff_abs=base.new_zeros((B,));inactive=base.new_zeros((B,))
77
+ else:
78
+ basis,amps=effect_basis(f,l,s,Q if rotate else None)
79
+ gates=head(base,prev,initial,amps,lead)
80
+ raw=(gates.unsqueeze(2)*basis).sum(dim=1)/math.sqrt(3)
81
+ rr=rms(raw)
82
+ active=(rr>=1e-9)
83
+ change=step_fraction*rms(base).clamp_min(1e-4)*raw/rr.clamp_min(1e-9)
84
+ change=torch.where(active,change,torch.zeros_like(change))
85
+ rel=(rms(change)/rms(base).clamp_min(1e-4)).flatten()
86
+ coeff_abs=gates.abs().mean(dim=(1,2,3))
87
+ inactive=(~active).flatten().float()
88
+ if active.any():
89
+ mismatch=(rel[active.flatten()]-step_fraction).abs().max().item()
90
+ if mismatch>max(1e-6,step_fraction*2e-3):
91
+ raise RuntimeError('R9_PER_SAMPLE_EXPOSURE_CONTRACT_FAIL '+str(mismatch))
92
+ exposure=exposure+rel.square()
93
+ z=base+change
94
+ out=prev+coeff['out']*torch.tanh(mm.decode(z));prev=out
95
+ preds.append(out.squeeze(-2) if one_d else out)
96
+ if lead in LEADS:
97
+ per_lead[lead]={'rel':rel.detach().cpu(),'coef':coeff_abs.detach().cpu(),
98
+ 'inactive':inactive.detach().cpu(), 'cum_energy':exposure.detach().cpu()}
99
+ return torch.stack(preds,1),per_lead
100
+
101
+
102
+ def specs_for(smoke):
103
+ budgets=(PRIMARY_BUDGET,) if smoke else (SECONDARY_BUDGET,PRIMARY_BUDGET)
104
+ specs=[('baseline',None,0.,1,'fixed_step')]
105
+ for kind in ('operator','rotated'):
106
+ for budget in budgets:
107
+ for onset in ONSETS:
108
+ modes=('fixed_step',) if onset==1 else ('fixed_step','matched_energy')
109
+ for mode in modes:specs.append((spec_name(kind,budget,onset,mode),kind,budget,onset,mode))
110
+ return specs
111
+
112
+
113
+ @torch.no_grad()
114
+ def evaluate(core,heads,Q,val,indices,device,*,smoke=False):
115
+ specs=specs_for(smoke);rows=[];curves=[];totals={name:{lead:[0.,0] for lead in LEADS} for name,*_ in specs}
116
+ diag={name:{lead:[0.,0.,0.,0.,0.] for lead in LEADS} for name,*_ in specs}
117
+ assert set(indices)==(set([0,1]) if smoke else set(VAL_INDICES))
118
+ parity_max=0.;prefix_parity_max=0.;exposure_maxdiff=0.
119
+ for ix in range(0,len(indices),2):
120
+ ids=indices[ix:ix+2];arr=[val[int(i)] for i in ids]
121
+ h=torch.stack([a[0] for a in arr]).to(device)
122
+ truth=torch.stack([a[1] for a in arr]).to(device)
123
+ scale=val.sd.to(device).reshape(1,-1,*([1]*(truth.ndim-3)))
124
+ pred_baseline,base_diag=rollout_timing(core,None,h)
125
+ all_preds={'baseline':(pred_baseline,base_diag)}
126
+ for name,kind,budget,onset,mode in specs[1:]:
127
+ p,d=rollout_timing(core,heads[kind],h,Q=Q,rotate=(kind=='rotated'),budget=budget,onset=onset,mode=mode)
128
+ all_preds[name]=(p,d)
129
+ if onset>1:
130
+ prefix_parity_max=max(prefix_parity_max,float((p[:,:onset-1]-pred_baseline[:,:onset-1]).abs().max()))
131
+ for name,kind,budget,onset,mode in specs:
132
+ pred,dd=all_preds[name]
133
+ for lead in LEADS:
134
+ loss=((pred[:,lead-1]-truth[:,lead-1])*scale).square().flatten(1).mean(1)
135
+ totals[name][lead][0]+=float(loss.sum());totals[name][lead][1]+=len(ids)
136
+ for window,loss_value in zip(ids,loss.cpu().tolist()):
137
+ rows.append({'task':'','window_index':int(window),'method':name,
138
+ 'lead':int(lead),'mse_physical':float(loss_value)})
139
+ if kind is not None:
140
+ v=dd[lead];acc=diag[name][lead]
141
+ acc[0]+=float(v['rel'].sum());acc[1]+=float(v['coef'].sum())
142
+ acc[2]+=float(v['inactive'].sum());acc[3]+=float(v['cum_energy'].sum());acc[4]+=len(ids)
143
+ # Compare Natural v Rotated realized step budget and integral exposure.
144
+ for budget in ((PRIMARY_BUDGET,) if smoke else (SECONDARY_BUDGET,PRIMARY_BUDGET)):
145
+ for onset in ONSETS:
146
+ for mode in (('fixed_step',) if onset==1 else ('fixed_step','matched_energy')):
147
+ n=all_preds[spec_name('operator',budget,onset,mode)][1]
148
+ r=all_preds[spec_name('rotated',budget,onset,mode)][1]
149
+ for lead in LEADS:
150
+ exposure_maxdiff=max(exposure_maxdiff,float((n[lead]['rel']-r[lead]['rel']).abs().max()))
151
+ if prefix_parity_max>1e-6:raise RuntimeError('B2_R9_ZERO_PREFIX_NOT_IDENTICAL '+str(prefix_parity_max))
152
+ if exposure_maxdiff>2e-6:raise RuntimeError('B2_R9_NAT_ROT_NOT_EXPOSURE_MATCHED '+str(exposure_maxdiff))
153
+ for name,kind,b,onset,mode in specs:
154
+ for lead in LEADS:
155
+ sm,n=totals[name][lead];bm,bn=totals['baseline'][lead]
156
+ a=diag[name][lead]
157
+ curves.append({'task':'','method':name,'lead':lead,'mse_physical':sm/n,
158
+ 'ratio_to_baseline':(sm/n)/(bm/bn),
159
+ 'realized_rel_rms':a[0]/max(a[4],1),
160
+ 'cumulative_rms_energy':a[3]/max(a[4],1),
161
+ 'gate_abs_mean':a[1]/max(a[4],1),
162
+ 'inactive_fraction':a[2]/max(a[4],1),
163
+ 'onset':onset,'mode':mode,'registered_budget':b})
164
+ return curves,rows,{'zero_prefix_max':prefix_parity_max,'exposure_difference_max':exposure_maxdiff,'num_windows':len(indices)}
165
+
166
+
167
+ def compare_r8_replay(samples,archive_ref,task,smoke=False):
168
+ if smoke:return 0.
169
+ got={(int(r['window_index']),r['method'],int(r['lead'])):float(r['mse_physical']) for r in samples}
170
+ checks=[]
171
+ for window in VAL_INDICES:
172
+ for lead in LEADS:
173
+ names={'baseline':'baseline'}
174
+ for b in (SECONDARY_BUDGET,PRIMARY_BUDGET):
175
+ for k in ('operator','rotated'):
176
+ names[spec_name(k,b,1,'fixed_step')]=f'{k}__b{b*100:.3f}pct__s+1'
177
+ for current,archived in names.items():
178
+ v=got[(window,current,lead)];ref=archive_ref[(task,window,archived,lead)]
179
+ diff=abs(v-ref)
180
+ if diff>max(1e-8,abs(ref)*0.003):
181
+ raise RuntimeError(f'B2_R8_WINDOW_REPLAY_MISMATCH {task}/{window}/{current}/{lead} {v} {ref}')
182
+ checks.append(diff)
183
+ return max(checks)
184
+
185
+
186
+ def summarize_risk(samples,task,budget=PRIMARY_BUDGET):
187
+ lookup={(int(r['window_index']),r['method'],int(r['lead'])):float(r['mse_physical']) for r in samples}
188
+ output=[]
189
+ for onset in ONSETS:
190
+ for mode in (('fixed_step',) if onset==1 else ('fixed_step','matched_energy')):
191
+ natural=spec_name('operator',budget,onset,mode);rot=spec_name('rotated',budget,onset,mode)
192
+ for lead in (1,3,9,12):
193
+ ids=VAL_INDICES
194
+ base=[lookup[i,'baseline',lead] for i in ids]
195
+ n=[lookup[i,natural,lead] for i in ids];r=[lookup[i,rot,lead] for i in ids]
196
+ floor=max(1e-12,sum(base)/len(base)*1e-4)
197
+ sample_ratios=sorted(a/max(b,floor) for a,b in zip(n,base))
198
+ output.append({'task':task,'onset':onset,'mode':mode,'lead':lead,
199
+ 'natural_vs_baseline_ratio':sum(n)/sum(base),
200
+ 'rotated_vs_baseline_ratio':sum(r)/sum(base),
201
+ 'natural_win_rate':sum(a<b for a,b in zip(n,base))/len(base),
202
+ 'natural_beats_rotated_rate':sum(a<b for a,b in zip(n,r))/len(base),
203
+ 'natural_regression_over_2pct':sum(a>b*1.02+floor for a,b in zip(n,base))/len(base),
204
+ 'sample_ratio_p90':sample_ratios[math.ceil(.9*len(sample_ratios))-1],
205
+ 'floor':floor})
206
+ return output
207
+
208
+
209
+ def gate_task(curves,risk,task):
210
+ arr={r['method']:{'first':r} for r in []} # placeholder-free: explicit indexed curve lookup below
211
+ refs={(r['method'],int(r['lead'])):r for r in curves}
212
+ signals=[]
213
+ for onset in ONSETS:
214
+ for mode in (('fixed_step',) if onset==1 else ('fixed_step','matched_energy')):
215
+ n=spec_name('operator',PRIMARY_BUDGET,onset,mode)
216
+ r=spec_name('rotated',PRIMARY_BUDGET,onset,mode)
217
+ late_n=sum(refs[n,ld]['mse_physical'] for ld in (9,12));late_r=sum(refs[r,ld]['mse_physical'] for ld in (9,12))
218
+ late_base=sum(refs['baseline',ld]['mse_physical'] for ld in (9,12))
219
+ early=max(refs[n,ld]['ratio_to_baseline'] for ld in (1,3))
220
+ tail=max(v['natural_regression_over_2pct'] for v in risk if v['onset']==onset and v['mode']==mode and v['lead'] in (9,12))
221
+ gain_base=100*(1-late_n/late_base);gain_rot=100*(1-late_n/late_r)
222
+ # Conditional diagnostic gate only. Even success does NOT authorize training without outer holdout.
223
+ pass_signal=(gain_base>=1.5 and gain_rot>=1.0 and early<=1.005 and tail<=.2)
224
+ signals.append({'onset':onset,'mode':mode,'late_gain_over_base_pct':gain_base,
225
+ 'late_gain_over_rotated_pct':gain_rot,'early_max_ratio':early,
226
+ 'worst_late_window_regression_fraction':tail,'candidate_diagnostic_pass':bool(pass_signal)})
227
+ return {'task':task,'decisions':signals,'any_diagnostic_candidate':any(v['candidate_diagnostic_pass'] for v in signals)}
228
+
229
+
230
+ def run(parent,r6zip,r7zip,r8zip,work,output,*,smoke=False,device='cpu'):
231
+ work=Path(work);output=Path(output);work.mkdir(parents=True,exist_ok=True);output.mkdir(parents=True,exist_ok=True)
232
+ validate_r6(r6zip);r7_report,_=validate_r7(r7zip);_,r8_ref=validate_r8(r8zip)
233
+ all_curves=[];all_samples=[];all_risks=[];cases=[];gate=[]
234
+ if smoke:
235
+ cfg=Config(profile='smoke');s1=make_synthetic_cache(cfg)
236
+ for sp in ('train','val','test'):
237
+ s1['splits'][sp]['h']=s1['splits'][sp]['h'].mean(dim=-2)
238
+ s1['splits'][sp]['y']=s1['splits'][sp]['y'].mean(dim=-2)
239
+ inputs=[('smoke_1d',s1),('smoke_2d',make_synthetic_cache(cfg))]
240
+ for name,cache in inputs:
241
+ core=UnifiedModel('V2_Ported_R5',1,8,12,4).to(device).eval().requires_grad_(False)
242
+ hch=1;heads=load_heads(r6zip,'burgers_1d' if name=='smoke_1d' else 'swe_2d',hch,device)
243
+ Q=orthogonal_matrix(int(core.model.skew.shape[0]),32207,device,torch.float32)
244
+ val=Windows(cache['splits']['val']['h'],cache['splits']['val']['y'],cache['mean'],cache['std'])
245
+ cur,smp,checks=evaluate(core,heads,Q,val,[0,1],device,smoke=True)
246
+ for row in cur:row['task']=name
247
+ for row in smp:row['task']=name
248
+ all_curves+=cur;all_samples+=smp;cases.append({'task':name,**checks})
249
+ status='PROGRAM_ONLY_SMOKE_NO_SCIENCE'
250
+ else:
251
+ if parent is None or sha(parent)!=PARENT_SHA:raise RuntimeError('MISSING_EXACT_B0_18_RUN_PARENT')
252
+ with zipfile.ZipFile(parent) as z:
253
+ m,contract=check_zip(z);cfg=Config(**m['config'])
254
+ for task in TASK_LIST:
255
+ core,cache,replay=make_cache_checked(task,z,cfg,work,device)
256
+ original=next(v for v in r7_report['tasks'] if v['task']==task)
257
+ if replay['checkpoint_sha256']!=original['replay']['checkpoint_sha256']:
258
+ raise RuntimeError('R9_PARENT_CHECKPOINT_CHANGED '+task)
259
+ val=Windows(cache['splits']['val']['h'],cache['splits']['val']['y'],cache['mean'],cache['std'])
260
+ heads=load_heads(r6zip,task,int(cache['splits']['val']['h'].shape[2]),device)
261
+ Q=orthogonal_matrix(int(core.model.skew.shape[0]),32207,device,torch.float32)
262
+ cur,smp,checks=evaluate(core,heads,Q,val,VAL_INDICES,device)
263
+ maxdelta=compare_r8_replay(smp,r8_ref,task)
264
+ for row in cur:row['task']=task
265
+ for row in smp:row['task']=task
266
+ risks=summarize_risk(smp,task)
267
+ local_gate=gate_task(cur,risks,task)
268
+ all_curves+=cur;all_samples+=smp;all_risks+=risks;gate.append(local_gate)
269
+ cases.append({'task':task,'replay':replay,'parity_vs_b2r8_max_mse_diff':maxdelta,**checks})
270
+ write_csv(output/'activation_lead_curves.csv',all_curves)
271
+ write_csv(output/'paired_window_mse.csv',all_samples)
272
+ write_csv(output/'paired_risk.csv',all_risks)
273
+ write_json(output/'progress.json',{'completed_tasks':[v['task'] for v in cases]})
274
+ if len(cases)!=3 or len(all_curves)!=(3*len(specs_for(False))*len(LEADS)):
275
+ raise RuntimeError('B2_R9_INCOMPLETE_OFFICIAL_COVERAGE')
276
+ status='FROZEN_ACTIVATION_TIMING_COMPLETED_NO_TRAINING'
277
+ write_csv(output/'activation_lead_curves.csv',all_curves)
278
+ write_csv(output/'paired_window_mse.csv',all_samples)
279
+ write_csv(output/'paired_risk.csv',all_risks)
280
+ write_json(output/'timing_gate.json',{'status':'DIAGNOSTIC_ONLY','registered_primary_budget':PRIMARY_BUDGET,
281
+ 'gates':gate,'no_training_authorized':True,'outer_holdout':False})
282
+ result={'experiment':'CIDM_v2_B2_R9_P0_CausalActivationTiming_EnergyMatched',
283
+ 'status':status,'original_training_steps':0,'new_controller_training_steps':0,
284
+ 'cases':cases,'total_curves':len(all_curves),'total_samples':len(all_samples),
285
+ 'total_risks':len(all_risks),'onsets':list(ONSETS),
286
+ 'budgets':[SECONDARY_BUDGET,PRIMARY_BUDGET],'primary_budget':PRIMARY_BUDGET,
287
+ 'original_parent_sha256':PARENT_SHA,'r6_zip_sha256':R6_REAL_ZIP_SHA,
288
+ 'r7_zip_sha256':R7_REAL_ZIP_SHA,'r8_zip_sha256':R8_REAL_ZIP_SHA,
289
+ 'truth_used_for_actions':False,'used_val_selected_parent':True,
290
+ 'outer_holdout':False,'schedules_are_deployment_eligible':False,
291
+ 'notes':['Predetermined lead-based schedules are causal but not a trained issue-time gate.',
292
+ 'Matched-energy onsets equalize sum of per-step squared relative perturbation budgets, not model response.',
293
+ 'All losses are scored after 12-step autonomous rollout; no future labels are input to any action.']}
294
+ write_json(output/'report.json',result)
295
+ return result