11"""Unit tests for the DFlash anchor-block attention mask."""
22
3- import pytest
43import torch
54from torch .nn .attention .flex_attention import create_mask
65
76from speculators .models .dflash .attention import create_anchor_block_mask_mod
87
98
9+ def _lengths_to_document_ids (lengths , total_seq_len ):
10+ document_ids = torch .full ((total_seq_len ,), - 1 , dtype = torch .long )
11+ document_ids [: lengths .sum ()] = torch .repeat_interleave (
12+ torch .arange (lengths .shape [0 ], dtype = torch .long ), lengths
13+ )
14+ return document_ids
15+
16+
1017def _reference_dense_from_mask_mod (
11- lengths ,
18+ document_ids ,
1219 total_seq_len ,
1320 anchor_positions ,
1421 block_size ,
@@ -17,7 +24,7 @@ def _reference_dense_from_mask_mod(
1724):
1825 """Ground truth: evaluate the flex mask_mod element-wise over the q x kv grid."""
1926 mask_mod , q_len , kv_len = create_anchor_block_mask_mod (
20- lengths = lengths ,
27+ document_ids = document_ids ,
2128 total_seq_len = total_seq_len ,
2229 anchor_positions = anchor_positions ,
2330 block_size = block_size ,
@@ -33,15 +40,15 @@ def _reference_dense_from_mask_mod(
3340
3441
3542def _dense_from_create_mask (
36- lengths ,
43+ document_ids ,
3744 total_seq_len ,
3845 anchor_positions ,
3946 block_size ,
4047 sliding_window = None ,
4148 sliding_window_non_causal = False ,
4249):
4350 mask_mod , q_len , kv_len = create_anchor_block_mask_mod (
44- lengths = lengths ,
51+ document_ids = document_ids ,
4552 total_seq_len = total_seq_len ,
4653 anchor_positions = anchor_positions ,
4754 block_size = block_size ,
@@ -54,7 +61,7 @@ def _dense_from_create_mask(
5461 H = None ,
5562 Q_LEN = q_len ,
5663 KV_LEN = kv_len ,
57- device = lengths .device ,
64+ device = document_ids .device ,
5865 )
5966
6067
@@ -63,13 +70,14 @@ def test_create_mask_matches_mask_mod_full_attention():
6370 device = torch .device ("cpu" )
6471 total_seq_len , block_size = 16 , 4
6572 lengths = torch .tensor ([10 , 6 ]) # two packed documents summing to total_seq_len
73+ document_ids = _lengths_to_document_ids (lengths , total_seq_len )
6674 anchor_positions = torch .tensor ([3 , 8 , 12 ])
6775
6876 ref = _reference_dense_from_mask_mod (
69- lengths , total_seq_len , anchor_positions , block_size
77+ document_ids , total_seq_len , anchor_positions , block_size
7078 )
7179 dense = _dense_from_create_mask (
72- lengths .to (device ), total_seq_len , anchor_positions , block_size
80+ document_ids .to (device ), total_seq_len , anchor_positions , block_size
7381 )
7482
7583 assert dense .shape == (1 , 1 , ref .shape [0 ], ref .shape [1 ])
@@ -81,18 +89,19 @@ def test_create_mask_matches_mask_mod_sliding_window():
8189 device = torch .device ("cpu" )
8290 total_seq_len , block_size = 16 , 4
8391 lengths = torch .tensor ([16 ]) # single document
92+ document_ids = _lengths_to_document_ids (lengths , total_seq_len )
8493 anchor_positions = torch .tensor ([5 , 9 , 14 ])
8594 sliding_window = 4
8695
8796 ref = _reference_dense_from_mask_mod (
88- lengths ,
97+ document_ids ,
8998 total_seq_len ,
9099 anchor_positions ,
91100 block_size ,
92101 sliding_window = sliding_window ,
93102 )
94103 dense = _dense_from_create_mask (
95- lengths .to (device ),
104+ document_ids .to (device ),
96105 total_seq_len ,
97106 anchor_positions ,
98107 block_size ,
@@ -107,32 +116,11 @@ def test_create_mask_each_query_sees_its_own_block():
107116 device = torch .device ("cpu" )
108117 total_seq_len , block_size = 12 , 4
109118 lengths = torch .tensor ([12 ])
119+ document_ids = _lengths_to_document_ids (lengths , total_seq_len )
110120 anchor_positions = torch .tensor ([2 , 7 , 10 ])
111121
112122 dense = _dense_from_create_mask (
113- lengths .to (device ), total_seq_len , anchor_positions , block_size
123+ document_ids .to (device ), total_seq_len , anchor_positions , block_size
114124 )
115125
116126 assert bool (dense [0 , 0 ].any (dim = - 1 ).all ())
117-
118-
119- def test_mask_mod_rejects_lengths_overflow ():
120- """sum(lengths) > total_seq_len raises."""
121- with pytest .raises (ValueError , match = "exceeds total_seq_len" ):
122- create_anchor_block_mask_mod (
123- lengths = torch .tensor ([10 , 10 ]), # sum = 20 > total_seq_len
124- total_seq_len = 16 ,
125- anchor_positions = torch .tensor ([3 , 8 ]),
126- block_size = 4 ,
127- )
128-
129-
130- def test_mask_mod_rejects_out_of_range_anchor ():
131- """anchor_positions outside [0, total_seq_len) raises."""
132- with pytest .raises (ValueError , match = "out of range" ):
133- create_anchor_block_mask_mod (
134- lengths = torch .tensor ([16 ]),
135- total_seq_len = 16 ,
136- anchor_positions = torch .tensor ([3 , 20 ]), # 20 >= total_seq_len
137- block_size = 4 ,
138- )
0 commit comments