# Copyright 2025 The NVIDIA Team and The HuggingFace Team. All rights reserved. # # 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 dataclasses import dataclass import torch import torch.nn as nn from ...configuration_utils import ConfigMixin, register_to_config from ...loaders import PeftAdapterMixin from ...utils import BaseOutput from ..attention import AttentionMixin, AttentionModuleMixin from ..attention_dispatch import dispatch_attention_fn from ..embeddings import TimestepEmbedding, Timesteps from ..modeling_utils import ModelMixin from ..normalization import RMSNorm @dataclass class Cosmos3OmniTransformerOutput(BaseOutput): """Output of [`Cosmos3OmniTransformer`]. Args: sample (`list[torch.Tensor]`): Per-item vision velocity predictions. sound (`list[torch.Tensor]`, *optional*): Per-item sound velocity predictions when sound generation is enabled. action (`list[torch.Tensor]`, *optional*): Per-item action velocity predictions when action generation is enabled. """ sample: list[torch.Tensor] sound: list[torch.Tensor] | None = None action: list[torch.Tensor] | None = None class Cosmos3AttnProcessor: """Dual-pathway attention processor for Cosmos3. Projects, normalizes, applies rotary position embeddings, then runs separate causal (understanding) and full (generation) attention pathways. The generation pathway cross-attends to both und and gen keys/values. """ _attention_backend = None _parallel_config = None def __call__( self, attn: "Cosmos3PackedMoTAttention", und_seq: torch.Tensor, gen_seq: torch.Tensor, rotary_emb: tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor], ) -> tuple[torch.Tensor, torch.Tensor]: # Per-pathway projections q_und = attn.to_q(und_seq).view(-1, attn.num_attention_heads, attn.head_dim) k_und = attn.to_k(und_seq).view(-1, attn.num_key_value_heads, attn.head_dim) v_und = attn.to_v(und_seq).view(-1, attn.num_key_value_heads, attn.head_dim) q_gen = attn.add_q_proj(gen_seq).view(-1, attn.num_attention_heads, attn.head_dim) k_gen = attn.add_k_proj(gen_seq).view(-1, attn.num_key_value_heads, attn.head_dim) v_gen = attn.add_v_proj(gen_seq).view(-1, attn.num_key_value_heads, attn.head_dim) q_und = attn.norm_q(q_und) k_und = attn.norm_k(k_und) k_und_for_gen = attn.k_norm_und_for_gen(k_und) if attn.k_norm_und_for_gen is not None else k_und q_gen = attn.norm_added_q(q_gen) k_gen = attn.norm_added_k(k_gen) # Apply rotary position embeddings per pathway cos_und, sin_und, cos_gen, sin_gen = rotary_emb cos_und = cos_und.unsqueeze(1) sin_und = sin_und.unsqueeze(1) q_und = q_und * cos_und + _rotate_half(q_und) * sin_und k_und = k_und * cos_und + _rotate_half(k_und) * sin_und k_und_for_gen = k_und_for_gen * cos_und + _rotate_half(k_und_for_gen) * sin_und cos_gen = cos_gen.unsqueeze(1) sin_gen = sin_gen.unsqueeze(1) q_gen = q_gen * cos_gen + _rotate_half(q_gen) * sin_gen k_gen = k_gen * cos_gen + _rotate_half(k_gen) * sin_gen # Causal pathway (understanding): und tokens self-attend with causal masking. causal_out = dispatch_attention_fn( q_und.unsqueeze(0), k_und.unsqueeze(0), v_und.unsqueeze(0), is_causal=True, enable_gqa=True, backend=self._attention_backend, parallel_config=self._parallel_config, ) causal_out = causal_out.squeeze(0).flatten(-2, -1) # Full pathway (generation): gen tokens cross-attend to all (und + gen) keys/values. all_k = torch.cat([k_und_for_gen, k_gen], dim=0) all_v = torch.cat([v_und, v_gen], dim=0) full_out = dispatch_attention_fn( q_gen.unsqueeze(0), all_k.unsqueeze(0), all_v.unsqueeze(0), is_causal=False, enable_gqa=True, backend=self._attention_backend, parallel_config=self._parallel_config, ) full_out = full_out.squeeze(0).flatten(-2, -1) # Per-pathway output projection und_out = attn.to_out(causal_out) gen_out = attn.to_add_out(full_out) return und_out, gen_out def _rotate_half(x: torch.Tensor) -> torch.Tensor: half = x.shape[-1] // 2 return torch.cat((-x[..., half:], x[..., :half]), dim=-1) class Cosmos3VLTextRotaryEmbedding(nn.Module): def __init__(self, head_dim: int, rope_theta: float, rope_axes_dim: tuple[int, int, int]): super().__init__() inv_freq = 1.0 / (rope_theta ** (torch.arange(0, head_dim, 2, dtype=torch.float32) / head_dim)) self.register_buffer("inv_freq", inv_freq, persistent=False) self.rope_axes_dim = rope_axes_dim def apply_interleaved_mrope(self, freqs, rope_axes_dim): """Reorganize chunked [TTT...HHH...WWW] frequency layout into interleaved [THTHWHTHW...TT], preserving frequency continuity across the 3 grids.""" freqs_t = freqs[0] for dim, offset in enumerate((1, 2), start=1): # H, W length = rope_axes_dim[dim] * 3 idx = slice(offset, length, 3) freqs_t[..., idx] = freqs[dim, ..., idx] return freqs_t def forward(self, position_ids, device, dtype): if position_ids.ndim == 2: position_ids = position_ids[None, ...].expand(3, position_ids.shape[0], -1) # [3,B,N] inv_freq_expanded = ( self.inv_freq[None, None, :, None].float().expand(3, position_ids.shape[1], -1, 1).to(device) ) # [3,B,head_dim//2,1] position_ids_expanded = position_ids[:, :, None, :].float() # [3,B,1,N] # Disable autocast so the position-id matmul runs in float32: under an ambient autocast it would run in # bfloat16, which cannot represent consecutive integers past 256, collapsing positions onto the same # frequency and degrading the rotary embedding. with torch.autocast(device_type=position_ids.device.type, enabled=False): freqs = inv_freq_expanded @ position_ids_expanded freqs = freqs.transpose(2, 3) # [3,B,N,head_dim//2] freqs = self.apply_interleaved_mrope(freqs, self.rope_axes_dim) # [B,N,head_dim//2] emb = torch.cat((freqs, freqs), dim=-1) # [B,N,head_dim] return emb.cos().to(dtype=dtype), emb.sin().to(dtype=dtype) # each: [B,N,head_dim] class Cosmos3NemotronRMSNorm(nn.Module): def __init__(self, dim: int, eps: float): super().__init__() self.eps = eps self.weight = nn.Parameter(torch.ones(dim)) def forward(self, hidden_states: torch.Tensor) -> torch.Tensor: input_dtype = hidden_states.dtype hidden_states = hidden_states.float() variance = hidden_states.pow(2).mean(-1, keepdim=True) hidden_states = hidden_states * torch.rsqrt(variance + self.eps) return (self.weight.float() * hidden_states).to(input_dtype) class Cosmos3VLTextMLP(nn.Module): def __init__(self, hidden_size: int, intermediate_size: int, hidden_act: str = "silu"): super().__init__() if hidden_act not in ("relu2", "silu"): raise ValueError(f"Cosmos3 only supports `hidden_act` values 'relu2' and 'silu', got {hidden_act!r}.") self.hidden_act = hidden_act if hidden_act == "silu": self.gate_proj = nn.Linear(hidden_size, intermediate_size, bias=False) self.up_proj = nn.Linear(hidden_size, intermediate_size, bias=False) self.down_proj = nn.Linear(intermediate_size, hidden_size, bias=False) self.act_fn = nn.SiLU() if hidden_act == "silu" else None def forward(self, x): if self.hidden_act == "relu2": return self.down_proj(torch.relu(self.up_proj(x)).square()) return self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x)) class DomainAwareLinear(nn.Module): """Linear projection with one weight/bias pair per embodiment domain.""" def __init__(self, input_size: int, output_size: int, num_domains: int) -> None: super().__init__() self.input_size = input_size self.output_size = output_size self.num_domains = num_domains self.fc = nn.Embedding(self.num_domains, self.output_size * self.input_size) self.bias = nn.Embedding(self.num_domains, self.output_size) def forward(self, x: torch.Tensor, domain_id: torch.Tensor) -> torch.Tensor: if domain_id.ndim == 0: domain_id = domain_id.unsqueeze(0) domain_id = domain_id.to(device=x.device, dtype=torch.long).reshape(-1) if x.shape[0] != domain_id.shape[0]: raise ValueError( "Cosmos3 action domain_id batch size must match action tokens: " f"tokens={x.shape[0]}, domain_id={domain_id.shape[0]}." ) if torch.any((domain_id < 0) | (domain_id >= self.num_domains)): raise ValueError(f"Cosmos3 action domain_id must be in [0, {self.num_domains}), got {domain_id.tolist()}.") weight = self.fc(domain_id).view(domain_id.shape[0], self.input_size, self.output_size) bias = self.bias(domain_id).view(domain_id.shape[0], self.output_size) if x.ndim == 2: return torch.bmm(x.unsqueeze(1), weight).squeeze(1) + bias if x.ndim == 3: return torch.bmm(x, weight) + bias.unsqueeze(1) raise ValueError(f"Cosmos3 DomainAwareLinear expected rank-2 or rank-3 input, got {tuple(x.shape)}.") class Cosmos3PackedMoTAttention(nn.Module, AttentionModuleMixin): """Dual-pathway packed attention with separate projections for the understanding and generation token streams.""" _default_processor_cls = Cosmos3AttnProcessor _available_processors = [Cosmos3AttnProcessor] _supports_qkv_fusion = False def __init__( self, hidden_size: int, head_dim: int, num_attention_heads: int, num_key_value_heads: int, attention_bias: bool, rms_norm_eps: float, qk_norm_for_text: bool = True, use_und_k_norm_for_gen: bool = False, norm_type: str = "rms_norm", processor=None, ): super().__init__() self.hidden_size = hidden_size self.head_dim = head_dim self.num_attention_heads = num_attention_heads self.num_key_value_heads = num_key_value_heads self.num_key_value_groups = num_attention_heads // num_key_value_heads # Understanding pathway. norm_q / norm_k are applied per-head (only on # head_dim), so no reshape is needed after them. self.to_q = nn.Linear(hidden_size, num_attention_heads * head_dim, bias=attention_bias) self.to_k = nn.Linear(hidden_size, num_key_value_heads * head_dim, bias=attention_bias) self.to_v = nn.Linear(hidden_size, num_key_value_heads * head_dim, bias=attention_bias) self.to_out = nn.Linear(num_attention_heads * head_dim, hidden_size, bias=attention_bias) if not qk_norm_for_text: self.norm_q = nn.Identity() self.norm_k = nn.Identity() elif norm_type == "nemotron_rms_norm": self.norm_q = Cosmos3NemotronRMSNorm(head_dim, eps=rms_norm_eps) self.norm_k = Cosmos3NemotronRMSNorm(head_dim, eps=rms_norm_eps) else: self.norm_q = RMSNorm(head_dim, eps=rms_norm_eps, elementwise_affine=True, bias=False) self.norm_k = RMSNorm(head_dim, eps=rms_norm_eps, elementwise_affine=True, bias=False) if use_und_k_norm_for_gen and not qk_norm_for_text: if norm_type == "nemotron_rms_norm": self.k_norm_und_for_gen = Cosmos3NemotronRMSNorm(head_dim, eps=rms_norm_eps) else: self.k_norm_und_for_gen = RMSNorm(head_dim, eps=rms_norm_eps, elementwise_affine=True, bias=False) else: self.k_norm_und_for_gen = None # Generation pathway self.add_q_proj = nn.Linear(hidden_size, num_attention_heads * head_dim, bias=attention_bias) self.add_k_proj = nn.Linear(hidden_size, num_key_value_heads * head_dim, bias=attention_bias) self.add_v_proj = nn.Linear(hidden_size, num_key_value_heads * head_dim, bias=attention_bias) self.to_add_out = nn.Linear(num_attention_heads * head_dim, hidden_size, bias=attention_bias) if norm_type == "nemotron_rms_norm": self.norm_added_q = Cosmos3NemotronRMSNorm(head_dim, eps=rms_norm_eps) self.norm_added_k = Cosmos3NemotronRMSNorm(head_dim, eps=rms_norm_eps) else: self.norm_added_q = RMSNorm(head_dim, eps=rms_norm_eps, elementwise_affine=True, bias=False) self.norm_added_k = RMSNorm(head_dim, eps=rms_norm_eps, elementwise_affine=True, bias=False) if processor is None: processor = self._default_processor_cls() self.set_processor(processor) def forward( self, und_seq: torch.Tensor, gen_seq: torch.Tensor, rotary_emb: tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor], ) -> tuple[torch.Tensor, torch.Tensor]: return self.processor(self, und_seq, gen_seq, rotary_emb) class Cosmos3VLTextMoTDecoderLayer(nn.Module): """Cosmos3 text MoT decoder layer for the Qwen3 and Nemotron dense backbones.""" def __init__( self, hidden_size: int, head_dim: int, num_attention_heads: int, num_key_value_heads: int, intermediate_size: int, attention_bias: bool, rms_norm_eps: float, hidden_act: str = "silu", qk_norm_for_text: bool = True, use_und_k_norm_for_gen: bool = False, ): super().__init__() self.hidden_size = hidden_size norm_type = "nemotron_rms_norm" if hidden_act == "relu2" else "rms_norm" self.self_attn = Cosmos3PackedMoTAttention( hidden_size=hidden_size, head_dim=head_dim, num_attention_heads=num_attention_heads, num_key_value_heads=num_key_value_heads, attention_bias=attention_bias, rms_norm_eps=rms_norm_eps, qk_norm_for_text=qk_norm_for_text, use_und_k_norm_for_gen=use_und_k_norm_for_gen, norm_type=norm_type, ) self.mlp = Cosmos3VLTextMLP( hidden_size=hidden_size, intermediate_size=intermediate_size, hidden_act=hidden_act ) self.mlp_moe_gen = Cosmos3VLTextMLP( hidden_size=hidden_size, intermediate_size=intermediate_size, hidden_act=hidden_act ) if norm_type == "nemotron_rms_norm": self.input_layernorm = Cosmos3NemotronRMSNorm(hidden_size, eps=rms_norm_eps) self.input_layernorm_moe_gen = Cosmos3NemotronRMSNorm(hidden_size, eps=rms_norm_eps) self.post_attention_layernorm = Cosmos3NemotronRMSNorm(hidden_size, eps=rms_norm_eps) self.post_attention_layernorm_moe_gen = Cosmos3NemotronRMSNorm(hidden_size, eps=rms_norm_eps) else: self.input_layernorm = RMSNorm(hidden_size, eps=rms_norm_eps, elementwise_affine=True, bias=False) self.input_layernorm_moe_gen = RMSNorm(hidden_size, eps=rms_norm_eps, elementwise_affine=True, bias=False) self.post_attention_layernorm = RMSNorm(hidden_size, eps=rms_norm_eps, elementwise_affine=True, bias=False) self.post_attention_layernorm_moe_gen = RMSNorm( hidden_size, eps=rms_norm_eps, elementwise_affine=True, bias=False ) def forward( self, und_seq: torch.Tensor, gen_seq: torch.Tensor, rotary_emb: tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor], ) -> tuple[torch.Tensor, torch.Tensor]: und_norm = self.input_layernorm(und_seq) gen_norm = self.input_layernorm_moe_gen(gen_seq) und_attn_out, gen_attn_out = self.self_attn(und_norm, gen_norm, rotary_emb) residual_und = und_seq + und_attn_out residual_gen = gen_seq + gen_attn_out mlp_out_und = self.mlp(self.post_attention_layernorm(residual_und)) mlp_out_gen = self.mlp_moe_gen(self.post_attention_layernorm_moe_gen(residual_gen)) return residual_und + mlp_out_und, residual_gen + mlp_out_gen class Cosmos3OmniTransformer(ModelMixin, ConfigMixin, PeftAdapterMixin, AttentionMixin): _supports_gradient_checkpointing = True _no_split_modules = ["Cosmos3VLTextMoTDecoderLayer"] _repeated_blocks = ["Cosmos3VLTextMoTDecoderLayer"] _skip_layerwise_casting_patterns = ["embed_tokens", "time_embedder", "norm"] _keep_in_fp32_modules = ["time_embedder"] # Optional context-parallelism seams. They default to ``None`` (no-op) so the # model itself carries no CP logic. `forward` applies `_cp_shard_fn` to the # per-pathway hidden states + rotary embeddings before the decoder layers, and # `_cp_gather_fn` to the per-pathway outputs after the final norm. An external # helper (see `examples/cosmos3/cosmos_parallel.py`) sets these to # shard/gather across a device mesh and installs a context-parallel attention # processor — the packed dual-pathway + GQA + ragged-length structure cannot be # expressed as diffusers' declarative `_cp_plan`, so CP lives outside the model. _cp_shard_fn = None _cp_gather_fn = None # `dtype` is injected into init_dict by ModelMixin.from_pretrained (configuration_utils.py:289), # so __init__ must accept it. Excluding it here keeps save_pretrained from writing it into # config.json — the value is a load-time runtime hint, not part of the model architecture. ignore_for_config = ["dtype"] @register_to_config def __init__( self, attention_bias: bool = False, attention_dropout: float = 0.0, dtype: str = "bfloat16", # required by the loader (see `ignore_for_config` above); not read here head_dim: int = 128, hidden_size: int = 4096, intermediate_size: int = 12288, base_fps: int = 24, enable_fps_modulation: bool = True, latent_channel: int = 48, unified_3d_mrope_reset_spatial_ids: bool = True, unified_3d_mrope_temporal_modality_margin: int = 15000, latent_patch_size: int = 2, num_attention_heads: int = 32, num_hidden_layers: int = 36, num_key_value_heads: int = 8, patch_latent_dim: int = 192, rms_norm_eps: float = 1e-6, rope_scaling: dict | None = None, rope_theta: float = 5000000.0, action_dim: int | None = None, action_gen: bool = False, num_embodiment_domains: int = 32, sound_dim: int | None = None, sound_gen: bool = False, sound_latent_fps: float = 25.0, timestep_scale: float = 0.001, vocab_size: int = 151936, hidden_act: str = "silu", qk_norm_for_text: bool = True, use_und_k_norm_for_gen: bool = False, rope_axes_dim: tuple[int, int, int] | list[int] | None = None, ): super().__init__() if rope_axes_dim is None: rope_axes_dim = ( rope_scaling.get("mrope_section", [24, 20, 20]) if rope_scaling is not None else [24, 20, 20] ) self.register_to_config(rope_axes_dim=rope_axes_dim) # Text-model layers live directly on the transformer (flat layout). The published # checkpoint must be re-keyed with the leading `model.` prefix stripped — see # scripts/build_flat_layout_repo.py for the rewrite. self.embed_tokens = nn.Embedding(vocab_size, hidden_size) self.layers = nn.ModuleList( [ Cosmos3VLTextMoTDecoderLayer( hidden_size=hidden_size, head_dim=head_dim, num_attention_heads=num_attention_heads, num_key_value_heads=num_key_value_heads, intermediate_size=intermediate_size, attention_bias=attention_bias, rms_norm_eps=rms_norm_eps, hidden_act=hidden_act, qk_norm_for_text=qk_norm_for_text, use_und_k_norm_for_gen=use_und_k_norm_for_gen, ) for _ in range(num_hidden_layers) ] ) if hidden_act == "relu2": self.norm = Cosmos3NemotronRMSNorm(hidden_size, eps=rms_norm_eps) self.norm_moe_gen = Cosmos3NemotronRMSNorm(hidden_size, eps=rms_norm_eps) else: self.norm = RMSNorm(hidden_size, eps=rms_norm_eps, elementwise_affine=True, bias=False) self.norm_moe_gen = RMSNorm(hidden_size, eps=rms_norm_eps, elementwise_affine=True, bias=False) self.rotary_emb = Cosmos3VLTextRotaryEmbedding( head_dim=head_dim, rope_theta=rope_theta, rope_axes_dim=rope_axes_dim ) # Modality projection heads + timestep embedding. self.vocab_size = vocab_size self.lm_head = nn.Linear(hidden_size, vocab_size, bias=False) self.proj_in = nn.Linear(patch_latent_dim, hidden_size, bias=True) self.proj_out = nn.Linear(hidden_size, patch_latent_dim, bias=True) self.time_proj = Timesteps(num_channels=256, flip_sin_to_cos=True, downscale_freq_shift=0) self.time_embedder = TimestepEmbedding(in_channels=256, time_embed_dim=hidden_size) self.action_gen = action_gen self.action_dim = action_dim self.num_embodiment_domains = num_embodiment_domains if action_gen: if self.action_dim is None: raise ValueError("`action_dim` must be provided when `action_gen=True`.") self.action_proj_in = DomainAwareLinear(self.action_dim, hidden_size, self.num_embodiment_domains) self.action_proj_out = DomainAwareLinear(hidden_size, self.action_dim, self.num_embodiment_domains) self.action_modality_embed = nn.Parameter(torch.zeros(hidden_size)) if sound_gen: if sound_dim is None: raise ValueError("`sound_dim` must be provided when `sound_gen=True`.") self.audio_proj_in = nn.Linear(sound_dim, hidden_size, bias=True) self.audio_proj_out = nn.Linear(hidden_size, sound_dim, bias=True) self.audio_modality_embed = nn.Parameter(torch.zeros(hidden_size)) self.gradient_checkpointing = False # ------------------------------------------------------------------------- # Pure-tensor packing/unpacking helpers (no layer state). # ------------------------------------------------------------------------- def _apply_timestep_embeds_to_noisy_tokens( self, packed_tokens: torch.Tensor, packed_timestep_embeds: torch.Tensor, noisy_frame_indexes: list[torch.Tensor], token_shapes: list[tuple[int, ...]], ) -> torch.Tensor: start_noisy_index = 0 flattened_noisy_frame_indexes: list[torch.Tensor] = [] for noisy_indexes_i, token_shape_i in zip(noisy_frame_indexes, token_shapes): spatial_numel_i = math.prod(token_shape_i[1:]) spatial_indexes_i = torch.arange(spatial_numel_i, device=packed_tokens.device) # Broadcast [N, 1] + [spatial_numel_i] → [N, spatial_numel_i] frame_offsets = (noisy_indexes_i * spatial_numel_i).unsqueeze(-1) + spatial_indexes_i + start_noisy_index flattened_noisy_frame_indexes.append(frame_offsets.flatten()) start_noisy_index += token_shape_i[0] * spatial_numel_i flattened = torch.cat(flattened_noisy_frame_indexes, dim=0).unsqueeze(-1).expand(-1, packed_tokens.shape[1]) return packed_tokens.scatter_add(dim=0, index=flattened, src=packed_timestep_embeds) def _patchify_and_pack_latents( self, tokens_vision: list[torch.Tensor], ) -> tuple[torch.Tensor, list[tuple[int, int, int]]]: p = self.config.latent_patch_size latent_channel = self.config.latent_channel packed_latent: list[torch.Tensor] = [] original_latent_shapes: list[tuple[int, int, int]] = [] for latent in tokens_vision: latent = latent.squeeze(0) # [C, T, H, W] _, t_actual, h_actual, w_actual = latent.shape original_latent_shapes.append((t_actual, h_actual, w_actual)) h_padded = ((h_actual + p - 1) // p) * p w_padded = ((w_actual + p - 1) // p) * p if h_padded != h_actual or w_padded != w_actual: padded = torch.zeros( (latent_channel, t_actual, h_padded, w_padded), device=latent.device, dtype=latent.dtype, ) padded[:, :, :h_actual, :w_actual] = latent latent = padded h_patches = h_padded // p w_patches = w_padded // p latent = latent.reshape(latent_channel, t_actual, h_patches, p, w_patches, p) latent = torch.einsum("cthpwq->thwpqc", latent).reshape(-1, p * p * latent_channel) packed_latent.append(latent) return torch.cat(packed_latent, dim=0), original_latent_shapes def _unpatchify_and_unpack_latents( self, packed_mse_preds: torch.Tensor, token_shapes_vision: list[tuple[int, int, int]], noisy_frame_indexes_vision: list[torch.Tensor], original_latent_shapes: list[tuple[int, int, int]], ) -> list[torch.Tensor]: p = self.config.latent_patch_size latent_channel = self.config.latent_channel unpatchified_latents: list[torch.Tensor] = [] start_idx = 0 for token_shape, noisy_frame_indexes, original_shape in zip( token_shapes_vision, noisy_frame_indexes_vision, original_latent_shapes ): t_c = token_shape[0] _, h_orig, w_orig = original_shape h_padded = ((h_orig + p - 1) // p) * p w_padded = ((w_orig + p - 1) // p) * p h_patches = h_padded // p w_patches = w_padded // p t_n = len(noisy_frame_indexes) output_tensor = torch.zeros( (latent_channel, t_c, h_orig, w_orig), device=packed_mse_preds.device, dtype=packed_mse_preds.dtype, ) num_patches = t_n * h_patches * w_patches if num_patches > 0: end_idx = start_idx + num_patches latent_patches = packed_mse_preds[start_idx:end_idx] latent_patches = latent_patches.reshape(t_n, h_patches, w_patches, p, p, latent_channel) latent = torch.einsum("thwpqc->cthpwq", latent_patches) latent = latent.reshape(latent_channel, t_n, h_patches * p, w_patches * p) latent = latent[:, :, :h_orig, :w_orig] output_tensor[:, noisy_frame_indexes] = latent start_idx = end_idx unpatchified_latents.append(output_tensor.unsqueeze(0)) return unpatchified_latents def _pack_sound_latents( self, tokens_sound: list[torch.Tensor], token_shapes_sound: list[tuple[int, int, int]], ) -> torch.Tensor: """List of ``[C, T]`` tensors → packed ``[total_T, C]`` tensor.""" return torch.cat( [sound[:, : shape[0]].permute(1, 0) for sound, shape in zip(tokens_sound, token_shapes_sound)], dim=0, ) def _unpack_sound_latents( self, packed_preds: torch.Tensor, token_shapes_sound: list[tuple[int, int, int]], noisy_frame_indexes_sound: list[torch.Tensor], ) -> list[torch.Tensor]: """Packed ``[total_noisy_T, C]`` predictions → list of ``[C, T]`` tensors (zeros at conditioned positions).""" sound_dim = self.config.sound_dim unpacked: list[torch.Tensor] = [] start_idx = 0 for shape, noisy_idxs in zip(token_shapes_sound, noisy_frame_indexes_sound): T = shape[0] output = torch.zeros((sound_dim, T), device=packed_preds.device, dtype=packed_preds.dtype) t_n = len(noisy_idxs) if t_n > 0: output[:, noisy_idxs] = packed_preds[start_idx : start_idx + t_n].T start_idx += t_n unpacked.append(output) return unpacked def _pack_action_latents( self, tokens_action: list[torch.Tensor], token_shapes_action: list[tuple[int, int, int]], domain_ids_action: list[torch.Tensor], ) -> tuple[torch.Tensor, torch.Tensor]: """List of ``[T, D]`` tensors → packed ``[total_T, D]`` plus per-token domain ids.""" packed: list[torch.Tensor] = [] domain_ids: list[torch.Tensor] = [] for action, shape, domain_id in zip(tokens_action, token_shapes_action, domain_ids_action): token_count = shape[0] packed.append(action[:token_count]) domain_ids.append(domain_id.reshape(1).expand(token_count)) return torch.cat(packed, dim=0), torch.cat(domain_ids, dim=0) def _unpack_action_latents( self, packed_preds: torch.Tensor, token_shapes_action: list[tuple[int, int, int]], noisy_frame_indexes_action: list[torch.Tensor], ) -> list[torch.Tensor]: """Packed ``[total_noisy_T, D]`` predictions → list of ``[T, D]`` tensors.""" unpacked: list[torch.Tensor] = [] start_idx = 0 for shape, noisy_idxs in zip(token_shapes_action, noisy_frame_indexes_action): T = shape[0] output = torch.zeros((T, self.action_dim), device=packed_preds.device, dtype=packed_preds.dtype) t_n = len(noisy_idxs) if t_n > 0: output[noisy_idxs] = packed_preds[start_idx : start_idx + t_n] start_idx += t_n unpacked.append(output) return unpacked # ------------------------------------------------------------------------- # forward: full per-step pass — encode text/vision/sound/action → run layers → # decode vision/sound/action. Pipeline calls this once per CFG pass. # ------------------------------------------------------------------------- def forward( self, input_ids: torch.Tensor, text_indexes: torch.Tensor, position_ids: torch.Tensor, und_len: int, sequence_length: int, vision_tokens: list[torch.Tensor], vision_token_shapes: list[tuple[int, int, int]], vision_sequence_indexes: torch.Tensor, vision_mse_loss_indexes: torch.Tensor, vision_timesteps: torch.Tensor, vision_noisy_frame_indexes: list[torch.Tensor], sound_tokens: list[torch.Tensor] | None = None, sound_token_shapes: list[tuple[int, int, int]] | None = None, sound_sequence_indexes: torch.Tensor | None = None, sound_mse_loss_indexes: torch.Tensor | None = None, sound_timesteps: torch.Tensor | None = None, sound_noisy_frame_indexes: list[torch.Tensor] | None = None, action_tokens: list[torch.Tensor] | None = None, action_token_shapes: list[tuple[int, int, int]] | None = None, action_sequence_indexes: torch.Tensor | None = None, action_mse_loss_indexes: torch.Tensor | None = None, action_timesteps: torch.Tensor | None = None, action_noisy_frame_indexes: list[torch.Tensor] | None = None, action_domain_ids: list[torch.Tensor] | None = None, return_dict: bool = True, ) -> ( Cosmos3OmniTransformerOutput | tuple[list[torch.Tensor], list[torch.Tensor] | None, list[torch.Tensor] | None] ): """Run a full denoising-step forward pass. Args: input_ids: Text token IDs placed at ``text_indexes`` in the joint sequence. text_indexes: Indices of text tokens in the joint sequence. position_ids: ``[3, sequence_length]`` mRoPE position IDs for the full joint sequence. und_len: Length of the causal text (understanding) prefix; generation tokens follow. sequence_length: Total length of the joint packed sequence. vision_tokens: Per-item vision latent tensors before patchify. vision_token_shapes: Patch grid shapes ``(T, H, W)`` per vision item. vision_sequence_indexes: Indices of vision tokens in the joint sequence. vision_mse_loss_indexes: Indices used to read vision predictions after the backbone. vision_timesteps: Per-patch diffusion timesteps for vision tokens. vision_noisy_frame_indexes: Noisy frame indices per vision item. sound_tokens: Optional sound latent tensors before packing. sound_token_shapes: Optional patch grid shapes for sound items. sound_sequence_indexes: Optional indices of sound tokens in the joint sequence. sound_mse_loss_indexes: Optional indices used to read sound predictions. sound_timesteps: Optional per-token diffusion timesteps for sound. sound_noisy_frame_indexes: Optional noisy frame indices per sound item. action_tokens: Optional action latent tensors before packing. action_token_shapes: Optional patch grid shapes ``(T, H, W)`` per action item. action_sequence_indexes: Optional indices of action tokens in the joint sequence. action_mse_loss_indexes: Optional indices used to read action predictions after the backbone. action_timesteps: Optional per-token diffusion timesteps for action tokens. action_noisy_frame_indexes: Optional noisy frame indices per action item. action_domain_ids: Optional per-item domain IDs selecting the action head weights. return_dict: Whether to return a [`Cosmos3OmniTransformerOutput`] instead of a tuple. Returns: A [`Cosmos3OmniTransformerOutput`] or a tuple of per-modality prediction lists. Optional modalities return ``None`` when their inputs are omitted. """ has_sound = sound_tokens is not None and sound_sequence_indexes is not None has_action = action_tokens is not None and action_sequence_indexes is not None # Embed text tokens into the joint hidden_states buffer at their sequence positions. packed_text_embedding = self.embed_tokens(input_ids) target_dtype = packed_text_embedding.dtype hidden_states = packed_text_embedding.new_zeros(size=(sequence_length, self.config.hidden_size)) hidden_states[text_indexes] = packed_text_embedding # Patchify + project vision latents, then add timestep embeddings to noisy frames. packed_tokens_vision, original_latent_shapes = self._patchify_and_pack_latents(vision_tokens) packed_tokens_vision = self.proj_in(packed_tokens_vision) timesteps_vision = vision_timesteps * self.config.timestep_scale time_embedder_dtype = next(self.time_embedder.parameters()).dtype packed_timestep_embeds_vision = self.time_embedder(self.time_proj(timesteps_vision).to(time_embedder_dtype)) packed_timestep_embeds_vision = packed_timestep_embeds_vision.to(target_dtype) packed_tokens_vision = self._apply_timestep_embeds_to_noisy_tokens( packed_tokens=packed_tokens_vision, packed_timestep_embeds=packed_timestep_embeds_vision, noisy_frame_indexes=vision_noisy_frame_indexes, token_shapes=vision_token_shapes, ) hidden_states[vision_sequence_indexes] = packed_tokens_vision # Pack + project sound latents (when present); all sound frames are noisy. if has_sound: packed_tokens_sound = self._pack_sound_latents(sound_tokens, sound_token_shapes).to(target_dtype) packed_tokens_sound = self.audio_proj_in(packed_tokens_sound) + self.audio_modality_embed timesteps_sound = sound_timesteps * self.config.timestep_scale packed_timestep_embeds_sound = self.time_embedder(self.time_proj(timesteps_sound).to(time_embedder_dtype)) packed_timestep_embeds_sound = packed_timestep_embeds_sound.to(target_dtype) packed_tokens_sound = self._apply_timestep_embeds_to_noisy_tokens( packed_tokens=packed_tokens_sound, packed_timestep_embeds=packed_timestep_embeds_sound, noisy_frame_indexes=sound_noisy_frame_indexes, token_shapes=sound_token_shapes, ) hidden_states[sound_sequence_indexes] = packed_tokens_sound # Pack + project action latents (when present). Domain ids select the action head weights. if has_action: packed_tokens_action, per_token_domain_ids = self._pack_action_latents( action_tokens, action_token_shapes, action_domain_ids ) packed_tokens_action = packed_tokens_action.to(target_dtype) per_token_domain_ids = per_token_domain_ids.to(device=packed_tokens_action.device) packed_tokens_action = self.action_proj_in(packed_tokens_action, per_token_domain_ids) packed_tokens_action = packed_tokens_action + self.action_modality_embed if action_mse_loss_indexes.numel() > 0: timesteps_action = action_timesteps * self.config.timestep_scale packed_timestep_embeds_action = self.time_embedder( self.time_proj(timesteps_action).to(time_embedder_dtype) ) packed_timestep_embeds_action = packed_timestep_embeds_action.to(target_dtype) packed_tokens_action = self._apply_timestep_embeds_to_noisy_tokens( packed_tokens=packed_tokens_action, packed_timestep_embeds=packed_timestep_embeds_action, noisy_frame_indexes=action_noisy_frame_indexes, token_shapes=action_token_shapes, ) hidden_states[action_sequence_indexes] = packed_tokens_action # Compute rotary embeddings once for the joint sequence, then slice into und/gen halves. _meta_tensor = torch.tensor([], dtype=hidden_states.dtype, device=hidden_states.device) cos, sin = self.rotary_emb( position_ids=position_ids.unsqueeze(0) if position_ids.ndim == 1 else position_ids.unsqueeze(1), device=hidden_states.device, dtype=hidden_states.dtype, ) # cos, sin: [1, N, head_dim] (1-D pos_ids) or [3, 1, N, head_dim] (mrope pos_ids) cos = cos.squeeze(0) sin = sin.squeeze(0) und_seq = hidden_states[:und_len] gen_seq = hidden_states[und_len:] rotary_emb = (cos[:und_len], sin[:und_len], cos[und_len:], sin[und_len:]) # Optional context-parallelism shard seam (no-op unless set by an external # helper, e.g. `examples/cosmos3/cosmos_parallel.py`). When set, it # shards each pathway's sequence and rotary embeddings across a device mesh, so # the decoder layers below run on local sequence shards. if self._cp_shard_fn is not None: und_seq, gen_seq, rotary_emb = self._cp_shard_fn(und_seq, gen_seq, rotary_emb) for decoder_layer in self.layers: if torch.is_grad_enabled() and self.gradient_checkpointing: und_seq, gen_seq = self._gradient_checkpointing_func( decoder_layer.__call__, und_seq, gen_seq, rotary_emb ) else: und_seq, gen_seq = decoder_layer(und_seq, gen_seq, rotary_emb) und_out = self.norm(und_seq) gen_out = self.norm_moe_gen(gen_seq) # Optional context-parallelism gather seam: re-gather the full per-pathway # sequence on every rank (and drop the padding) before the global-index decode # below, since the downstream indexes address positions in the unpadded joint # sequence. No-op unless `_cp_shard_fn`'s counterpart is set. if self._cp_gather_fn is not None: und_out, gen_out = self._cp_gather_fn(und_out, gen_out) last_hidden_state = torch.cat([und_out, gen_out], dim=0) # Decode vision predictions from the joint hidden state. preds_vision_packed = self.proj_out(last_hidden_state[vision_mse_loss_indexes]) preds_vision = self._unpatchify_and_unpack_latents( preds_vision_packed, token_shapes_vision=vision_token_shapes, noisy_frame_indexes_vision=vision_noisy_frame_indexes, original_latent_shapes=original_latent_shapes, ) preds_sound: list[torch.Tensor] | None = None if has_sound: preds_sound_packed = self.audio_proj_out(last_hidden_state[sound_mse_loss_indexes]) preds_sound = self._unpack_sound_latents(preds_sound_packed, sound_token_shapes, sound_noisy_frame_indexes) preds_action: list[torch.Tensor] | None = None if has_action: per_noisy_domain_ids = [ domain_id.reshape(1).expand(len(noisy_idxs)) for domain_id, noisy_idxs in zip(action_domain_ids, action_noisy_frame_indexes) ] per_noisy_domain_ids = torch.cat(per_noisy_domain_ids, dim=0).to(device=last_hidden_state.device) preds_action_packed = self.action_proj_out( last_hidden_state[action_mse_loss_indexes], per_noisy_domain_ids ) preds_action = self._unpack_action_latents( preds_action_packed, action_token_shapes, action_noisy_frame_indexes ) if not return_dict: return preds_vision, preds_sound, preds_action return Cosmos3OmniTransformerOutput(sample=preds_vision, sound=preds_sound, action=preds_action)