Files
wehub-resource-sync cddb07a176
docs / deploy (push) Has been cancelled
docs / changes (push) Has been cancelled
docs / check-and-build (push) Has been cancelled
build container image / cpu (push) Has been cancelled
build container image / cuda (push) Has been cancelled
build container image / rocm (push) Has been cancelled
frontend checks / frontend-checks (push) Has been cancelled
frontend tests / frontend-tests (push) Has been cancelled
lfs checks / lfs-check (push) Has been cancelled
python checks / python-checks (push) Has been cancelled
python tests / py3.12: macos-default (push) Has been cancelled
python tests / py3.11: windows-cpu (push) Has been cancelled
python tests / py3.12: windows-cpu (push) Has been cancelled
python tests / py3.11: linux-cpu (push) Has been cancelled
typegen checks / typegen-checks (push) Has been cancelled
uv lock checks / uv-lock-checks (push) Has been cancelled
openapi checks / openapi-checks (push) Has been cancelled
python tests / py3.11: macos-default (push) Has been cancelled
python tests / py3.12: linux-cpu (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:22:06 +08:00

1041 lines
39 KiB
Python

"""Anima transformer model: Cosmos Predict2 MiniTrainDIT + LLM Adapter.
The Anima architecture combines:
1. MiniTrainDIT: A Cosmos Predict2 DiT backbone with 28 blocks, 2048-dim hidden state,
and 3D RoPE positional embeddings.
2. LLMAdapter: A 6-layer cross-attention transformer that fuses Qwen3 0.6B hidden states
with learned T5-XXL token embeddings to produce conditioning for the DiT.
Original source code:
- MiniTrainDIT backbone and positional embeddings: https://github.com/nvidia-cosmos/cosmos-predict2
SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: Apache-2.0
- LLMAdapter and Anima wrapper: Clean-room implementation based on
https://github.com/hdae/diffusers-anima (Apache-2.0)
"""
import logging
import math
from typing import Optional, Tuple
import torch
import torch.nn.functional as F
from einops import rearrange, repeat
from einops.layers.torch import Rearrange
from torch import nn
logger = logging.getLogger(__name__)
# ============================================================================
# Positional Embeddings
# Original source: https://github.com/nvidia-cosmos/cosmos-predict2
# Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. Apache-2.0
# ============================================================================
class VideoRopePosition3DEmb(nn.Module):
"""3D Rotary Position Embedding for video/image transformers.
Generates rotary embeddings with separate frequency components for
height, width, and temporal dimensions.
"""
def __init__(
self,
*,
head_dim: int,
len_h: int,
len_w: int,
len_t: int,
base_fps: int = 24,
h_extrapolation_ratio: float = 1.0,
w_extrapolation_ratio: float = 1.0,
t_extrapolation_ratio: float = 1.0,
enable_fps_modulation: bool = True,
device: Optional[torch.device] = None,
**kwargs,
):
super().__init__()
self.base_fps = base_fps
self.max_h = len_h
self.max_w = len_w
self.enable_fps_modulation = enable_fps_modulation
dim = head_dim
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.register_buffer(
"dim_spatial_range",
torch.arange(0, dim_h, 2, device=device)[: (dim_h // 2)].float() / dim_h,
persistent=False,
)
self.register_buffer(
"dim_temporal_range",
torch.arange(0, dim_t, 2, device=device)[: (dim_t // 2)].float() / dim_t,
persistent=False,
)
self.h_ntk_factor = h_extrapolation_ratio ** (dim_h / (dim_h - 2))
self.w_ntk_factor = w_extrapolation_ratio ** (dim_w / (dim_w - 2))
self.t_ntk_factor = t_extrapolation_ratio ** (dim_t / (dim_t - 2))
def forward(
self,
x_B_T_H_W_C: torch.Tensor,
fps: Optional[torch.Tensor] = None,
device: Optional[torch.device] = None,
) -> torch.Tensor:
return self.generate_embeddings(x_B_T_H_W_C.shape, fps=fps, device=device)
def generate_embeddings(
self,
B_T_H_W_C: torch.Size,
fps: Optional[torch.Tensor] = None,
device: Optional[torch.device] = None,
dtype: Optional[torch.dtype] = None,
) -> torch.Tensor:
h_theta = 10000.0 * self.h_ntk_factor
w_theta = 10000.0 * self.w_ntk_factor
t_theta = 10000.0 * self.t_ntk_factor
h_spatial_freqs = 1.0 / (h_theta ** self.dim_spatial_range.to(device=device))
w_spatial_freqs = 1.0 / (w_theta ** self.dim_spatial_range.to(device=device))
temporal_freqs = 1.0 / (t_theta ** self.dim_temporal_range.to(device=device))
B, T, H, W, _ = B_T_H_W_C
seq = torch.arange(max(H, W, T), dtype=torch.float, device=device)
half_emb_h = torch.outer(seq[:H].to(device=device), h_spatial_freqs)
half_emb_w = torch.outer(seq[:W].to(device=device), w_spatial_freqs)
if fps is None or self.enable_fps_modulation is False:
half_emb_t = torch.outer(seq[:T].to(device=device), temporal_freqs)
else:
half_emb_t = torch.outer(seq[:T].to(device=device) / fps * self.base_fps, temporal_freqs)
half_emb_h = torch.stack(
[torch.cos(half_emb_h), -torch.sin(half_emb_h), torch.sin(half_emb_h), torch.cos(half_emb_h)], dim=-1
)
half_emb_w = torch.stack(
[torch.cos(half_emb_w), -torch.sin(half_emb_w), torch.sin(half_emb_w), torch.cos(half_emb_w)], dim=-1
)
half_emb_t = torch.stack(
[torch.cos(half_emb_t), -torch.sin(half_emb_t), torch.sin(half_emb_t), torch.cos(half_emb_t)], dim=-1
)
em_T_H_W_D = torch.cat(
[
repeat(half_emb_t, "t d x -> t h w d x", h=H, w=W),
repeat(half_emb_h, "h d x -> t h w d x", t=T, w=W),
repeat(half_emb_w, "w d x -> t h w d x", t=T, h=H),
],
dim=-2,
)
return rearrange(em_T_H_W_D, "t h w d (i j) -> (t h w) d i j", i=2, j=2).float()
def _normalize(x: torch.Tensor, dim: Optional[list[int]] = None, eps: float = 0) -> torch.Tensor:
if dim is None:
dim = list(range(1, x.ndim))
norm = torch.linalg.vector_norm(x, dim=dim, keepdim=True, dtype=torch.float32)
norm = torch.add(eps, norm, alpha=math.sqrt(norm.numel() / x.numel()))
return x / norm.to(x.dtype)
class LearnablePosEmbAxis(nn.Module):
"""Learnable per-axis positional embeddings."""
def __init__(
self,
*,
model_channels: int,
len_h: int,
len_w: int,
len_t: int,
device: Optional[torch.device] = None,
dtype: Optional[torch.dtype] = None,
**kwargs,
):
super().__init__()
self.pos_emb_h = nn.Parameter(torch.empty(len_h, model_channels, device=device, dtype=dtype))
self.pos_emb_w = nn.Parameter(torch.empty(len_w, model_channels, device=device, dtype=dtype))
self.pos_emb_t = nn.Parameter(torch.empty(len_t, model_channels, device=device, dtype=dtype))
def forward(
self,
x_B_T_H_W_C: torch.Tensor,
fps: Optional[torch.Tensor] = None,
device: Optional[torch.device] = None,
dtype: Optional[torch.dtype] = None,
) -> torch.Tensor:
return self.generate_embeddings(x_B_T_H_W_C.shape, device=device, dtype=dtype)
def generate_embeddings(
self,
B_T_H_W_C: torch.Size,
fps: Optional[torch.Tensor] = None,
device: Optional[torch.device] = None,
dtype: Optional[torch.dtype] = None,
) -> torch.Tensor:
B, T, H, W, _ = B_T_H_W_C
emb_h_H = self.pos_emb_h[:H].to(device=device, dtype=dtype)
emb_w_W = self.pos_emb_w[:W].to(device=device, dtype=dtype)
emb_t_T = self.pos_emb_t[:T].to(device=device, dtype=dtype)
emb = (
repeat(emb_t_T, "t d -> b t h w d", b=B, h=H, w=W)
+ repeat(emb_h_H, "h d -> b t h w d", b=B, t=T, w=W)
+ repeat(emb_w_W, "w d -> b t h w d", b=B, t=T, h=H)
)
return _normalize(emb, dim=-1, eps=1e-6)
# ============================================================================
# Cosmos Predict2 MiniTrainDIT
# Original source: https://github.com/nvidia-cosmos/cosmos-predict2
# Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. Apache-2.0
# ============================================================================
def apply_rotary_pos_emb_cosmos(t: torch.Tensor, freqs: torch.Tensor) -> torch.Tensor:
"""Apply rotary position embeddings in Cosmos format (2x2 rotation matrices)."""
t_ = t.reshape(*t.shape[:-1], 2, -1).movedim(-2, -1).unsqueeze(-2).float()
t_out = freqs[..., 0] * t_[..., 0] + freqs[..., 1] * t_[..., 1]
t_out = t_out.movedim(-1, -2).reshape(*t.shape).type_as(t)
return t_out
class GPT2FeedForward(nn.Module):
def __init__(self, d_model: int, d_ff: int) -> None:
super().__init__()
self.activation = nn.GELU()
self.layer1 = nn.Linear(d_model, d_ff, bias=False)
self.layer2 = nn.Linear(d_ff, d_model, bias=False)
def forward(self, x: torch.Tensor) -> torch.Tensor:
return self.layer2(self.activation(self.layer1(x)))
class CosmosAttention(nn.Module):
"""Multi-head attention for the Cosmos DiT backbone.
Supports both self-attention and cross-attention with QK normalization
and rotary position embeddings.
"""
def __init__(
self,
query_dim: int,
context_dim: Optional[int] = None,
n_heads: int = 8,
head_dim: int = 64,
dropout: float = 0.0,
) -> None:
super().__init__()
self.is_selfattn = context_dim is None
context_dim = query_dim if context_dim is None else context_dim
inner_dim = head_dim * n_heads
self.n_heads = n_heads
self.head_dim = head_dim
self.q_proj = nn.Linear(query_dim, inner_dim, bias=False)
self.q_norm = nn.RMSNorm(head_dim, eps=1e-6)
self.k_proj = nn.Linear(context_dim, inner_dim, bias=False)
self.k_norm = nn.RMSNorm(head_dim, eps=1e-6)
self.v_proj = nn.Linear(context_dim, inner_dim, bias=False)
self.v_norm = nn.Identity()
self.output_proj = nn.Linear(inner_dim, query_dim, bias=False)
self.output_dropout = nn.Dropout(dropout) if dropout > 1e-4 else nn.Identity()
def forward(
self,
x: torch.Tensor,
context: Optional[torch.Tensor] = None,
rope_emb: Optional[torch.Tensor] = None,
) -> torch.Tensor:
q = self.q_proj(x)
context = x if context is None else context
k = self.k_proj(context)
v = self.v_proj(context)
q, k, v = (rearrange(t, "b ... (h d) -> b ... h d", h=self.n_heads, d=self.head_dim) for t in (q, k, v))
q = self.q_norm(q)
k = self.k_norm(k)
v = self.v_norm(v)
if self.is_selfattn and rope_emb is not None:
q = apply_rotary_pos_emb_cosmos(q, rope_emb)
k = apply_rotary_pos_emb_cosmos(k, rope_emb)
# Reshape for scaled_dot_product_attention: (B, heads, seq, dim)
in_q_shape = q.shape
in_k_shape = k.shape
q = rearrange(q, "b ... h d -> b h ... d").reshape(in_q_shape[0], in_q_shape[-2], -1, in_q_shape[-1])
k = rearrange(k, "b ... h d -> b h ... d").reshape(in_k_shape[0], in_k_shape[-2], -1, in_k_shape[-1])
v = rearrange(v, "b ... h d -> b h ... d").reshape(in_k_shape[0], in_k_shape[-2], -1, in_k_shape[-1])
result = F.scaled_dot_product_attention(q, k, v)
result = rearrange(result, "b h s d -> b s (h d)")
return self.output_dropout(self.output_proj(result))
class Timesteps(nn.Module):
"""Sinusoidal timestep embeddings."""
def __init__(self, num_channels: int):
super().__init__()
self.num_channels = num_channels
def forward(self, timesteps_B_T: torch.Tensor) -> torch.Tensor:
assert timesteps_B_T.ndim == 2
timesteps = timesteps_B_T.flatten().float()
half_dim = self.num_channels // 2
exponent = -math.log(10000) * torch.arange(half_dim, dtype=torch.float32, device=timesteps.device) / half_dim
emb = timesteps[:, None].float() * torch.exp(exponent)[None, :]
emb = torch.cat([torch.cos(emb), torch.sin(emb)], dim=-1)
return rearrange(emb, "(b t) d -> b t d", b=timesteps_B_T.shape[0], t=timesteps_B_T.shape[1])
class TimestepEmbedding(nn.Module):
"""Projects sinusoidal timestep embeddings to model dimension."""
def __init__(self, in_features: int, out_features: int, use_adaln_lora: bool = False):
super().__init__()
self.use_adaln_lora = use_adaln_lora
self.linear_1 = nn.Linear(in_features, out_features, bias=not use_adaln_lora)
self.activation = nn.SiLU()
if use_adaln_lora:
self.linear_2 = nn.Linear(out_features, 3 * out_features, bias=False)
else:
self.linear_2 = nn.Linear(out_features, out_features, bias=False)
def forward(self, sample: torch.Tensor) -> Tuple[torch.Tensor, Optional[torch.Tensor]]:
emb = self.linear_2(self.activation(self.linear_1(sample)))
if self.use_adaln_lora:
return sample, emb
return emb, None
class PatchEmbed(nn.Module):
"""Patchify input tensor via rearrange + linear projection."""
def __init__(
self,
spatial_patch_size: int,
temporal_patch_size: int,
in_channels: int = 3,
out_channels: int = 768,
):
super().__init__()
self.spatial_patch_size = spatial_patch_size
self.temporal_patch_size = temporal_patch_size
self.proj = nn.Sequential(
Rearrange(
"b c (t r) (h m) (w n) -> b t h w (c r m n)",
r=temporal_patch_size,
m=spatial_patch_size,
n=spatial_patch_size,
),
nn.Linear(
in_channels * spatial_patch_size * spatial_patch_size * temporal_patch_size,
out_channels,
bias=False,
),
)
def forward(self, x: torch.Tensor) -> torch.Tensor:
assert x.dim() == 5
return self.proj(x)
class FinalLayer(nn.Module):
"""Final AdaLN-modulated output projection."""
def __init__(
self,
hidden_size: int,
spatial_patch_size: int,
temporal_patch_size: int,
out_channels: int,
use_adaln_lora: bool = False,
adaln_lora_dim: int = 256,
):
super().__init__()
self.layer_norm = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
self.linear = nn.Linear(
hidden_size, spatial_patch_size * spatial_patch_size * temporal_patch_size * out_channels, bias=False
)
self.hidden_size = hidden_size
self.use_adaln_lora = use_adaln_lora
if use_adaln_lora:
self.adaln_modulation = nn.Sequential(
nn.SiLU(),
nn.Linear(hidden_size, adaln_lora_dim, bias=False),
nn.Linear(adaln_lora_dim, 2 * hidden_size, bias=False),
)
else:
self.adaln_modulation = nn.Sequential(
nn.SiLU(),
nn.Linear(hidden_size, 2 * hidden_size, bias=False),
)
def forward(
self,
x_B_T_H_W_D: torch.Tensor,
emb_B_T_D: torch.Tensor,
adaln_lora_B_T_3D: Optional[torch.Tensor] = None,
) -> torch.Tensor:
if self.use_adaln_lora:
assert adaln_lora_B_T_3D is not None
shift, scale = (self.adaln_modulation(emb_B_T_D) + adaln_lora_B_T_3D[:, :, : 2 * self.hidden_size]).chunk(
2, dim=-1
)
else:
shift, scale = self.adaln_modulation(emb_B_T_D).chunk(2, dim=-1)
shift = rearrange(shift, "b t d -> b t 1 1 d")
scale = rearrange(scale, "b t d -> b t 1 1 d")
x_B_T_H_W_D = self.layer_norm(x_B_T_H_W_D) * (1 + scale) + shift
return self.linear(x_B_T_H_W_D)
class DiTBlock(nn.Module):
"""Cosmos DiT transformer block with self-attention, cross-attention, and MLP.
Each component uses AdaLN (Adaptive Layer Normalization) modulation from
the timestep embedding.
"""
def __init__(
self,
x_dim: int,
context_dim: int,
num_heads: int,
mlp_ratio: float = 4.0,
use_adaln_lora: bool = False,
adaln_lora_dim: int = 256,
):
super().__init__()
self.x_dim = x_dim
self.use_adaln_lora = use_adaln_lora
self.layer_norm_self_attn = nn.LayerNorm(x_dim, elementwise_affine=False, eps=1e-6)
self.self_attn = CosmosAttention(x_dim, None, num_heads, x_dim // num_heads)
self.layer_norm_cross_attn = nn.LayerNorm(x_dim, elementwise_affine=False, eps=1e-6)
self.cross_attn = CosmosAttention(x_dim, context_dim, num_heads, x_dim // num_heads)
self.layer_norm_mlp = nn.LayerNorm(x_dim, elementwise_affine=False, eps=1e-6)
self.mlp = GPT2FeedForward(x_dim, int(x_dim * mlp_ratio))
# AdaLN modulation layers (shift, scale, gate for each of 3 components)
if use_adaln_lora:
self.adaln_modulation_self_attn = nn.Sequential(
nn.SiLU(),
nn.Linear(x_dim, adaln_lora_dim, bias=False),
nn.Linear(adaln_lora_dim, 3 * x_dim, bias=False),
)
self.adaln_modulation_cross_attn = nn.Sequential(
nn.SiLU(),
nn.Linear(x_dim, adaln_lora_dim, bias=False),
nn.Linear(adaln_lora_dim, 3 * x_dim, bias=False),
)
self.adaln_modulation_mlp = nn.Sequential(
nn.SiLU(),
nn.Linear(x_dim, adaln_lora_dim, bias=False),
nn.Linear(adaln_lora_dim, 3 * x_dim, bias=False),
)
else:
self.adaln_modulation_self_attn = nn.Sequential(nn.SiLU(), nn.Linear(x_dim, 3 * x_dim, bias=False))
self.adaln_modulation_cross_attn = nn.Sequential(nn.SiLU(), nn.Linear(x_dim, 3 * x_dim, bias=False))
self.adaln_modulation_mlp = nn.Sequential(nn.SiLU(), nn.Linear(x_dim, 3 * x_dim, bias=False))
def forward(
self,
x_B_T_H_W_D: torch.Tensor,
emb_B_T_D: torch.Tensor,
crossattn_emb: torch.Tensor,
rope_emb_L_1_1_D: Optional[torch.Tensor] = None,
adaln_lora_B_T_3D: Optional[torch.Tensor] = None,
extra_per_block_pos_emb: Optional[torch.Tensor] = None,
) -> torch.Tensor:
residual_dtype = x_B_T_H_W_D.dtype
compute_dtype = emb_B_T_D.dtype
if extra_per_block_pos_emb is not None:
x_B_T_H_W_D = x_B_T_H_W_D + extra_per_block_pos_emb
# Compute AdaLN modulations
if self.use_adaln_lora:
assert adaln_lora_B_T_3D is not None
shift_sa, scale_sa, gate_sa = (self.adaln_modulation_self_attn(emb_B_T_D) + adaln_lora_B_T_3D).chunk(
3, dim=-1
)
shift_ca, scale_ca, gate_ca = (self.adaln_modulation_cross_attn(emb_B_T_D) + adaln_lora_B_T_3D).chunk(
3, dim=-1
)
shift_mlp, scale_mlp, gate_mlp = (self.adaln_modulation_mlp(emb_B_T_D) + adaln_lora_B_T_3D).chunk(3, dim=-1)
else:
shift_sa, scale_sa, gate_sa = self.adaln_modulation_self_attn(emb_B_T_D).chunk(3, dim=-1)
shift_ca, scale_ca, gate_ca = self.adaln_modulation_cross_attn(emb_B_T_D).chunk(3, dim=-1)
shift_mlp, scale_mlp, gate_mlp = self.adaln_modulation_mlp(emb_B_T_D).chunk(3, dim=-1)
# Reshape for broadcasting: (B, T, D) -> (B, T, 1, 1, D)
shift_sa, scale_sa, gate_sa = (rearrange(t, "b t d -> b t 1 1 d") for t in (shift_sa, scale_sa, gate_sa))
shift_ca, scale_ca, gate_ca = (rearrange(t, "b t d -> b t 1 1 d") for t in (shift_ca, scale_ca, gate_ca))
shift_mlp, scale_mlp, gate_mlp = (rearrange(t, "b t d -> b t 1 1 d") for t in (shift_mlp, scale_mlp, gate_mlp))
B, T, H, W, D = x_B_T_H_W_D.shape
def _adaln(x: torch.Tensor, norm: nn.Module, scale: torch.Tensor, shift: torch.Tensor) -> torch.Tensor:
return norm(x) * (1 + scale) + shift
# Self-attention
normed = _adaln(x_B_T_H_W_D, self.layer_norm_self_attn, scale_sa, shift_sa)
result = rearrange(
self.self_attn(
rearrange(normed.to(compute_dtype), "b t h w d -> b (t h w) d"), None, rope_emb=rope_emb_L_1_1_D
),
"b (t h w) d -> b t h w d",
t=T,
h=H,
w=W,
)
x_B_T_H_W_D = x_B_T_H_W_D + gate_sa.to(residual_dtype) * result.to(residual_dtype)
# Cross-attention
normed = _adaln(x_B_T_H_W_D, self.layer_norm_cross_attn, scale_ca, shift_ca)
result = rearrange(
self.cross_attn(
rearrange(normed.to(compute_dtype), "b t h w d -> b (t h w) d"),
crossattn_emb,
rope_emb=rope_emb_L_1_1_D,
),
"b (t h w) d -> b t h w d",
t=T,
h=H,
w=W,
)
x_B_T_H_W_D = result.to(residual_dtype) * gate_ca.to(residual_dtype) + x_B_T_H_W_D
# MLP
normed = _adaln(x_B_T_H_W_D, self.layer_norm_mlp, scale_mlp, shift_mlp)
result = self.mlp(normed.to(compute_dtype))
x_B_T_H_W_D = x_B_T_H_W_D + gate_mlp.to(residual_dtype) * result.to(residual_dtype)
return x_B_T_H_W_D
class MiniTrainDIT(nn.Module):
"""Cosmos Predict2 DiT backbone for video/image generation.
This is the core transformer architecture that Anima extends. It processes
3D latent tensors (B, C, T, H, W) with patch embedding, positional encoding,
and adaptive layer normalization.
Args:
max_img_h: Maximum image height in pixels.
max_img_w: Maximum image width in pixels.
max_frames: Maximum number of video frames.
in_channels: Number of input latent channels.
out_channels: Number of output channels.
patch_spatial: Spatial patch size.
patch_temporal: Temporal patch size.
concat_padding_mask: Whether to concatenate a padding mask channel.
model_channels: Hidden dimension of the transformer.
num_blocks: Number of DiT blocks.
num_heads: Number of attention heads.
mlp_ratio: MLP expansion ratio.
crossattn_emb_channels: Cross-attention context dimension.
use_adaln_lora: Whether to use AdaLN-LoRA.
adaln_lora_dim: AdaLN-LoRA bottleneck dimension.
extra_per_block_abs_pos_emb: Whether to use extra learnable positional embeddings.
"""
def __init__(
self,
max_img_h: int = 240,
max_img_w: int = 240,
max_frames: int = 1,
in_channels: int = 16,
out_channels: int = 16,
patch_spatial: int = 2,
patch_temporal: int = 1,
concat_padding_mask: bool = True,
model_channels: int = 2048,
num_blocks: int = 28,
num_heads: int = 16,
mlp_ratio: float = 4.0,
crossattn_emb_channels: int = 1024,
pos_emb_cls: str = "rope3d",
pos_emb_learnable: bool = False,
pos_emb_interpolation: str = "crop",
min_fps: int = 1,
max_fps: int = 30,
use_adaln_lora: bool = False,
adaln_lora_dim: int = 256,
rope_h_extrapolation_ratio: float = 1.0,
rope_w_extrapolation_ratio: float = 1.0,
rope_t_extrapolation_ratio: float = 1.0,
extra_per_block_abs_pos_emb: bool = False,
extra_h_extrapolation_ratio: float = 1.0,
extra_w_extrapolation_ratio: float = 1.0,
extra_t_extrapolation_ratio: float = 1.0,
rope_enable_fps_modulation: bool = True,
image_model: Optional[str] = None,
) -> None:
super().__init__()
self.max_img_h = max_img_h
self.max_img_w = max_img_w
self.max_frames = max_frames
self.in_channels = in_channels
self.out_channels = out_channels
self.patch_spatial = patch_spatial
self.patch_temporal = patch_temporal
self.num_heads = num_heads
self.num_blocks = num_blocks
self.model_channels = model_channels
self.concat_padding_mask = concat_padding_mask
self.pos_emb_cls = pos_emb_cls
self.extra_per_block_abs_pos_emb = extra_per_block_abs_pos_emb
# Positional embeddings
self.pos_embedder = VideoRopePosition3DEmb(
head_dim=model_channels // num_heads,
len_h=max_img_h // patch_spatial,
len_w=max_img_w // patch_spatial,
len_t=max_frames // patch_temporal,
max_fps=max_fps,
min_fps=min_fps,
h_extrapolation_ratio=rope_h_extrapolation_ratio,
w_extrapolation_ratio=rope_w_extrapolation_ratio,
t_extrapolation_ratio=rope_t_extrapolation_ratio,
enable_fps_modulation=rope_enable_fps_modulation,
)
if extra_per_block_abs_pos_emb:
self.extra_pos_embedder = LearnablePosEmbAxis(
model_channels=model_channels,
len_h=max_img_h // patch_spatial,
len_w=max_img_w // patch_spatial,
len_t=max_frames // patch_temporal,
)
self.use_adaln_lora = use_adaln_lora
self.adaln_lora_dim = adaln_lora_dim
# Timestep embedding
self.t_embedder = nn.Sequential(
Timesteps(model_channels),
TimestepEmbedding(model_channels, model_channels, use_adaln_lora=use_adaln_lora),
)
self.t_embedding_norm = nn.RMSNorm(model_channels, eps=1e-6)
# Patch embedding
embed_in_channels = in_channels + 1 if concat_padding_mask else in_channels
self.x_embedder = PatchEmbed(
spatial_patch_size=patch_spatial,
temporal_patch_size=patch_temporal,
in_channels=embed_in_channels,
out_channels=model_channels,
)
# Transformer blocks
self.blocks = nn.ModuleList(
[
DiTBlock(
x_dim=model_channels,
context_dim=crossattn_emb_channels,
num_heads=num_heads,
mlp_ratio=mlp_ratio,
use_adaln_lora=use_adaln_lora,
adaln_lora_dim=adaln_lora_dim,
)
for _ in range(num_blocks)
]
)
# Final output layer
self.final_layer = FinalLayer(
hidden_size=model_channels,
spatial_patch_size=patch_spatial,
temporal_patch_size=patch_temporal,
out_channels=out_channels,
use_adaln_lora=use_adaln_lora,
adaln_lora_dim=adaln_lora_dim,
)
def _pad_to_patch_size(self, x: torch.Tensor) -> torch.Tensor:
"""Pad input tensor so dimensions are divisible by patch sizes."""
_, _, T, H, W = x.shape
pad_t = (self.patch_temporal - T % self.patch_temporal) % self.patch_temporal
pad_h = (self.patch_spatial - H % self.patch_spatial) % self.patch_spatial
pad_w = (self.patch_spatial - W % self.patch_spatial) % self.patch_spatial
if pad_t > 0 or pad_h > 0 or pad_w > 0:
x = F.pad(x, (0, pad_w, 0, pad_h, 0, pad_t))
return x
def prepare_embedded_sequence(
self,
x_B_C_T_H_W: torch.Tensor,
fps: Optional[torch.Tensor] = None,
padding_mask: Optional[torch.Tensor] = None,
) -> Tuple[torch.Tensor, Optional[torch.Tensor], Optional[torch.Tensor]]:
if self.concat_padding_mask:
if padding_mask is None:
padding_mask = torch.zeros(
x_B_C_T_H_W.shape[0],
1,
x_B_C_T_H_W.shape[3],
x_B_C_T_H_W.shape[4],
dtype=x_B_C_T_H_W.dtype,
device=x_B_C_T_H_W.device,
)
x_B_C_T_H_W = torch.cat(
[x_B_C_T_H_W, padding_mask.unsqueeze(1).repeat(1, 1, x_B_C_T_H_W.shape[2], 1, 1)], dim=1
)
x_B_T_H_W_D = self.x_embedder(x_B_C_T_H_W)
extra_pos_emb = None
if self.extra_per_block_abs_pos_emb:
extra_pos_emb = self.extra_pos_embedder(
x_B_T_H_W_D, fps=fps, device=x_B_C_T_H_W.device, dtype=x_B_C_T_H_W.dtype
)
if "rope" in self.pos_emb_cls.lower():
return x_B_T_H_W_D, self.pos_embedder(x_B_T_H_W_D, fps=fps, device=x_B_C_T_H_W.device), extra_pos_emb
return x_B_T_H_W_D, None, extra_pos_emb
def unpatchify(self, x_B_T_H_W_M: torch.Tensor) -> torch.Tensor:
return rearrange(
x_B_T_H_W_M,
"B T H W (p1 p2 t C) -> B C (T t) (H p1) (W p2)",
p1=self.patch_spatial,
p2=self.patch_spatial,
t=self.patch_temporal,
)
def forward(
self,
x: torch.Tensor,
timesteps: torch.Tensor,
context: torch.Tensor,
fps: Optional[torch.Tensor] = None,
padding_mask: Optional[torch.Tensor] = None,
**kwargs,
) -> torch.Tensor:
orig_shape = list(x.shape)
x = self._pad_to_patch_size(x)
x_B_T_H_W_D, rope_emb_L_1_1_D, extra_pos_emb = self.prepare_embedded_sequence(
x, fps=fps, padding_mask=padding_mask
)
if timesteps.ndim == 1:
timesteps = timesteps.unsqueeze(1)
t_emb, adaln_lora = self.t_embedder[1](self.t_embedder[0](timesteps).to(x_B_T_H_W_D.dtype))
t_emb = self.t_embedding_norm(t_emb)
block_kwargs = {
"rope_emb_L_1_1_D": rope_emb_L_1_1_D.unsqueeze(1).unsqueeze(0) if rope_emb_L_1_1_D is not None else None,
"adaln_lora_B_T_3D": adaln_lora,
"extra_per_block_pos_emb": extra_pos_emb,
}
# Keep residual stream in fp32 for numerical stability with fp16 compute
if x_B_T_H_W_D.dtype == torch.float16:
x_B_T_H_W_D = x_B_T_H_W_D.float()
for block in self.blocks:
x_B_T_H_W_D = block(x_B_T_H_W_D, t_emb, context, **block_kwargs)
x_out = self.final_layer(x_B_T_H_W_D.to(context.dtype), t_emb, adaln_lora_B_T_3D=adaln_lora)
x_out = self.unpatchify(x_out)[:, :, : orig_shape[-3], : orig_shape[-2], : orig_shape[-1]]
return x_out
# ============================================================================
# LLM Adapter
# Reference implementation: https://github.com/hdae/diffusers-anima
# SPDX-License-Identifier: Apache-2.0
# ============================================================================
def _rotate_half(x: torch.Tensor) -> torch.Tensor:
"""Split the last dimension in half and negate-swap: [-x2, x1]."""
half = x.shape[-1] // 2
first, second = x[..., :half], x[..., half:]
return torch.cat((-second, first), dim=-1)
def _apply_rope(x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor) -> torch.Tensor:
"""Apply rotary position embeddings to tensor x given precomputed cos/sin."""
return (x * cos.unsqueeze(1)) + (_rotate_half(x) * sin.unsqueeze(1))
class LLMAdapterRotaryEmbedding(nn.Module):
"""Rotary position embedding for the LLM Adapter's attention layers."""
def __init__(self, head_dim: int, theta: float = 10000.0):
super().__init__()
half_dim = head_dim // 2
index = torch.arange(half_dim, dtype=torch.float32)
exponent = (2.0 / float(head_dim)) * index
inv_freq = torch.reciprocal(torch.pow(torch.tensor(theta, dtype=torch.float32), exponent))
self.register_buffer("inv_freq", inv_freq, persistent=False)
def forward(self, x: torch.Tensor, position_ids: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
pos = position_ids.to(device=x.device, dtype=torch.float32)
inv = self.inv_freq.to(device=x.device, dtype=torch.float32)
freqs = torch.einsum("bl,d->bld", pos, inv)
emb = freqs.repeat(1, 1, 2)
return emb.cos().to(dtype=x.dtype), emb.sin().to(dtype=x.dtype)
class LLMAdapterAttention(nn.Module):
"""Attention for the LLM Adapter with QK normalization and rotary position embeddings."""
def __init__(self, query_dim: int, context_dim: int, n_heads: int, head_dim: int):
super().__init__()
inner_dim = head_dim * n_heads
self.n_heads = n_heads
self.head_dim = head_dim
self.q_proj = nn.Linear(query_dim, inner_dim, bias=False)
self.q_norm = nn.RMSNorm(head_dim, eps=1e-6)
self.k_proj = nn.Linear(context_dim, inner_dim, bias=False)
self.k_norm = nn.RMSNorm(head_dim, eps=1e-6)
self.v_proj = nn.Linear(context_dim, inner_dim, bias=False)
self.o_proj = nn.Linear(inner_dim, query_dim, bias=False)
def forward(
self,
x: torch.Tensor,
*,
context: Optional[torch.Tensor] = None,
attn_mask: Optional[torch.Tensor] = None,
pos_q: Optional[Tuple[torch.Tensor, torch.Tensor]] = None,
pos_k: Optional[Tuple[torch.Tensor, torch.Tensor]] = None,
) -> torch.Tensor:
context = x if context is None else context
q = self.q_proj(x).view(x.shape[0], x.shape[1], self.n_heads, self.head_dim).transpose(1, 2)
k = self.k_proj(context).view(context.shape[0], context.shape[1], self.n_heads, self.head_dim).transpose(1, 2)
v = self.v_proj(context).view(context.shape[0], context.shape[1], self.n_heads, self.head_dim).transpose(1, 2)
q = self.q_norm(q)
k = self.k_norm(k)
if pos_q is not None and pos_k is not None:
q = _apply_rope(q, *pos_q)
k = _apply_rope(k, *pos_k)
y = F.scaled_dot_product_attention(q, k, v, attn_mask=attn_mask)
y = y.transpose(1, 2).reshape(x.shape[0], x.shape[1], -1).contiguous()
return self.o_proj(y)
class LLMAdapterTransformerBlock(nn.Module):
"""Single transformer block in the LLM Adapter.
Each block contains self-attention, cross-attention, and MLP with
RMSNorm pre-normalization.
"""
def __init__(
self,
source_dim: int,
model_dim: int,
num_heads: int = 16,
):
super().__init__()
head_dim = model_dim // num_heads
self.norm_self_attn = nn.RMSNorm(model_dim, eps=1e-6)
self.self_attn = LLMAdapterAttention(model_dim, model_dim, num_heads, head_dim)
self.norm_cross_attn = nn.RMSNorm(model_dim, eps=1e-6)
self.cross_attn = LLMAdapterAttention(model_dim, source_dim, num_heads, head_dim)
self.norm_mlp = nn.RMSNorm(model_dim, eps=1e-6)
self.mlp = nn.Sequential(
nn.Linear(model_dim, model_dim * 4),
nn.GELU(),
nn.Linear(model_dim * 4, model_dim),
)
def forward(
self,
x: torch.Tensor,
*,
context: torch.Tensor,
target_mask: Optional[torch.Tensor] = None,
source_mask: Optional[torch.Tensor] = None,
pos_target: Tuple[torch.Tensor, torch.Tensor],
pos_source: Tuple[torch.Tensor, torch.Tensor],
) -> torch.Tensor:
x = x + self.self_attn(
self.norm_self_attn(x),
attn_mask=target_mask,
pos_q=pos_target,
pos_k=pos_target,
)
x = x + self.cross_attn(
self.norm_cross_attn(x),
context=context,
attn_mask=source_mask,
pos_q=pos_target,
pos_k=pos_source,
)
x = x + self.mlp(self.norm_mlp(x))
return x
class LLMAdapter(nn.Module):
"""LLM Adapter: bridges Qwen3 hidden states and T5-XXL token embeddings.
Takes Qwen3 hidden states and T5-XXL token IDs, produces conditioning
embeddings for the Cosmos DiT via cross-attention through 6 transformer layers.
Args:
vocab_size: Size of the T5 token vocabulary.
dim: Model dimension (used for embeddings, projections, and all layers).
num_layers: Number of transformer layers.
num_heads: Number of attention heads.
"""
def __init__(
self,
vocab_size: int = 32128,
dim: int = 1024,
num_layers: int = 6,
num_heads: int = 16,
):
super().__init__()
self.embed = nn.Embedding(vocab_size, dim)
self.blocks = nn.ModuleList(
[LLMAdapterTransformerBlock(source_dim=dim, model_dim=dim, num_heads=num_heads) for _ in range(num_layers)]
)
self.out_proj = nn.Linear(dim, dim)
self.norm = nn.RMSNorm(dim, eps=1e-6)
self.rotary_emb = LLMAdapterRotaryEmbedding(dim // num_heads)
def forward(
self,
source_hidden_states: torch.Tensor,
target_input_ids: torch.Tensor,
target_attention_mask: Optional[torch.Tensor] = None,
source_attention_mask: Optional[torch.Tensor] = None,
) -> torch.Tensor:
# Expand attention masks for multi-head attention
if target_attention_mask is not None:
target_attention_mask = target_attention_mask.to(torch.bool)
if target_attention_mask.ndim == 2:
target_attention_mask = target_attention_mask[:, None, None, :]
if source_attention_mask is not None:
source_attention_mask = source_attention_mask.to(torch.bool)
if source_attention_mask.ndim == 2:
source_attention_mask = source_attention_mask[:, None, None, :]
context = source_hidden_states
x = self.embed(target_input_ids).to(dtype=context.dtype)
# Build position IDs and compute rotary embeddings
target_pos_ids = torch.arange(x.shape[1], device=x.device, dtype=torch.long).unsqueeze(0)
source_pos_ids = torch.arange(context.shape[1], device=x.device, dtype=torch.long).unsqueeze(0)
pos_target = self.rotary_emb(x, target_pos_ids)
pos_source = self.rotary_emb(x, source_pos_ids)
for block in self.blocks:
x = block(
x,
context=context,
target_mask=target_attention_mask,
source_mask=source_attention_mask,
pos_target=pos_target,
pos_source=pos_source,
)
return self.norm(self.out_proj(x))
# ============================================================================
# Anima: MiniTrainDIT + LLMAdapter
# Reference implementation: https://github.com/hdae/diffusers-anima
# SPDX-License-Identifier: Apache-2.0
# ============================================================================
class AnimaTransformer(MiniTrainDIT):
"""Anima transformer: Cosmos Predict2 DiT with integrated LLM Adapter.
Extends MiniTrainDIT by adding the LLMAdapter component that preprocesses
text embeddings before they are fed to the DiT cross-attention layers.
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.llm_adapter = LLMAdapter()
def preprocess_text_embeds(
self,
text_embeds: torch.Tensor,
text_ids: Optional[torch.Tensor],
t5xxl_weights: Optional[torch.Tensor] = None,
) -> torch.Tensor:
"""Run the LLM Adapter to produce conditioning for the DiT.
Args:
text_embeds: Qwen3 hidden states. Shape: (batch, seq_len, 1024).
text_ids: T5-XXL token IDs. Shape: (batch, seq_len). If None, returns text_embeds directly.
t5xxl_weights: Optional per-token weights. Shape: (batch, seq_len, 1).
Returns:
Conditioning tensor. Shape: (batch, 512, 1024), zero-padded if needed.
"""
if text_ids is None:
return text_embeds
out = self.llm_adapter(text_embeds, text_ids)
if t5xxl_weights is not None:
out = out * t5xxl_weights
if out.shape[1] < 512:
out = F.pad(out, (0, 0, 0, 512 - out.shape[1]))
return out
def forward(
self,
x: torch.Tensor,
timesteps: torch.Tensor,
context: torch.Tensor,
t5xxl_ids: Optional[torch.Tensor] = None,
t5xxl_weights: Optional[torch.Tensor] = None,
**kwargs,
) -> torch.Tensor:
"""Forward pass with LLM Adapter preprocessing.
Args:
x: Input latent tensor. Shape: (B, C, T, H, W).
timesteps: Timestep values. Shape: (B,) or (B, T).
context: Qwen3 hidden states. Shape: (B, seq_len, 1024).
t5xxl_ids: T5-XXL token IDs. Shape: (B, seq_len).
t5xxl_weights: Per-token weights. Shape: (B, seq_len, 1).
Returns:
Denoised output. Shape: (B, C, T, H, W).
"""
if t5xxl_ids is not None:
context = self.preprocess_text_embeds(context, t5xxl_ids, t5xxl_weights=t5xxl_weights)
return super().forward(x, timesteps, context, **kwargs)