main
py 351 lines 11.6 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 import torch
5
6 from vllm.triton_utils import tl, triton
7
8 from .mrope import MRotaryEmbedding
9
10
11 def _validate_bailing_mrope_config(
12 head_size: int,
13 rotary_dim: int,
14 mrope_section: list[int],
15 ) -> int:
16 if rotary_dim <= 0 or rotary_dim % 2 != 0 or rotary_dim > head_size:
17 raise ValueError(
18 "Bailing M-RoPE requires an even rotary_dim in (0, head_size], "
19 f"got rotary_dim={rotary_dim} and head_size={head_size}"
20 )
21 if len(mrope_section) != 3:
22 raise ValueError(
23 "Bailing M-RoPE expects [T, H, W] sections, "
24 f"got mrope_section={mrope_section}"
25 )
26
27 temporal_size, height_size, width_size = mrope_section
28 if any(section_size < 0 for section_size in mrope_section):
29 raise ValueError(
30 "Bailing M-RoPE sections must be non-negative, "
31 f"got mrope_section={mrope_section}"
32 )
33 if height_size != width_size:
34 raise ValueError(
35 "Bailing M-RoPE requires equal H and W sections, "
36 f"got height_size={height_size} and width_size={width_size}"
37 )
38 spatial_size = height_size + width_size
39 if spatial_size + temporal_size != rotary_dim // 2:
40 raise ValueError(
41 "Bailing M-RoPE sections must sum to rotary_dim // 2, got "
42 f"mrope_section={mrope_section} and rotary_dim={rotary_dim}"
43 )
44 return spatial_size
45
46
47 def _validate_bailing_positions(
48 positions: torch.Tensor,
49 *,
50 require_2d: bool = False,
51 ) -> None:
52 if positions.dtype not in (torch.int32, torch.int64):
53 raise ValueError(
54 "Bailing M-RoPE positions must have int32 or int64 dtype, got "
55 f"dtype={positions.dtype}"
56 )
57 if positions.ndim == 1 and not require_2d:
58 return
59 if positions.ndim == 2:
60 if positions.shape[0] == 3:
61 return
62 raise ValueError(
63 "Bailing M-RoPE expects T/H/W positions with shape [3, N], "
64 f"got shape={tuple(positions.shape)}"
65 )
66 if require_2d:
67 raise ValueError(
68 "Bailing M-RoPE expects T/H/W positions with shape [3, N], "
69 f"got shape={tuple(positions.shape)}"
70 )
71 raise ValueError(
72 f"Bailing M-RoPE positions must be 1D or 2D, got shape={tuple(positions.shape)}"
73 )
74
75
76 @triton.jit
77 def _triton_bailing_mrope_forward(
78 q_ptr,
79 k_ptr,
80 positions_ptr,
81 cos_sin_cache_ptr,
82 q_stride_token,
83 q_stride_head,
84 q_stride_dim,
85 k_stride_token,
86 k_stride_head,
87 k_stride_dim,
88 positions_stride_axis,
89 positions_stride_token,
90 cache_stride_position,
91 cache_stride_dim,
92 n_qh: tl.constexpr,
93 n_kh: tl.constexpr,
94 rotary_dim: tl.constexpr,
95 pad_n_qh: tl.constexpr,
96 pad_n_kh: tl.constexpr,
97 pad_half_rotary_dim: tl.constexpr,
98 spatial_size: tl.constexpr,
99 ):
100 """Apply GPT-J-style H/W-interleaved, T-tail M-RoPE in place."""
101 token_idx = tl.program_id(0)
102 position_offset = token_idx * positions_stride_token
103 temporal_position = tl.load(positions_ptr + position_offset).to(tl.int64)
104 height_position = tl.load(
105 positions_ptr + positions_stride_axis + position_offset
106 ).to(tl.int64)
107 width_position = tl.load(
108 positions_ptr + 2 * positions_stride_axis + position_offset
109 ).to(tl.int64)
110
111 frequency_offsets = tl.arange(0, pad_half_rotary_dim)
112 frequency_mask = frequency_offsets < rotary_dim // 2
113 spatial_position = tl.where(
114 frequency_offsets % 2 == 0,
115 height_position,
116 width_position,
117 )
118 selected_position = tl.where(
119 frequency_offsets < spatial_size,
120 spatial_position,
121 temporal_position,
122 )
123 cache_offsets = (
124 selected_position * cache_stride_position + frequency_offsets * cache_stride_dim
125 )
126 cos_row = tl.load(
127 cos_sin_cache_ptr + cache_offsets,
128 mask=frequency_mask,
129 other=0.0,
130 ).to(tl.float32)
131 sin_row = tl.load(
132 cos_sin_cache_ptr + cache_offsets + (rotary_dim // 2) * cache_stride_dim,
133 mask=frequency_mask,
134 other=0.0,
135 ).to(tl.float32)
136
137 q_head_offsets = tl.arange(0, pad_n_qh)[:, None]
138 k_head_offsets = tl.arange(0, pad_n_kh)[:, None]
139 pair_offsets = frequency_offsets[None, :]
140 q_mask = (q_head_offsets < n_qh) & frequency_mask[None, :]
141 k_mask = (k_head_offsets < n_kh) & frequency_mask[None, :]
142
143 q_base = q_ptr + token_idx * q_stride_token
144 q_even_offsets = q_head_offsets * q_stride_head + 2 * pair_offsets * q_stride_dim
145 q_odd_offsets = q_even_offsets + q_stride_dim
146 q_even = tl.load(q_base + q_even_offsets, mask=q_mask, other=0.0).to(tl.float32)
147 q_odd = tl.load(q_base + q_odd_offsets, mask=q_mask, other=0.0).to(tl.float32)
148 q_even_cos = (q_even * cos_row[None, :]).to(q_ptr.type.element_ty).to(tl.float32)
149 q_odd_sin = (q_odd * sin_row[None, :]).to(q_ptr.type.element_ty).to(tl.float32)
150 q_odd_cos = (q_odd * cos_row[None, :]).to(q_ptr.type.element_ty).to(tl.float32)
151 q_even_sin = (q_even * sin_row[None, :]).to(q_ptr.type.element_ty).to(tl.float32)
152 tl.store(
153 q_base + q_even_offsets,
154 q_even_cos - q_odd_sin,
155 mask=q_mask,
156 )
157 tl.store(
158 q_base + q_odd_offsets,
159 q_odd_cos + q_even_sin,
160 mask=q_mask,
161 )
162
163 k_base = k_ptr + token_idx * k_stride_token
164 k_even_offsets = k_head_offsets * k_stride_head + 2 * pair_offsets * k_stride_dim
165 k_odd_offsets = k_even_offsets + k_stride_dim
166 k_even = tl.load(k_base + k_even_offsets, mask=k_mask, other=0.0).to(tl.float32)
167 k_odd = tl.load(k_base + k_odd_offsets, mask=k_mask, other=0.0).to(tl.float32)
168 k_even_cos = (k_even * cos_row[None, :]).to(k_ptr.type.element_ty).to(tl.float32)
169 k_odd_sin = (k_odd * sin_row[None, :]).to(k_ptr.type.element_ty).to(tl.float32)
170 k_odd_cos = (k_odd * cos_row[None, :]).to(k_ptr.type.element_ty).to(tl.float32)
171 k_even_sin = (k_even * sin_row[None, :]).to(k_ptr.type.element_ty).to(tl.float32)
172 tl.store(
173 k_base + k_even_offsets,
174 k_even_cos - k_odd_sin,
175 mask=k_mask,
176 )
177 tl.store(
178 k_base + k_odd_offsets,
179 k_odd_cos + k_even_sin,
180 mask=k_mask,
181 )
182
183
184 def triton_bailing_mrope(
185 query: torch.Tensor,
186 key: torch.Tensor,
187 positions: torch.Tensor,
188 cos_sin_cache: torch.Tensor,
189 spatial_size: int,
190 head_size: int,
191 rotary_dim: int,
192 ) -> tuple[torch.Tensor, torch.Tensor]:
193 """Fused Bailing M-RoPE for packed or strided Q/K."""
194 _validate_bailing_positions(positions, require_2d=True)
195 num_tokens = positions.shape[-1]
196 if num_tokens == 0:
197 return query, key
198
199 query_3d = query.view(num_tokens, -1, head_size)
200 key_3d = key.view(num_tokens, -1, head_size)
201 n_q_heads = query_3d.shape[1]
202 n_k_heads = key_3d.shape[1]
203 pad_n_q_heads = triton.next_power_of_2(n_q_heads)
204 pad_n_k_heads = triton.next_power_of_2(n_k_heads)
205 pad_half_rotary_dim = triton.next_power_of_2(rotary_dim // 2)
206 _triton_bailing_mrope_forward[(num_tokens,)](
207 query_3d,
208 key_3d,
209 positions,
210 cos_sin_cache,
211 query_3d.stride(0),
212 query_3d.stride(1),
213 query_3d.stride(2),
214 key_3d.stride(0),
215 key_3d.stride(1),
216 key_3d.stride(2),
217 positions.stride(0),
218 positions.stride(1),
219 cos_sin_cache.stride(0),
220 cos_sin_cache.stride(1),
221 n_q_heads,
222 n_k_heads,
223 rotary_dim,
224 pad_n_q_heads,
225 pad_n_k_heads,
226 pad_half_rotary_dim,
227 spatial_size,
228 num_warps=4,
229 )
230 return query, key
231
232
233 class BailingMRotaryEmbedding(MRotaryEmbedding):
234 """Bailing M-RoPE with alternating H/W frequencies and a temporal tail."""
235
236 def __init__(
237 self,
238 head_size: int,
239 rotary_dim: int,
240 max_position_embeddings: int,
241 base: float,
242 is_neox_style: bool,
243 dtype: torch.dtype,
244 mrope_section: list[int],
245 ) -> None:
246 self.spatial_size = _validate_bailing_mrope_config(
247 head_size, rotary_dim, mrope_section
248 )
249 super().__init__(
250 head_size,
251 rotary_dim,
252 max_position_embeddings,
253 base,
254 is_neox_style,
255 dtype,
256 mrope_section=mrope_section,
257 )
258
259 def select_cos_sin(
260 self,
261 positions: torch.Tensor,
262 cos_sin_cache: torch.Tensor | None = None,
263 ) -> tuple[torch.Tensor, torch.Tensor]:
264 _validate_bailing_positions(positions)
265 if cos_sin_cache is None:
266 cos_sin_cache = self.cos_sin_cache
267 cos_sin = cos_sin_cache[positions.to(torch.long)]
268 cos, sin = cos_sin.chunk(2, dim=-1)
269 if positions.ndim == 1:
270 return cos, sin
271
272 spatial_mask = torch.arange(self.spatial_size, device=cos.device) % 2 == 0
273 spatial_mask = spatial_mask.unsqueeze(0)
274 spatial_cos = torch.where(
275 spatial_mask,
276 cos[1, :, : self.spatial_size],
277 cos[2, :, : self.spatial_size],
278 )
279 spatial_sin = torch.where(
280 spatial_mask,
281 sin[1, :, : self.spatial_size],
282 sin[2, :, : self.spatial_size],
283 )
284 temporal_slice = slice(self.spatial_size, self.rotary_dim // 2)
285 return (
286 torch.cat((spatial_cos, cos[0, :, temporal_slice]), dim=-1),
287 torch.cat((spatial_sin, sin[0, :, temporal_slice]), dim=-1),
288 )
289
290 def forward_native(
291 self,
292 positions: torch.Tensor,
293 query: torch.Tensor,
294 key: torch.Tensor | None = None,
295 offsets: torch.Tensor | None = None,
296 ) -> tuple[torch.Tensor, torch.Tensor | None]:
297 if key is None:
298 raise ValueError("Bailing M-RoPE requires key states")
299
300 cos_sin_cache = self._match_cos_sin_cache_dtype(query)
301 cos, sin = self.select_cos_sin(positions, cos_sin_cache)
302 num_tokens = positions.shape[-1]
303 query_shape = query.shape
304 query = query.view(num_tokens, -1, self.head_size)
305 query_rot = self.apply_rotary_emb.forward_native(
306 query[..., : self.rotary_dim],
307 cos,
308 sin,
309 )
310 query = torch.cat((query_rot, query[..., self.rotary_dim :]), dim=-1)
311
312 key_shape = key.shape
313 key = key.view(num_tokens, -1, self.head_size)
314 key_rot = self.apply_rotary_emb.forward_native(
315 key[..., : self.rotary_dim],
316 cos,
317 sin,
318 )
319 key = torch.cat((key_rot, key[..., self.rotary_dim :]), dim=-1)
320 return query.reshape(query_shape), key.reshape(key_shape)
321
322 def forward_cuda(
323 self,
324 positions: torch.Tensor,
325 query: torch.Tensor,
326 key: torch.Tensor | None = None,
327 offsets: torch.Tensor | None = None,
328 ) -> tuple[torch.Tensor, torch.Tensor | None]:
329 if positions.ndim == 2 and not self.is_neox_style:
330 if key is None:
331 raise ValueError("Bailing M-RoPE requires key states")
332 cos_sin_cache = self._match_cos_sin_cache_dtype(query)
333 return triton_bailing_mrope(
334 query,
335 key,
336 positions,
337 cos_sin_cache,
338 self.spatial_size,
339 self.head_size,
340 self.rotary_dim,
341 )
342 return self.forward_native(positions, query, key, offsets)
343
344 def forward_cpu(
345 self,
346 positions: torch.Tensor,
347 query: torch.Tensor,
348 key: torch.Tensor | None = None,
349 offsets: torch.Tensor | None = None,
350 ) -> tuple[torch.Tensor, torch.Tensor | None]:
351 return self.forward_native(positions, query, key, offsets)