6262from __future__ import annotations
6363
6464import argparse
65+ import dataclasses
6566import json
6667import math
6768import random
@@ -168,129 +169,128 @@ def main() -> int:
168169
169170 # ---------- NIAH dataset ----------
170171 samples : List [NIAHSample ] = make_niah_dataset (
171- tokenizer ,
172172 n_samples = args .n_samples ,
173173 haystack_min_lines = args .haystack_min_lines ,
174174 haystack_max_lines = args .haystack_max_lines ,
175175 seed = args .seed ,
176176 )
177- print (f"[k3-integrated] generated { len (samples )} NIAH samples" , file = sys .stderr )
177+
178+ # Encode prompts via chat template (ADR 0008 §2.4: the runtime is
179+ # template-free; the harness applies the template), matching the
180+ # K1.E NIAH harness convention.
181+ def encode_chat (prompt_text : str ) -> torch .Tensor :
182+ messages = [{"role" : "user" , "content" : prompt_text }]
183+ ids = tokenizer .apply_chat_template (
184+ messages , add_generation_prompt = True , tokenize = True ,
185+ return_tensors = "pt" ,
186+ )
187+ if isinstance (ids , list ):
188+ ids = torch .tensor ([ids ])
189+ return ids .to (device )
190+
191+ sample_ids = [encode_chat (s .prompt_text ) for s in samples ]
192+ seq_lens = [int (t .size (1 )) for t in sample_ids ]
193+ eos_id = tokenizer .eos_token_id
194+ print (
195+ f"[k3-integrated] dataset: { len (samples )} samples, prompt token len "
196+ f"min={ min (seq_lens )} max={ max (seq_lens )} "
197+ f"mean={ sum (seq_lens ) // len (seq_lens )} " ,
198+ file = sys .stderr ,
199+ )
200+
201+ def _greedy (decode_step ) -> Tuple [List [str ], List [float ], List [int ]]:
202+ """Run greedy decode over all samples with a per-step callable
203+ ``decode_step(cur_ids) -> logits[0, -1]``. Returns per-sample
204+ (decoded_text, latency_s, decode_token_count)."""
205+ decoded_all : List [str ] = []
206+ lat_all : List [float ] = []
207+ tok_all : List [int ] = []
208+ for i in range (len (samples )):
209+ cur = sample_ids [i ]
210+ gen : List [int ] = []
211+ t0 = time .perf_counter ()
212+ for _ in range (args .max_new_tokens ):
213+ last_logits = decode_step (cur )
214+ nxt = int (torch .argmax (last_logits ).item ())
215+ gen .append (nxt )
216+ if eos_id is not None and nxt == eos_id :
217+ break
218+ cur = torch .cat (
219+ [cur , torch .tensor ([[nxt ]], device = device , dtype = torch .long )],
220+ dim = 1 ,
221+ )
222+ lat_all .append (time .perf_counter () - t0 )
223+ decoded_all .append (tokenizer .decode (gen , skip_special_tokens = True ))
224+ tok_all .append (len (gen ))
225+ print (
226+ f"[k3-integrated] sample { i } : T={ seq_lens [i ]} tokens={ len (gen )} "
227+ f"decoded[:48]={ decoded_all [- 1 ][:48 ]!r} " ,
228+ file = sys .stderr ,
229+ )
230+ return decoded_all , lat_all , tok_all
178231
179232 # ---------- Run integrated cross-model verifier ----------
180- cross_results : List [ Dict [ str , Any ]] = []
181- cross_attn_window : List [ Dict [ str , Any ]] = []
233+ print ( "[k3-integrated] running K3 cross-model verifier (f_θ restoration)" ,
234+ file = sys . stderr , flush = True )
182235 reset_memory_peak (device )
183236
184- for i , sample in enumerate (samples ):
185- input_ids = torch .tensor (
186- [sample .input_ids ], dtype = torch .long , device = device ,
187- )
188- T = int (input_ids .size (1 ))
189-
190- # Run cross-model verifier
191- outputs = cross_verifier .forward (
192- input_ids ,
237+ def _cross_step (cur ):
238+ out = cross_verifier .forward (
239+ cur ,
193240 apply_rotary_pos_emb = apply_rotary_pos_emb ,
194241 eager_attention_forward = eager_attention_forward ,
195242 all_attention_functions = ALL_ATTENTION_FUNCTIONS ,
196243 )
197- # Greedy decode max_new_tokens after the prompt
198- cur = input_ids
199- gen_tokens : List [int ] = []
200- for _ in range (args .max_new_tokens ):
201- out = cross_verifier .forward (
202- cur ,
203- apply_rotary_pos_emb = apply_rotary_pos_emb ,
204- eager_attention_forward = eager_attention_forward ,
205- all_attention_functions = ALL_ATTENTION_FUNCTIONS ,
206- )
207- nxt = int (torch .argmax (out .logits [0 , - 1 ]).item ())
208- gen_tokens .append (nxt )
209- cur = torch .cat (
210- [cur , torch .tensor ([[nxt ]], device = device , dtype = torch .long )],
211- dim = 1 ,
212- )
213-
214- decoded = tokenizer .decode (gen_tokens , skip_special_tokens = True )
215- is_correct = recall_predicate (decoded , sample )
216- cross_results .append ({
217- "sample_idx" : i ,
218- "decoded" : decoded [:200 ],
219- "is_correct" : is_correct ,
220- "seq_len" : T ,
221- })
222-
223- # effective_attention_fraction at the last query position
224- attn_w = compute_effective_attention_window (
225- seq_len = T ,
226- sink_size = args .sink_size ,
227- window_size = args .window_size ,
228- evicted_kv_restored = True , # K3 architecture: evicted K/V are restored
229- structural_constraint = (
230- f"causal_with_dlm_reconstruction "
231- f"(local_cache=sink={ args .sink_size } +window={ args .window_size } , "
232- f"k3_cross_model_f_theta)"
233- ),
234- )
235- cross_attn_window .append (attn_w )
244+ return out .logits [0 , - 1 ]
236245
237- print (
238- f"[k3-integrated] sample { i } : T={ T } correct={ is_correct } "
239- f"decoded[:60]={ decoded [:60 ]!r} " ,
240- file = sys .stderr ,
241- )
242-
243- # ---------- Aggregate ----------
244- cross_recall = aggregate_recall (cross_results )
245- cross_attn_agg = aggregate_attention_window_metrics (cross_attn_window )
246- cross_mem = record_memory (device , label = "after_k3_cross_model" )
246+ cross_decoded , cross_lat , cross_tok = _greedy (_cross_step )
247+ cross_res = aggregate_recall (
248+ "k3_cross_model" , samples , cross_decoded , cross_lat , cross_tok ,
249+ )
250+ cross_mem = record_memory (device )
251+ cross_attn_agg = aggregate_attention_window_metrics (
252+ "v04_dlm_restored" ,
253+ prompt_token_lens = seq_lens ,
254+ sink_size = args .sink_size ,
255+ window_size = args .window_size ,
256+ )
257+ print (
258+ f"[k3-integrated] cross-model recall={ cross_res .recall :.3f} "
259+ f"({ cross_res .samples_correct } /{ cross_res .samples_total } )" ,
260+ file = sys .stderr ,
261+ )
247262
248263 # ---------- Optional oracle baseline ----------
249- oracle_results = None
250- oracle_recall = None
264+ oracle_res = None
251265 oracle_mem = None
252266 if not args .skip_oracle :
253267 print ("[k3-integrated] running full-attention oracle baseline" ,
254268 file = sys .stderr , flush = True )
255269 reset_memory_peak (device )
256- oracle_results = []
257- for i , sample in enumerate (samples ):
258- input_ids = torch .tensor (
259- [sample .input_ids ], dtype = torch .long , device = device ,
260- )
261- cur = input_ids
262- gen_tokens = []
263- for _ in range (args .max_new_tokens ):
264- with torch .no_grad ():
265- out = verifier (input_ids = cur , use_cache = False )
266- nxt = int (torch .argmax (out .logits [0 , - 1 ]).item ())
267- gen_tokens .append (nxt )
268- cur = torch .cat (
269- [cur , torch .tensor ([[nxt ]], device = device , dtype = torch .long )],
270- dim = 1 ,
271- )
272- decoded = tokenizer .decode (gen_tokens , skip_special_tokens = True )
273- is_correct = recall_predicate (decoded , sample )
274- oracle_results .append ({
275- "sample_idx" : i ,
276- "decoded" : decoded [:200 ],
277- "is_correct" : is_correct ,
278- "seq_len" : int (input_ids .size (1 )),
279- })
280- print (
281- f"[k3-integrated] oracle sample { i } : correct={ is_correct } " ,
282- file = sys .stderr ,
283- )
284- oracle_recall = aggregate_recall (oracle_results )
285- oracle_mem = record_memory (device , label = "after_oracle" )
270+
271+ def _oracle_step (cur ):
272+ with torch .no_grad ():
273+ out = verifier (input_ids = cur , use_cache = False )
274+ return out .logits [0 , - 1 ]
275+
276+ oracle_decoded , oracle_lat , oracle_tok = _greedy (_oracle_step )
277+ oracle_res = aggregate_recall (
278+ "oracle" , samples , oracle_decoded , oracle_lat , oracle_tok ,
279+ )
280+ oracle_mem = record_memory (device )
281+ print (
282+ f"[k3-integrated] oracle recall={ oracle_res .recall :.3f} "
283+ f"({ oracle_res .samples_correct } /{ oracle_res .samples_total } )" ,
284+ file = sys .stderr ,
285+ )
286286
287287 # ---------- Build report ----------
288288 recall_delta = (
289- abs (cross_recall ["recall" ] - oracle_recall ["recall" ])
290- if oracle_recall else None
289+ abs (cross_res .recall - oracle_res .recall ) if oracle_res else None
291290 )
291+ eff_frac_mean = cross_attn_agg .get ("effective_attention_fraction_mean" )
292292 report = {
293- "schema_version" : 1 ,
293+ "schema_version" : 2 ,
294294 "kind" : "k3_integrated_niah_acceptance" ,
295295 "config" : {
296296 "verifier_id" : args .verifier_id ,
@@ -305,18 +305,11 @@ def main() -> int:
305305 "max_new_tokens" : args .max_new_tokens ,
306306 "seed" : args .seed ,
307307 "skip_oracle" : bool (args .skip_oracle ),
308+ "prompt_token_lens" : seq_lens ,
308309 },
309310 "results" : {
310- "k3_cross_model" : {
311- "name" : "k3_cross_model" ,
312- ** cross_recall ,
313- "per_sample" : cross_results ,
314- },
315- ** (
316- {"oracle" : {"name" : "oracle" , ** oracle_recall ,
317- "per_sample" : oracle_results }}
318- if oracle_recall else {}
319- ),
311+ "k3_cross_model" : dataclasses .asdict (cross_res ),
312+ ** ({"oracle" : dataclasses .asdict (oracle_res )} if oracle_res else {}),
320313 },
321314 "attention_window" : {
322315 "per_config" : {"k3_cross_model" : cross_attn_agg },
@@ -326,9 +319,9 @@ def main() -> int:
326319 ** ({"oracle" : oracle_mem } if oracle_mem else {}),
327320 },
328321 "gate" : {
329- "architectural_correctness" : (
330- cross_attn_agg . get ( "effective_attention_fraction_mean" ) == 1.0
331- ) ,
322+ "architectural_correctness" : (eff_frac_mean == 1.0 ),
323+ "recall_cross_model" : cross_res . recall ,
324+ "recall_oracle" : oracle_res . recall if oracle_res else None ,
332325 "recall_delta_vs_oracle_pp" : (
333326 recall_delta * 100 if recall_delta is not None else None
334327 ),
@@ -343,31 +336,28 @@ def main() -> int:
343336 )
344337 out_path .parent .mkdir (parents = True , exist_ok = True )
345338 out_path .write_text (json .dumps (report , indent = 2 ))
339+
340+ print (f"\n [k3-integrated] DONE." , file = sys .stderr )
346341 print (
347- f"\n [k3-integrated] DONE.\n "
348- f" cross-model recall: { cross_recall ['recall' ]:.3f} "
349- f"({ cross_recall ['samples_correct' ]} /{ cross_recall ['samples_total' ]} )\n "
350- f" oracle recall: "
351- f"{ oracle_recall ['recall' ]:.3f} ({ oracle_recall ['samples_correct' ]} /{ oracle_recall ['samples_total' ]} )"
352- if oracle_recall else
353- f"\n [k3-integrated] DONE.\n "
354- f" cross-model recall: { cross_recall ['recall' ]:.3f} "
355- f"({ cross_recall ['samples_correct' ]} /{ cross_recall ['samples_total' ]} )\n "
356- f" oracle: skipped" ,
342+ f" cross-model recall: { cross_res .recall :.3f} "
343+ f"({ cross_res .samples_correct } /{ cross_res .samples_total } )" ,
357344 file = sys .stderr ,
358345 )
359- if recall_delta is not None :
360- print (f" |delta vs oracle|: { recall_delta * 100 :.2f} pp" , file = sys .stderr )
346+ if oracle_res is not None :
347+ print (
348+ f" oracle recall: { oracle_res .recall :.3f} "
349+ f"({ oracle_res .samples_correct } /{ oracle_res .samples_total } )" ,
350+ file = sys .stderr ,
351+ )
352+ print (f" |delta vs oracle|: { recall_delta * 100 :.2f} pp" , file = sys .stderr )
361353 print (
362354 f" ADR §11.8 1a gate (≤ 5pp): "
363355 f"{ 'PASS' if recall_delta <= 0.05 else 'FAIL' } " ,
364356 file = sys .stderr ,
365357 )
366- print (
367- f" effective_attention_fraction: "
368- f"{ cross_attn_agg .get ('effective_attention_fraction_mean' )} " ,
369- file = sys .stderr ,
370- )
358+ else :
359+ print (" oracle: skipped" , file = sys .stderr )
360+ print (f" effective_attention_fraction: { eff_frac_mean } " , file = sys .stderr )
371361 print (f" Report: { out_path } " , file = sys .stderr )
372362 return 0
373363
0 commit comments