2828D_HEAD = 16
2929
3030
31- def _empty_past_feeds (per_layer_n_heads : list , d_head : int ) -> dict :
32- feeds = {"past_len" : np .array (0 , dtype = np .int64 )}
33- for i , nh in enumerate (per_layer_n_heads ):
34- feeds [f"past_K_{ i } " ] = np .zeros ((0 , nh , d_head ), dtype = np .float32 )
35- feeds [f"past_V_{ i } " ] = np .zeros ((0 , nh , d_head ), dtype = np .float32 )
36- return feeds
37-
38-
3931def _discover_meta (session ):
40- # past_K_i is sequence-major (n_past , n_heads, d_head): heads on axis 1 .
32+ # past_K_i is sequence-major with a STATIC slot count: (S , n_heads, d_head).
4133 inputs = {inp .name : inp for inp in session .get_inputs ()}
4234 n_layers = sum (1 for name in inputs if name .startswith ("past_K_" ))
4335 per_layer_n_heads = [int (inputs [f"past_K_{ i } " ].shape [1 ]) for i in range (n_layers )]
4436 d_head = int (inputs ["past_K_0" ].shape [2 ])
45- return n_layers , per_layer_n_heads , d_head
37+ cache_stride = inputs ["past_K_0" ].shape [0 ]
38+ assert isinstance (cache_stride , int ), (
39+ f"past_K_0 first dim must be static, got { cache_stride !r} "
40+ )
41+ return n_layers , per_layer_n_heads , d_head , cache_stride
42+
43+
44+ def _zero_past (per_layer_n_heads : list , d_head : int , S : int ):
45+ """Full static-S zero-filled cache buffers, one (k, v) pair per layer."""
46+ k = [np .zeros ((S , nh , d_head ), dtype = np .float32 ) for nh in per_layer_n_heads ]
47+ v = [np .zeros ((S , nh , d_head ), dtype = np .float32 ) for nh in per_layer_n_heads ]
48+ return k , v
49+
50+
51+ def _feeds (inputs_np , past_k , past_v , base : int ) -> dict :
52+ """Static-cache feeds: full-S past buffers + cache_position for the rows."""
53+ n_new = int (inputs_np .shape [0 ])
54+ feeds = {
55+ "inputs" : inputs_np ,
56+ "cache_position" : np .arange (base , base + n_new , dtype = np .int64 ),
57+ }
58+ for i , (k , v ) in enumerate (zip (past_k , past_v )):
59+ feeds [f"past_K_{ i } " ] = k
60+ feeds [f"past_V_{ i } " ] = v
61+ return feeds
62+
63+
64+ def _write_deltas (past_k , past_v , results , base : int , n_new : int ):
65+ """Persist a run's delta outputs into the cache slots [base : base+n_new)."""
66+ n_layers = len (past_k )
67+ for i in range (n_layers ):
68+ past_k [i ][base : base + n_new ] = results [1 + 2 * i ]
69+ past_v [i ][base : base + n_new ] = results [1 + 2 * i + 1 ]
4670
4771
4872def _build_sample_graph ():
@@ -120,12 +144,11 @@ def test_headless_onnx_prefill_matches_compute():
120144 with tempfile .TemporaryDirectory () as tmpdir :
121145 onnx_path = _export (out , pos , tmpdir )
122146 session = onnxruntime .InferenceSession (onnx_path )
123- n_layers , per_layer_n_heads , d_head = _discover_meta (session )
147+ n_layers , per_layer_n_heads , d_head , S = _discover_meta (session )
124148
125149 inputs_np = torch .cat ([a_vals , b_vals ], dim = 1 ).numpy ().astype (np .float32 )
126- feeds = {"inputs" : inputs_np }
127- feeds .update (_empty_past_feeds (per_layer_n_heads , d_head ))
128- onnx_out = session .run (["outputs" ], feeds )[0 ]
150+ past_k , past_v = _zero_past (per_layer_n_heads , d_head , S )
151+ onnx_out = session .run (["outputs" ], _feeds (inputs_np , past_k , past_v , 0 ))[0 ]
129152
130153 assert np .allclose (
131154 onnx_out , expected , atol = 1e-3
@@ -150,32 +173,26 @@ def test_headless_onnx_chunked_decode_matches_full_prefill():
150173 with tempfile .TemporaryDirectory () as tmpdir :
151174 onnx_path = _export (out , pos , tmpdir )
152175 session = onnxruntime .InferenceSession (onnx_path )
153- n_layers , per_layer_n_heads , d_head = _discover_meta (session )
176+ n_layers , per_layer_n_heads , d_head , S = _discover_meta (session )
154177 out_names = ["outputs" ]
155178 for i in range (n_layers ):
156179 out_names += [f"delta_K_{ i } " , f"delta_V_{ i } " ]
157180
158181 # Full prefill (ground truth)
159- feeds = {"inputs" : inputs_np }
160- feeds .update (_empty_past_feeds (per_layer_n_heads , d_head ))
161- full_outputs = session .run (["outputs" ], feeds )[0 ]
162-
163- # Prefill 2 rows
164- feeds = {"inputs" : inputs_np [:2 ]}
165- feeds .update (_empty_past_feeds (per_layer_n_heads , d_head ))
166- results = session .run (out_names , feeds )
167- past_K = [results [1 + 2 * i ] for i in range (n_layers )]
168- past_V = [results [1 + 2 * i + 1 ] for i in range (n_layers )]
169-
170- # Decode a chunk of 3 rows (past_len=2, n_new=3)
171- feeds = {
172- "inputs" : inputs_np [2 :5 ],
173- "past_len" : np .array (2 , dtype = np .int64 ),
174- }
175- for i in range (n_layers ):
176- feeds [f"past_K_{ i } " ] = past_K [i ]
177- feeds [f"past_V_{ i } " ] = past_V [i ]
178- chunk_out = session .run (["outputs" ], feeds )[0 ]
182+ pk_full , pv_full = _zero_past (per_layer_n_heads , d_head , S )
183+ full_outputs = session .run (
184+ ["outputs" ], _feeds (inputs_np , pk_full , pv_full , 0 )
185+ )[0 ]
186+
187+ # Prefill 2 rows, persisting the deltas into slots [0:2)
188+ past_k , past_v = _zero_past (per_layer_n_heads , d_head , S )
189+ results = session .run (out_names , _feeds (inputs_np [:2 ], past_k , past_v , 0 ))
190+ _write_deltas (past_k , past_v , results , 0 , 2 )
191+
192+ # Decode a chunk of 3 rows (base=2, n_new=3)
193+ chunk_out = session .run (
194+ ["outputs" ], _feeds (inputs_np [2 :5 ], past_k , past_v , 2 )
195+ )[0 ]
179196
180197 assert np .allclose (full_outputs [2 :5 ], chunk_out , atol = 1e-3 ), (
181198 f"chunked decode diff: " f"{ np .abs (full_outputs [2 :5 ] - chunk_out ).max ():.6f} "
@@ -191,47 +208,43 @@ def test_headless_onnx_decode_step_matches_full_prefill():
191208 with tempfile .TemporaryDirectory () as tmpdir :
192209 onnx_path = _export (out , pos , tmpdir )
193210 session = onnxruntime .InferenceSession (onnx_path )
194- n_layers , per_layer_n_heads , d_head = _discover_meta (session )
211+ n_layers , per_layer_n_heads , d_head , S = _discover_meta (session )
195212 out_names = ["outputs" ]
196213 for i in range (n_layers ):
197214 out_names += [f"delta_K_{ i } " , f"delta_V_{ i } " ]
198215
199216 # Full prefill
200- feeds = {"inputs" : inputs_np }
201- feeds .update (_empty_past_feeds (per_layer_n_heads , d_head ))
202- full_outputs = session .run (["outputs" ], feeds )[0 ]
217+ pk_full , pv_full = _zero_past (per_layer_n_heads , d_head , S )
218+ full_outputs = session .run (
219+ ["outputs" ], _feeds (inputs_np , pk_full , pv_full , 0 )
220+ )[0 ]
203221
204222 # Prefill 4 rows + decode 1 row
205- feeds = {"inputs" : inputs_np [:4 ]}
206- feeds .update (_empty_past_feeds (per_layer_n_heads , d_head ))
207- results = session .run (out_names , feeds )
208- past_K = [results [1 + 2 * i ] for i in range (n_layers )]
209- past_V = [results [1 + 2 * i + 1 ] for i in range (n_layers )]
210-
211- feeds = {
212- "inputs" : inputs_np [4 :5 ],
213- "past_len" : np .array (4 , dtype = np .int64 ),
214- }
215- for i in range (n_layers ):
216- feeds [f"past_K_{ i } " ] = past_K [i ]
217- feeds [f"past_V_{ i } " ] = past_V [i ]
218- decode_out = session .run (["outputs" ], feeds )[0 ]
223+ past_k , past_v = _zero_past (per_layer_n_heads , d_head , S )
224+ results = session .run (out_names , _feeds (inputs_np [:4 ], past_k , past_v , 0 ))
225+ _write_deltas (past_k , past_v , results , 0 , 4 )
226+
227+ decode_out = session .run (
228+ ["outputs" ], _feeds (inputs_np [4 :5 ], past_k , past_v , 4 )
229+ )[0 ]
219230
220231 assert np .allclose (
221232 full_outputs [- 1 ], decode_out [0 ], atol = 1e-3
222233 ), f"decode seam diff: { np .abs (full_outputs [- 1 ] - decode_out [0 ]).max ():.6f} "
223234
224235
225236# ---------------------------------------------------------------------------
226- # Test 2b: the causal mask follows the BOUND past_K shape, not past_len.
227- # This is the load-bearing invariant of the in-place (delta) cache: the
228- # runtime binds past_K = cache[:cache_len] (a contiguous prefix of a larger
229- # owned buffer), so the mask geometry must come from the bound seq-dim, and
230- # binding the whole allocation would attend to the dead tail.
237+ # Test 2b: slots at positions > cache_position are INERT.
238+ # This is the load-bearing invariant of the static cache (the INVERSE of the
239+ # old dynamic-cache test that lived here): the runtime always binds the full
240+ # (S, nh, d_head) allocation, and the in-graph mask must give the
241+ # not-yet-committed tail slots softmax weight exactly 0.0 — finite garbage
242+ # in those slots cannot change the output. (Zero-init remains required in
243+ # production because 0 * NaN = NaN; this test uses finite garbage.)
231244# ---------------------------------------------------------------------------
232245
233246
234- def test_headless_onnx_mask_follows_bound_cache_shape ():
247+ def test_headless_onnx_static_tail_is_inert ():
235248 out , pos = _build_sample_graph ()
236249 a_vals = torch .tensor ([[3.0 ], [5.0 ], [- 2.0 ], [0.0 ], [4.0 ]])
237250 b_vals = torch .tensor ([[4.0 ], [- 1.0 ], [3.0 ], [7.0 ], [2.0 ]])
@@ -241,62 +254,43 @@ def test_headless_onnx_mask_follows_bound_cache_shape():
241254 with tempfile .TemporaryDirectory () as tmpdir :
242255 onnx_path = _export (out , pos , tmpdir )
243256 session = onnxruntime .InferenceSession (onnx_path )
244- n_layers , per_layer_n_heads , d_head = _discover_meta (session )
257+ n_layers , per_layer_n_heads , d_head , S = _discover_meta (session )
245258 out_names = ["outputs" ]
246259 for i in range (n_layers ):
247260 out_names += [f"delta_K_{ i } " , f"delta_V_{ i } " ]
248261
249- feeds = {"inputs" : inputs_np }
250- feeds .update (_empty_past_feeds (per_layer_n_heads , d_head ))
251- full_outputs = session .run (["outputs" ], feeds )[0 ]
252-
253- # Prefill n rows from empty: the deltas ARE the n-row cache (seq-major).
254- feeds = {"inputs" : inputs_np [:n ]}
255- feeds .update (_empty_past_feeds (per_layer_n_heads , d_head ))
256- results = session .run (out_names , feeds )
257- cache_k = [results [1 + 2 * i ] for i in range (n_layers )]
258- cache_v = [results [1 + 2 * i + 1 ] for i in range (n_layers )]
259-
260- def decode (past_k , past_v , past_len ):
261- feeds = {
262- "inputs" : inputs_np [n : n + 1 ],
263- "past_len" : np .array (past_len , dtype = np .int64 ),
264- }
265- for i in range (n_layers ):
266- feeds [f"past_K_{ i } " ] = past_k [i ]
267- feeds [f"past_V_{ i } " ] = past_v [i ]
268- return session .run (["outputs" ], feeds )[0 ]
269-
270- # (1) Exact n-row past reproduces row n of the full prefill.
271- exact = decode (cache_k , cache_v , n )
262+ pk_full , pv_full = _zero_past (per_layer_n_heads , d_head , S )
263+ full_outputs = session .run (
264+ ["outputs" ], _feeds (inputs_np , pk_full , pv_full , 0 )
265+ )[0 ]
266+
267+ # Prefill n rows into a zero cache.
268+ past_k , past_v = _zero_past (per_layer_n_heads , d_head , S )
269+ results = session .run (out_names , _feeds (inputs_np [:n ], past_k , past_v , 0 ))
270+ _write_deltas (past_k , past_v , results , 0 , n )
271+
272+ def decode (pk , pv ):
273+ return session .run (["outputs" ], _feeds (inputs_np [n : n + 1 ], pk , pv , n ))[0 ]
274+
275+ # (1) Zero-tail cache reproduces row n of the full prefill (the
276+ # prefill/decode seam under the static mask).
277+ exact = decode (past_k , past_v )
272278 assert np .allclose (full_outputs [n ], exact [0 ], atol = 1e-3 )
273279
274- # Allocate a larger buffer; [:n] is the real cache, the tail is garbage.
275- m = n + 3
276- big_k , big_v = [], []
280+ # (2) Fill every slot at positions > n with finite garbage. Slot n
281+ # itself is overwritten by the in-graph ScatterND (the decode row's
282+ # own key — the causal diagonal), so garbage there is inert too.
283+ garbage_k = [pk .copy () for pk in past_k ]
284+ garbage_v = [pv .copy () for pv in past_v ]
277285 for i in range (n_layers ):
278- nh = per_layer_n_heads [i ]
279- bk = np .full ((m , nh , d_head ), 7.0 , dtype = np .float32 )
280- bv = np .full ((m , nh , d_head ), - 3.0 , dtype = np .float32 )
281- bk [:n ] = cache_k [i ]
282- bv [:n ] = cache_v [i ]
283- big_k .append (bk )
284- big_v .append (bv )
285-
286- # (1') Binding the contiguous prefix VIEW big[:n] is bit-equal — the
287- # owned-cache in-place pattern is sound.
288- sliced = decode ([bk [:n ] for bk in big_k ], [bv [:n ] for bv in big_v ], n )
289- assert np .allclose (
290- exact [0 ], sliced [0 ], atol = 1e-6
291- ), f"prefix-slice bind diverged: { np .abs (exact [0 ] - sliced [0 ]).max ():.6e} "
292-
293- # (2) Binding the WHOLE buffer (past_len=n) attends to the dead tail —
294- # the mask spans m, not n — so it must differ. This is why the runtime
295- # binds cache[:cache_len], never the full allocation.
296- full_buf = decode (big_k , big_v , n )
297- assert not np .allclose (exact [0 ], full_buf [0 ], atol = 1e-3 ), (
298- "binding the full allocation matched the prefix bind — the mask is "
299- "not following the bound past_K shape"
286+ garbage_k [i ][n :] = 7.0
287+ garbage_v [i ][n :] = - 3.0
288+
289+ dirty = decode (garbage_k , garbage_v )
290+ assert np .allclose (exact [0 ], dirty [0 ], atol = 1e-6 ), (
291+ "garbage in masked static-cache slots changed the output "
292+ f"(max diff { np .abs (exact [0 ] - dirty [0 ]).max ():.6e} ) — the mask is "
293+ "not zeroing the tail weights exactly"
300294 )
301295
302296
@@ -345,13 +339,18 @@ def test_onnx_headless_module_empty_past_shape():
345339 with tempfile .TemporaryDirectory () as tmpdir :
346340 onnx_path = _export (out , pos , tmpdir )
347341 module = OnnxHeadlessModule (onnx_path )
348- past_K , past_V = module .empty_past ()
349- assert len (past_K ) == module ._n_layers
350- assert len (past_V ) == module ._n_layers
351- for i , K in enumerate (past_K ):
352- assert K .shape == (0 , module ._per_layer_n_heads [i ], module ._d_head )
353- for i , V in enumerate (past_V ):
354- assert V .shape == (0 , module ._per_layer_n_heads [i ], module ._d_head )
342+ past = module .empty_past ()
343+ S = module .cache_stride
344+ assert S == 32 # _export passes max_seq_len=32; cache_stride defaults to it
345+ assert past .length == 0
346+ assert len (past .k ) == module ._n_layers
347+ assert len (past .v ) == module ._n_layers
348+ for i , K in enumerate (past .k ):
349+ assert K .shape == (S , module ._per_layer_n_heads [i ], module ._d_head )
350+ assert (K == 0 ).all (), "static cache must be zero-initialized"
351+ for i , V in enumerate (past .v ):
352+ assert V .shape == (S , module ._per_layer_n_heads [i ], module ._d_head )
353+ assert (V == 0 ).all (), "static cache must be zero-initialized"
355354
356355
357356# ---------------------------------------------------------------------------
@@ -451,7 +450,7 @@ def test_headless_onnx_trim_heads_shrinks_kv_cache():
451450 with tempfile .TemporaryDirectory () as tmpdir :
452451 onnx_path = _export (out , pos , tmpdir , trim_heads = True )
453452 session = onnxruntime .InferenceSession (onnx_path )
454- _ , per_layer_n_heads , _ = _discover_meta (session )
453+ _ , per_layer_n_heads , _ , _ = _discover_meta (session )
455454
456455 assert per_layer_n_heads , "no layers discovered"
457456 assert all (1 <= nh <= max_heads for nh in per_layer_n_heads )
@@ -473,7 +472,7 @@ def test_headless_onnx_no_trim_preserves_full_width():
473472 with tempfile .TemporaryDirectory () as tmpdir :
474473 onnx_path = _export (out , pos , tmpdir , trim_heads = False )
475474 session = onnxruntime .InferenceSession (onnx_path )
476- _ , per_layer_n_heads , _ = _discover_meta (session )
475+ _ , per_layer_n_heads , _ , _ = _discover_meta (session )
477476
478477 assert per_layer_n_heads , "no layers discovered"
479478 assert all (nh == max_heads for nh in per_layer_n_heads ), (
@@ -519,16 +518,14 @@ def test_headless_onnx_trim_is_numerical_noop():
519518 sess_trim = onnxruntime .InferenceSession (trim_path )
520519 sess_notrim = onnxruntime .InferenceSession (notrim_path )
521520
522- _ , heads_trim , d_head = _discover_meta (sess_trim )
523- _ , heads_notrim , _ = _discover_meta (sess_notrim )
521+ _ , heads_trim , d_head , S_trim = _discover_meta (sess_trim )
522+ _ , heads_notrim , _ , S_notrim = _discover_meta (sess_notrim )
524523
525- feeds_trim = {"inputs" : inputs_np }
526- feeds_trim .update (_empty_past_feeds (heads_trim , d_head ))
527- out_trim = sess_trim .run (["outputs" ], feeds_trim )[0 ]
524+ pk , pv = _zero_past (heads_trim , d_head , S_trim )
525+ out_trim = sess_trim .run (["outputs" ], _feeds (inputs_np , pk , pv , 0 ))[0 ]
528526
529- feeds_notrim = {"inputs" : inputs_np }
530- feeds_notrim .update (_empty_past_feeds (heads_notrim , d_head ))
531- out_notrim = sess_notrim .run (["outputs" ], feeds_notrim )[0 ]
527+ pk , pv = _zero_past (heads_notrim , d_head , S_notrim )
528+ out_notrim = sess_notrim .run (["outputs" ], _feeds (inputs_np , pk , pv , 0 ))[0 ]
532529
533530 assert np .allclose (out_trim , out_notrim , atol = 1e-4 ), (
534531 f"trim changed the output: max diff "
0 commit comments