"""Primitive rotary embedding ops: _rotate_neox, _rotate_gptj, _apply_rotary_emb, apply_rotary_pos_emb variants.""" from __future__ import annotations from typing import Tuple import torch from sglang.srt.utils import cpu_has_amx_support, get_compiler_backend, is_cpu, is_npu _is_npu = is_npu() _is_cpu = is_cpu() _is_cpu_amx_available = cpu_has_amx_support() if _is_npu: import torch_npu NPU_ROTARY_MUL_MAX_NUM_HEADS = 1000 NPU_ROTARY_MUL_MAX_HEAD_SIZE = 896 def rotate_neox(x: torch.Tensor) -> torch.Tensor: x1 = x[..., : x.shape[-1] // 2] x2 = x[..., x.shape[-1] // 2 :] return torch.cat((-x2, x1), dim=-1) def rotate_gptj(x: torch.Tensor) -> torch.Tensor: x1 = x[..., ::2] x2 = x[..., 1::2] x = torch.stack((-x2, x1), dim=-1) return x.flatten(-2) def apply_rotary_emb( x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor, is_neox_style: bool, ) -> torch.Tensor: """ Args: x: [num_tokens, num_heads, head_size] cos: [num_tokens, head_size // 2] sin: [num_tokens, head_size // 2] is_neox_style: Whether to use the Neox-style or GPT-J-style rotary positional embeddings. """ cos = cos.unsqueeze(-2).to(x.dtype) sin = sin.unsqueeze(-2).to(x.dtype) if is_neox_style: x1, x2 = torch.chunk(x, 2, dim=-1) else: x1 = x[..., ::2] x2 = x[..., 1::2] o1 = x1 * cos - x2 * sin o2 = x2 * cos + x1 * sin if is_neox_style: return torch.cat((o1, o2), dim=-1) else: return torch.stack((o1, o2), dim=-1).flatten(-2) # Copied from transformers def rotate_half(x): """Rotates half the hidden dims of the input.""" x1 = x[..., : x.shape[-1] // 2] x2 = x[..., x.shape[-1] // 2 :] return torch.cat((-x2, x1), dim=-1) @torch.compile(dynamic=True, backend=get_compiler_backend()) def apply_rotary_pos_emb_native( q: torch.Tensor, k: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor, unsqueeze_dim=1, ) -> Tuple[torch.Tensor, torch.Tensor]: orig_q_dtype = q.dtype orig_k_dtype = k.dtype q, k = q.float(), k.float() # embedding is performed in float cos = cos.unsqueeze(unsqueeze_dim).float() sin = sin.unsqueeze(unsqueeze_dim).float() q_embed = (q * cos) + (rotate_half(q) * sin) k_embed = (k * cos) + (rotate_half(k) * sin) q_embed = q_embed.to(orig_q_dtype) k_embed = k_embed.to(orig_k_dtype) return q_embed, k_embed def apply_rotary_pos_emb_npu( q: torch.Tensor, k: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor, unsqueeze_dim=1, ) -> Tuple[torch.Tensor, torch.Tensor]: """Ascend implementation equivalent to apply_rotary_pos_emb_native. Args: q: [num_tokens, num_heads, head_size] k: [num_tokens, num_kv_heads, head_size] cos: [num_tokens, head_size] sin: [num_tokens, head_size] """ if ( cos.dim() != 2 or q.dim() != 3 or q.shape[1] >= NPU_ROTARY_MUL_MAX_NUM_HEADS or q.shape[2] >= NPU_ROTARY_MUL_MAX_HEAD_SIZE ): # Note: num_heads and head_size of q must be less than 1000 and 896, respectively return apply_rotary_pos_emb_native(q, k, cos, sin, unsqueeze_dim) cos = cos.unsqueeze(unsqueeze_dim).unsqueeze(0) sin = sin.unsqueeze(unsqueeze_dim).unsqueeze(0) q = q.unsqueeze(0) k = k.unsqueeze(0) q_embed = torch_npu.npu_rotary_mul(q, cos, sin) k_embed = torch_npu.npu_rotary_mul(k, cos, sin) q_embed = q_embed.squeeze(0) k_embed = k_embed.squeeze(0) return q_embed, k_embed if _is_npu: apply_rotary_pos_emb = apply_rotary_pos_emb_npu elif _is_cpu and _is_cpu_amx_available: apply_rotary_pos_emb = torch.ops.sgl_kernel.apply_rotary_pos_emb_cpu else: apply_rotary_pos_emb = apply_rotary_pos_emb_native