# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import math from typing import List, Optional, Tuple import numpy as np import torch from einops import rearrange, repeat def get_1d_sincos_pos_embed_from_grid(embed_dim, pos): """ embed_dim: output dimension for each position pos: a list of positions to be encoded: size (M,) out: (M, D) """ assert embed_dim % 2 == 0 omega = np.arange(embed_dim // 2, dtype=np.float64) omega /= embed_dim / 2.0 omega = 1.0 / 10000**omega # (D/2,) pos = pos.reshape(-1) # (M,) out = np.einsum("m,d->md", pos, omega) # (M, D/2), outer product emb_sin = np.sin(out) # (M, D/2) emb_cos = np.cos(out) # (M, D/2) emb = np.concatenate([emb_sin, emb_cos], axis=1) # (M, D) return emb def _rotate_half_te(x: torch.Tensor) -> torch.Tensor: """ change sign so the last dimension becomes [-odd, +even]. Adopted from TransformerEngine. Source: https://github.com/NVIDIA/TransformerEngine/blob/main/transformer_engine/pytorch/attention.py """ x = x.view(x.shape[:-1] + torch.Size((2, x.shape[-1] // 2))) x1, x2 = x.unbind(dim=-2) return torch.cat((-x2, x1), dim=-1) def _apply_rotary_pos_emb_te( t: torch.Tensor, cos_freqs: torch.Tensor, sin_freqs: torch.Tensor, ) -> torch.Tensor: """ Apply rotary positional embedding tensor to the input tensor. Adopted from TransformerEngine. Source: https://github.com/NVIDIA/TransformerEngine/blob/main/transformer_engine/pytorch/attention.py Parameters ---------- t: torch.Tensor Input tensor of shape `[b, s, h, d]`, on which rotary positional embedding will be applied. cos_freqs: torch.Tensor Cosine component of rotary positional embedding tensor of shape `[s, 1, 1, d]` and dtype 'float', sin_freqs: torch.Tensor Sine component of rotary positional embedding tensor of shape `[s, 1, 1, d]` and dtype 'float', """ rot_dim = cos_freqs.shape[-1] # ideally t_pass is empty so rotary pos embedding is applied to all tensor t t, t_pass = t[..., :rot_dim], t[..., rot_dim:] # first part is cosine component # second part is sine component, need to change signs with _rotate_half method t = (t * cos_freqs) + (_rotate_half_te(t) * sin_freqs) output = torch.cat((t, t_pass), dim=-1) return output class RotaryPositionEmbedding(torch.nn.Module): """ Rotary Position Embedding module as described in the paper: https://arxiv.org/abs/2104.09864 This module implements rotary positional embeddings, which are used to enhance the performance of transformer models. Args: dim (int): Dimensionality of the input tensor. max_position_embeddings (Optional[int]): Maximum position embeddings. original_max_position_embeddings (Optional[int]): Original maximum position embeddings. rope_theta (Optional[float]): Base for the frequency calculation. apply_yarn (Optional[bool]): Whether to apply YaRN (Yet another Rotary). scale (Optional[int]): Scaling factor for the frequency calculation. extrapolation_factor (Optional[int]): Extrapolation factor for the frequency extension. attn_factor (Optional[int]): Attention factor for the frequency calculation. beta_fast (Optional[int]): Fast beta value for the YaRN frequency calculation. beta_slow (Optional[int]): Slow beta value for the YaRN frequency calculation. rope_dim (Optional[str]): Dimensionality of the RoPE. Choices: "1D", "2D", "3D". latent_shape (Optional[List[int]]): Shape of the latent tensor for video or image inputs. original_latent_shape (Optional[List[int]]): Original shape of the latent tensor for video or image inputs. pad_to_multiple_of (Optional[int]): Pad the position embedding to a multiple of this value. """ def __init__( self, dim: int, max_position_embeddings: Optional[int] = None, original_max_position_embeddings: Optional[int] = None, rope_theta: Optional[float] = 10000.0, apply_yarn: Optional[bool] = False, scale: Optional[int] = None, extrapolation_factor: Optional[int] = 1, attn_factor: Optional[int] = 1, beta_fast: Optional[int] = 32, beta_slow: Optional[int] = 1, rope_dim: Optional[str] = "1D", latent_shape: Optional[List[int]] = None, original_latent_shape: Optional[List[int]] = None, pad_to_multiple_of: Optional[int] = None, ): super().__init__() self.dim = dim self.max_position_embeddings = max_position_embeddings self.original_max_position_embeddings = original_max_position_embeddings self.rope_theta = rope_theta self.apply_yarn = apply_yarn self.scale = scale self.extrapolation_factor = extrapolation_factor self.attn_factor = attn_factor self.beta_fast = beta_fast self.beta_slow = beta_slow self.mscale = 1.0 self.rope_dim = rope_dim self.latent_shape = latent_shape self.original_latent_shape = original_latent_shape self.pad_to_multiple_of = pad_to_multiple_of self.get_inv_freq(torch.cuda.current_device()) def get_mscale(self, scale: float = 1.0) -> float: """Get the magnitude scaling factor for YaRN.""" if scale <= 1: return 1.0 return 0.1 * math.log(scale) + 1.0 def forward(self, seq_len: Optional[int] = None) -> torch.Tensor: """ Forward pass for the rotary position embedding. Args: seq_len (Optional[int]): Length of the sequence. Returns: torch.Tensor: The computed frequencies for positional embedding. """ if self.apply_yarn and seq_len > self.max_seq_len_cached: self.max_seq_len_cached = seq_len self.freqs = self.compute_freqs() return self.freqs def compute_freqs( self, ) -> Tuple[torch.Tensor, torch.Tensor]: """Compute the spatial frequencies for the latent tensor.""" self.seq = torch.arange(self.max_seq_len_cached, dtype=torch.float).cuda() if self.rope_dim == "1D": emb = torch.einsum("i,j->ij", self.seq, self.inv_freq) elif self.rope_dim == "2D": H, W = self.latent_shape half_emb_h = torch.outer(self.seq[:H], self.spatial_inv_freq) half_emb_w = torch.outer(self.seq[:W], self.spatial_inv_freq) emb = torch.cat( [ repeat(half_emb_h, "h d -> h w d", w=W), repeat(half_emb_w, "w d -> h w d", h=H), ] * 2, dim=-1, ) emb = rearrange(emb, "h w d -> (h w) 1 1 d").float() elif self.rope_dim == "3D": T, H, W = self.latent_shape half_emb_t = torch.outer(self.seq[:T], self.temporal_inv_freq) half_emb_h = torch.outer(self.seq[:H], self.spatial_inv_freq) half_emb_w = torch.outer(self.seq[:W], self.spatial_inv_freq) emb = torch.cat( [ repeat(half_emb_t, "t d -> t h w d", h=H, w=W), repeat(half_emb_h, "h d -> t h w d", t=T, w=W), repeat(half_emb_w, "w d -> t h w d", t=T, h=H), ] * 2, dim=-1, ) emb = rearrange(emb, "t h w d -> (t h w) 1 1 d").float() else: raise ValueError(f"Invalid RoPE dimensionality: {self.rope_dim}") return emb def get_scale_factors(self, inv_freq: torch.Tensor, original_seq_len: int) -> torch.Tensor: """Get the scale factors for YaRN.""" # Calculate the high and low frequency cutoffs for YaRN. Note: `beta_fast` and `beta_slow` are called # `high_freq_factor` and `low_freq_factor` in the Llama 3.1 RoPE scaling code. high_freq_cutoff = 2 * math.pi * self.beta_fast / original_seq_len low_freq_cutoff = 2 * math.pi * self.beta_slow / original_seq_len # Obtain a smooth mask that has a value of 0 for low frequencies and 1 for high frequencies, with linear # interpolation in between. smooth_mask = torch.clamp((inv_freq - low_freq_cutoff) / (high_freq_cutoff - low_freq_cutoff), min=0, max=1) # For low frequencies, we scale the frequency by 1/self.scale. For high frequencies, we keep the frequency. scale_factors = (1 - smooth_mask) / self.scale + smooth_mask return scale_factors def get_inv_freq(self, device: torch.device) -> None: """Get the inverse frequency.""" if self.rope_dim == "1D": assert self.max_position_embeddings is not None, "Max position embeddings required." inv_freq = 1.0 / ( self.rope_theta ** (torch.arange(0, self.dim, 2, dtype=torch.float32, device=device) / self.dim) ) if self.apply_yarn: assert self.original_max_position_embeddings is not None, "Original max position embeddings required." assert self.beta_slow is not None, "Beta slow value required." assert self.beta_fast is not None, "Beta fast value required." scale_factors = self.get_scale_factors(inv_freq, self.original_max_position_embeddings) # Apply the scaling factors to inv_freq. inv_freq = inv_freq * scale_factors # Set the magnitude scaling factor. self.mscale = float(self.get_mscale(self.scale) * self.attn_factor) self.max_seq_len_cached = self.max_position_embeddings self.inv_freq = inv_freq elif self.rope_dim == "2D": assert self.latent_shape is not None, "Latent shape required." dim_h = self.dim // 2 spatial_inv_freq = 1.0 / ( self.rope_theta ** torch.arange(0, dim_h, 2, dtype=torch.float32, device=device) / dim_h ) if self.apply_yarn: assert self.original_latent_shape is not None, "Original latent shape required." assert self.beta_slow is not None, "Beta slow value required." assert self.beta_fast is not None, "Beta fast value required." scale_factors = self.get_scale_factors(spatial_inv_freq, self.original_latent_shape[0]) spatial_inv_freq = spatial_inv_freq * scale_factors self.mscale = float(self.get_mscale(self.scale) * self.attn_factor) self.spatial_inv_freq = spatial_inv_freq self.max_seq_len_cached = max(self.latent_shape) elif self.rope_dim == "3D": assert self.latent_shape is not None, "Latent shape required." dim_h = self.dim // 6 * 2 dim_t = self.dim - 2 * dim_h self.dim_spatial_range = torch.arange(0, dim_h, 2)[: (dim_h // 2)].float().to(device) / dim_h spatial_inv_freq = 1.0 / (self.rope_theta**self.dim_spatial_range) self.dim_temporal_range = torch.arange(0, dim_t, 2)[: (dim_t // 2)].float().to(device) / dim_t temporal_inv_freq = 1.0 / (self.rope_theta**self.dim_temporal_range) if self.apply_yarn: assert self.original_latent_shape is not None, "Original latent shape required." assert self.beta_slow is not None, "Beta slow value required." assert self.beta_fast is not None, "Beta fast value required." scale_factors_spatial = self.get_scale_factors(spatial_inv_freq, self.original_latent_shape[1]) spatial_inv_freq = spatial_inv_freq * scale_factors_spatial scale_factors_temporal = self.get_scale_factors(temporal_inv_freq, self.original_latent_shape[0]) temporal_inv_freq = temporal_inv_freq * scale_factors_temporal self.mscale = float(self.get_mscale(self.scale) * self.attn_factor) self.spatial_inv_freq = spatial_inv_freq self.temporal_inv_freq = temporal_inv_freq self.max_seq_len_cached = max(self.latent_shape) else: raise ValueError(f"Invalid RoPE dimensionality: {self.rope_dim}") self.freqs = self.compute_freqs() class RotaryPositionEmbeddingPytorchV2(RotaryPositionEmbedding): """ Rotary Position Embedding that works in the same way as the TransformerEngine RoPE (https://github.com/NVIDIA/TransformerEngine/blob/main/transformer_engine/pytorch/attention.py) """ def __init__( self, seq_len: int, training_type: str = None, **kwargs, ): super().__init__( **kwargs, ) emb = self.create_rope_freqs(seq_len=seq_len, training_type=training_type) emb = emb.transpose(0, 1).contiguous() # [seq, 1, 1, dim] -> [1, seq, 1, dim] assert emb.shape[0] == 1 and emb.shape[2] == 1, f"emb shape: {emb.shape}" # cos/sin first then dtype conversion for better precision self.register_buffer("cos_cached", torch.cos(emb), persistent=False) self.register_buffer("sin_cached", torch.sin(emb), persistent=False) def create_rope_freqs(self, seq_len: int, training_type: str = None) -> torch.Tensor: """ Create rotary position embedding frequencies. Args: seq_len (int): Sequence length of a sample. Returns: torch.Tensor: The computed positional embeddings. """ if self.rope_dim == "1D": freqs = super().forward(seq_len=seq_len) emb = torch.cat((freqs, freqs), dim=-1) emb = emb.reshape(emb.size(0), 1, 1, emb.size(1)) elif self.rope_dim in ["2D", "3D"]: emb = super().forward(seq_len=seq_len) if training_type == "text_to_video": # since we added token at the beginning of the video for text2world, we also extend the position embedding by one token in the beginning bov_pe = torch.zeros((1, *emb.shape[1:]), device=emb.device) emb = torch.cat((bov_pe, emb), dim=0) else: raise ValueError(f"Invalid RoPE dimensionality: {self.rope_dim}") if self.pad_to_multiple_of is not None and emb.shape[0] % self.pad_to_multiple_of != 0: # Round up to the nearest multiple of pad_to_multiple_of pad_len = self.pad_to_multiple_of - emb.shape[0] % self.pad_to_multiple_of emb = torch.cat((emb, torch.zeros((pad_len, *emb.shape[1:]), device=emb.device)), dim=0) return emb def forward( self, q: torch.Tensor, k: torch.Tensor, input_pos: Optional[torch.Tensor] = None, seq_len: Optional[int] = None ) -> Tuple[torch.Tensor, torch.Tensor]: if q.dtype != self.cos_cached.dtype: self.cos_cached = self.cos_cached.to(q.dtype) self.sin_cached = self.sin_cached.to(q.dtype) cos_emb = self.cos_cached sin_emb = self.sin_cached if input_pos is not None: cos_emb = cos_emb[:, input_pos, :, :] sin_emb = sin_emb[:, input_pos, :, :] elif seq_len is not None: cos_emb = cos_emb[:, :seq_len, :, :] sin_emb = sin_emb[:, :seq_len, :, :] q = _apply_rotary_pos_emb_te(q, cos_emb, sin_emb) k = _apply_rotary_pos_emb_te(k, cos_emb, sin_emb) return q, k class RotaryPositionEmbeddingPytorchV1(RotaryPositionEmbedding): """ Rotary Position Embedding that works in the same way as mistral_inference (https://github.com/mistralai/mistral-inference/blob/main/src/mistral_inference/rope.py) or llama3 (https://github.com/meta-llama/llama3/blob/main/llama/model.py) """ def __init__( self, **kwargs, ): super().__init__( **kwargs, ) if self.rope_dim == "1D": emb = torch.stack((self.freqs, self.freqs), dim=-1).reshape(*self.freqs.shape[:-1], -1) elif self.rope_dim in ["2D", "3D"]: emb = rearrange(self.freqs, "s 1 1 d -> s d").float() self.register_buffer("cos_cached", (emb.cos() * self.mscale)[None, :, None, :], persistent=False) self.register_buffer("sin_cached", (emb.sin() * self.mscale)[None, :, None, :], persistent=False) def rotate_half(self, x: torch.Tensor) -> torch.Tensor: """Rotate half the hidden dimensions of the input tensor.""" x_reshaped = x.reshape(*x.shape[:-1], -1, 2) x1 = x_reshaped[..., 0] x2 = x_reshaped[..., 1] output = torch.stack((-x2, x1), dim=-1).reshape(*x.shape) return output def forward( self, q: torch.Tensor, k: torch.Tensor, input_pos: Optional[torch.Tensor] = None, seq_len: Optional[int] = None ) -> Tuple[torch.Tensor, torch.Tensor]: """ Forward pass for the rotary position embedding. Args: q (torch.Tensor): Query tensor. k (torch.Tensor): Key tensor. input_pos (Optional[torch.Tensor]): Starting position for the sequence. seq_len (Optional[int]): Length of the sequence. Returns: Tuple[torch.Tensor, torch.Tensor]: Rotated query and key tensors. """ if self.apply_yarn and seq_len > self.max_seq_len_cached: freqs = super().forward(seq_len) if self.rope_dim == "1D": emb = torch.stack((freqs, freqs), dim=-1).reshape(*freqs.shape[:-1], -1) elif self.rope_dim in ["2D", "3D"]: emb = rearrange(freqs, "s 1 1 d -> s d").float() else: raise ValueError(f"Invalid RoPE dimensionality: {self.rope_dim}") self.register_buffer( "cos_cached", (emb.cos() * self.mscale)[None, :, None, :].to(q.dtype), persistent=False ) self.register_buffer( "sin_cached", (emb.sin() * self.mscale)[None, :, None, :].to(q.dtype), persistent=False ) if input_pos is not None: cos_cached = self.cos_cached[:, input_pos] sin_cached = self.sin_cached[:, input_pos] else: assert ( self.cos_cached.shape[1] >= seq_len ), f"Invalid sequence length; cos_cached.shape {self.cos_cached.shape}, seq_len {seq_len}." cos_cached = self.cos_cached[:, :seq_len, ...] sin_cached = self.sin_cached[:, :seq_len, ...] xq = q * cos_cached + self.rotate_half(q) * sin_cached xk = k * cos_cached + self.rotate_half(k) * sin_cached return xq.type_as(q), xk.type_as(k) class SinCosPosEmbAxisTE(torch.nn.Module): def __init__( self, dim: int, latent_shape: Optional[List[int]] = None, pad_to_multiple_of: Optional[int] = None, dtype: torch.dtype = torch.bfloat16, **kwargs, ): """ Args: dim (int): Dimensionality of the input tensor. latent_shape (Optional[List[int]]): Shape of the latent tensor for video or image inputs. pad_to_multiple_of (Optional[int]): Pad the position embedding to a multiple of this value. dtype (torch.dtype): Data type of the position embedding tensor. """ super().__init__() dim_h = dim // 6 * 2 dim_w = dim_h dim_t = dim - 2 * dim_h assert dim == dim_h + dim_w + dim_t, f"bad dim: {dim} != {dim_h} + {dim_w} + {dim_t}" self.latent_shape = latent_shape T, H, W = latent_shape emb_h = get_1d_sincos_pos_embed_from_grid(dim_h, pos=np.arange(H)) emb_w = get_1d_sincos_pos_embed_from_grid(dim_w, pos=np.arange(W)) emb_t = get_1d_sincos_pos_embed_from_grid(dim_t, pos=np.arange(T)) self.register_buffer("pos_emb_h", torch.from_numpy(emb_h).to(dtype=dtype, device="cuda"), persistent=False) self.register_buffer("pos_emb_w", torch.from_numpy(emb_w).to(dtype=dtype, device="cuda"), persistent=False) self.register_buffer("pos_emb_t", torch.from_numpy(emb_t).to(dtype=dtype, device="cuda"), persistent=False) self.pad_to_multiple_of = pad_to_multiple_of def forward( self, training_type: str = None, ) -> torch.Tensor: T, H, W = self.latent_shape emb = torch.cat( [ repeat(self.pos_emb_t, "t d-> t h w d", h=H, w=W), repeat(self.pos_emb_h, "h d-> t h w d", t=T, w=W), repeat(self.pos_emb_w, "w d-> t h w d", t=T, h=H), ], dim=-1, ) # Flatten the T,H,W dimensions emb = rearrange(emb, "t h w d -> (t h w) d") if training_type == "text_to_video": bov_pe = torch.zeros((1, *emb.shape[1:]), device=emb.device, dtype=emb.dtype) emb = torch.cat((bov_pe, emb), dim=0) if self.pad_to_multiple_of is not None and emb.shape[0] % self.pad_to_multiple_of != 0: pad_len = self.pad_to_multiple_of - emb.shape[0] % self.pad_to_multiple_of emb = torch.cat((emb, torch.zeros((pad_len, *emb.shape[1:]), device=emb.device, dtype=emb.dtype)), dim=0) seq_len, dim = emb.shape emb = emb.reshape(1, seq_len, dim) return emb