sunandbean's picture
Add files using upload-large-folder tool
629b123 verified
Raw History Blame Contribute Delete
27.3 kB
# SPDX-FileCopyrightText: © 2025 Tenstorrent USA, Inc.
# SPDX-License-Identifier: Apache-2.0
import math
from functools import partial
import torch
import ttnn
from ..parallel.manager import CCLManager
from ..utils.tensor import bf16_tensor, typed_tensor, unflatten
from .linear import ColParallelLinear, Linear, RowParallelLinear
from .module import Module, Parameter
# TODO: Fuse with linear instead
ACT2CLS = {
"swish": ttnn.swish,
"silu": ttnn.silu,
"mish": ttnn.mish,
"gelu": ttnn.gelu,
"gelu_tanh": partial(ttnn.gelu, fast_and_approximate_mode=True),
"relu": ttnn.relu,
}
# Positional Encoding.
class Timesteps(Module):
def __init__(
self,
num_channels: int,
cos_first: bool = True,
max_period: int = 10000,
downscale_freq_shift: float = 0,
scale: int = 1,
dtype: ttnn.DataType = ttnn.bfloat16,
mesh_device=None,
):
super().__init__()
self.num_channels = num_channels
self.cos_first = cos_first
self.downscale_freq_shift = downscale_freq_shift
self.scale = scale
self.max_period = max_period
self.mesh_device = mesh_device
self.dtype = dtype
self.time_proj_factor = self._create_time_proj_factor()
def _create_time_proj_factor(self) -> ttnn.Tensor:
assert self.num_channels % 2 == 0
half_dim = self.num_channels // 2
exponent = -math.log(self.max_period) * torch.arange(start=0, end=half_dim, dtype=torch.float32)
exponent = exponent / (half_dim - self.downscale_freq_shift)
factor = torch.exp(exponent)
return ttnn.unsqueeze_to_4D(typed_tensor(factor, dtype=self.dtype, device=self.mesh_device))
def forward(self, timestep: ttnn.Tensor) -> ttnn.Tensor:
# Time projection (sinusoidal embedding)
assert timestep.dtype == self.dtype, f"Input timestep {timestep.dtype} and model dtype {self.dtype} must match"
emb = self.scale * timestep * self.time_proj_factor
c = ttnn.cos(emb)
s = ttnn.sin(emb)
cat_args = [c, s] if self.cos_first else [s, c]
timesteps_proj = ttnn.concat(cat_args, dim=-1)
return timesteps_proj
class TimestepEmbedding(Module):
def __init__(
self,
in_channels,
time_embed_dim,
act_fn="silu",
dtype=ttnn.bfloat16,
bias: bool = True,
mesh_device=None,
tp_mesh_axis=None,
ccl_manager=None,
):
super().__init__()
self.in_channels = in_channels
self.time_embed_dim = time_embed_dim
self.mesh_device = mesh_device
self.act_fn = ACT2CLS[act_fn] # TODO: Fuse with linear instead
self.linear_1 = Linear(in_channels, time_embed_dim, bias=bias, mesh_device=mesh_device, dtype=dtype)
if tp_mesh_axis is None:
self.linear_2 = Linear(time_embed_dim, time_embed_dim, bias=bias, mesh_device=mesh_device, dtype=dtype)
else: # Specifically for Wan2.2
self.linear_2 = ColParallelLinear(
time_embed_dim,
time_embed_dim,
bias=bias,
mesh_device=mesh_device,
dtype=dtype,
mesh_axis=tp_mesh_axis,
ccl_manager=ccl_manager,
)
def forward(self, x: ttnn.Tensor) -> ttnn.Tensor:
x = self.linear_1(x)
x = self.act_fn(x) # TODO: Fuse with linear instead
return self.linear_2(x)
class PixArtAlphaTextProjection(Module):
def __init__(self, in_features, hidden_size, bias: bool = True, mesh_device=None, act_fn="silu"):
super().__init__()
self.in_features = in_features
self.hidden_size = hidden_size
self.mesh_device = mesh_device
self.linear_1 = Linear(in_features, hidden_size, bias=bias, mesh_device=mesh_device, activation_fn=act_fn)
self.linear_2 = Linear(hidden_size, hidden_size, bias=bias, mesh_device=mesh_device)
def forward(self, x: ttnn.Tensor) -> ttnn.Tensor:
x = self.linear_1(x)
return self.linear_2(x)
class SD35CombinedTimestepTextProjEmbeddings(Module):
def __init__(self, embedding_dim, pooled_projection_dim, mesh_device=None):
super().__init__()
self.embedding_dim = embedding_dim
self.pooled_projection_dim = pooled_projection_dim
self.mesh_device = mesh_device
self.timestep_embedder = TimestepEmbedding(256, embedding_dim, mesh_device=mesh_device)
self.text_embedder = (
PixArtAlphaTextProjection(pooled_projection_dim, embedding_dim, mesh_device=mesh_device)
if pooled_projection_dim > 0
else None
)
self.time_proj_factor = self._create_time_proj_factor(256)
def _create_time_proj_factor(self, num_channels) -> ttnn.Tensor:
assert num_channels % 2 == 0
half_dim = num_channels // 2
max_period = 10000
exponent = -math.log(max_period) * torch.arange(start=0, end=half_dim, dtype=torch.float32)
exponent = exponent / half_dim
factor = torch.exp(exponent)
return ttnn.unsqueeze_to_4D(bf16_tensor(factor, device=self.mesh_device))
def forward(self, timestep: ttnn.Tensor, pooled_projection: ttnn.Tensor | None = None) -> ttnn.Tensor:
# Time projection (sinusoidal embedding)
emb = timestep * self.time_proj_factor
c = ttnn.cos(emb)
s = ttnn.sin(emb)
timesteps_proj = ttnn.concat([c, s], dim=-1)
timesteps_emb = self.timestep_embedder(timesteps_proj)
if self.text_embedder is None:
return timesteps_emb
if pooled_projection is None:
msg = "pooled_projection must be provided when text embedder is enabled"
raise ValueError(msg)
text_emb = self.text_embedder(pooled_projection)
return timesteps_emb + text_emb
class CombinedTimestepGuidanceTextProjEmbeddings(Module):
def __init__(
self,
*,
embedding_dim: int,
pooled_projection_dim: int,
mesh_device: ttnn.MeshDevice | None = None,
bias: bool = True,
with_guidance: bool = True,
) -> None:
super().__init__()
self.embedding_dim = embedding_dim
self.pooled_projection_dim = pooled_projection_dim
self.mesh_device = mesh_device
self.timestep_embedder = TimestepEmbedding(256, embedding_dim, bias=bias, mesh_device=mesh_device)
self.guidance_embedder = (
TimestepEmbedding(256, embedding_dim, bias=bias, mesh_device=mesh_device) if with_guidance else None
)
self.text_embedder = (
PixArtAlphaTextProjection(pooled_projection_dim, embedding_dim, bias=bias, mesh_device=mesh_device)
if pooled_projection_dim != 0
else None
)
self.time_proj_factor = self._create_time_proj_factor(256)
def _create_time_proj_factor(self, num_channels) -> ttnn.Tensor:
assert num_channels % 2 == 0
half_dim = num_channels // 2
max_period = 10000
exponent = -math.log(max_period) * torch.arange(start=0, end=half_dim, dtype=torch.float32)
exponent = exponent / half_dim
factor = torch.exp(exponent)
return ttnn.unsqueeze_to_4D(bf16_tensor(factor, device=self.mesh_device))
# In order to avoid calling `unsqueeze` in this function, we expect already unsqueezed rank two
# `timestep` and `guidance` tensors.
def forward(
self,
*,
timestep: ttnn.Tensor,
guidance: ttnn.Tensor | None = None,
pooled_projection: ttnn.Tensor | None = None,
) -> ttnn.Tensor:
batch_size = timestep.shape[0]
assert timestep.shape == [batch_size, 1]
assert timestep.dtype == ttnn.float32, "timesteps require float32 precision"
emb = timestep * self.time_proj_factor
c = ttnn.cos(emb)
s = ttnn.sin(emb)
timesteps_proj = ttnn.concat([c, s], dim=-1)
output = self.timestep_embedder(timesteps_proj)
if self.text_embedder is not None:
assert pooled_projection is not None
assert len(pooled_projection.shape) == 2
assert pooled_projection.shape[0] == batch_size
output += self.text_embedder(pooled_projection)
if self.guidance_embedder is not None:
assert guidance is not None
assert guidance.shape == [batch_size, 1]
emb = guidance * self.time_proj_factor
c = ttnn.cos(emb)
s = ttnn.sin(emb)
guidances_proj = ttnn.concat([c, s], dim=-1)
output += self.guidance_embedder(guidances_proj)
return output
class PatchEmbed(Module):
"""
Patch embedding with unfolded conv2d implementation.
Converts input images to patch embeddings with positional encoding.
"""
def __init__(
self,
height,
width,
patch_size,
in_channels,
embed_dim,
pos_embed_max_size,
tp_mesh_axis,
sp_mesh_axis,
sequence_padding: tuple[int, int] = (0, 0),
mesh_device=None,
):
super().__init__()
self.height = height // patch_size
self.width = width // patch_size
self.patch_size = patch_size
self.in_channels = in_channels
self.embed_dim = embed_dim
self.pos_embed_max_size = pos_embed_max_size
self.mesh_device = mesh_device
self.tp_mesh_axis = tp_mesh_axis
self.sp_mesh_axis = sp_mesh_axis
self.sequence_padding = sequence_padding
# Position embeddings
self.pos_embed = None
# Compute kernel config for linear operations
self.compute_kernel_config = ttnn.init_device_compute_kernel_config(
mesh_device.arch(),
math_fidelity=ttnn.MathFidelity.HiFi4,
math_approx_mode=False,
fp32_dest_acc_en=True,
packer_l1_acc=False,
)
self.proj_weight = Parameter(
total_shape=[in_channels * patch_size * patch_size, embed_dim],
mesh_axes=[None, tp_mesh_axis],
device=mesh_device,
)
self.proj_bias = Parameter(
total_shape=[1, 1, 1, embed_dim], mesh_axes=[None, None, None, tp_mesh_axis], device=mesh_device
)
seq_len = self.height * self.width + sequence_padding[0] + sequence_padding[1]
self.pos_embed = Parameter(
total_shape=[1, seq_len, embed_dim],
device=mesh_device,
mesh_axes=[None, sp_mesh_axis, tp_mesh_axis],
)
def _cropped_pos_embed(self, pos_embed_param):
pos_embed_max_size = math.isqrt(pos_embed_param.shape[1])
top = (pos_embed_max_size - self.height) // 2
left = (pos_embed_max_size - self.width) // 2
spatial_pos_embed = pos_embed_param.reshape([1, pos_embed_max_size, pos_embed_max_size, -1])
spatial_pos_embed = spatial_pos_embed[:, top : top + self.height, left : left + self.width, :]
spatial_pos_embed = spatial_pos_embed.reshape([1, -1, spatial_pos_embed.shape[-1]])
return torch.nn.functional.pad(spatial_pos_embed, (0, 0, *self.sequence_padding))
def _prepare_torch_state(self, state: dict[str, torch.Tensor]) -> None:
conv_weight = state.pop("proj.weight", None)
if conv_weight is not None:
# Convert from (out_channels, in_channels, kh, kw) to (kh*kw*in_channels, out_channels)
out_channels, in_c, kh, kw = conv_weight.shape
conv_weight = conv_weight.permute(2, 3, 1, 0) # (kh, kw, in_c, out_channels)
conv_weight = conv_weight.reshape(kh * kw * in_c, out_channels)
state["proj_weight"] = conv_weight
if "proj.bias" in state:
state["proj_bias"] = state.pop("proj.bias").reshape(1, 1, 1, -1)
if "pos_embed" in state:
state["pos_embed"] = self._cropped_pos_embed(state.pop("pos_embed"))
def forward(self, latent: ttnn.Tensor, *, already_unfolded: bool = False) -> ttnn.Tensor:
"""
Forward pass: apply patch projection and add position embeddings.
Args:
latent: Input tensor of shape (batch_size, height, width, channels) in NHWC format
fractured dim 2 along sp_axis
Returns:
Patch embeddings of shape (batch_size, num_patches, embed_dim)
fractured dim 2 along sp_axis, dim 3 along tp_axis
"""
if already_unfolded:
latent = ttnn.linear(
latent,
self.proj_weight.data,
bias=self.proj_bias.data,
dtype=ttnn.bfloat16,
compute_kernel_config=self.compute_kernel_config,
)
else:
latent = self._unfold_conv2d(latent)
return latent + self.pos_embed.data
def _unfold_conv2d(self, x):
"""
Unfold conv2d operation: reshape patches and apply linear transformation.
Args:
x: Input tensor (batch_size, height, width, channels)
fractured dim 2 along sp_axis
Returns:
Projected tensor (1, batch_size, patches_h * patches_w, embed_dim)
fractured dim 2 along sp_axis, dim 3 along tp_axis
"""
batch_size, img_h, img_w, img_c = x.shape
patches_h = img_h // self.patch_size
patches_w = img_w // self.patch_size
# Reshape input to extract patches
# (batch_size, patches_h, patch_size, patches_w, patch_size, img_c)
x = ttnn.reshape(x, (batch_size, patches_h, self.patch_size, patches_w, self.patch_size, img_c))
# Permute to group patch dimensions together
# (batch_size, patches_h, patches_w, patch_size, patch_size, img_c)
x = ttnn.permute(x, (0, 1, 3, 2, 4, 5))
# Flatten patch dimensions
# (batch_size, patches_h * patches_w, patch_size * patch_size * img_c)
x = ttnn.reshape(x, (batch_size, patches_h * patches_w, self.patch_size * self.patch_size * img_c))
# Apply linear projection (equivalent to conv2d)
out = ttnn.linear(
x,
self.proj_weight.data,
bias=self.proj_bias.data,
dtype=ttnn.bfloat16,
compute_kernel_config=self.compute_kernel_config,
)
out = ttnn.reshape(out, (1, batch_size, patches_h * patches_w, -1))
return out
class MochiPatchEmbed(Module):
def __init__(
self,
patch_size,
in_channels,
embed_dim,
mesh_device=None,
):
super().__init__()
self.patch_size = patch_size
self.in_channels = in_channels
self.embed_dim = embed_dim
self.mesh_device = mesh_device
# Compute kernel config for linear operations
self.compute_kernel_config = ttnn.init_device_compute_kernel_config(
mesh_device.arch(),
math_fidelity=ttnn.MathFidelity.HiFi4,
math_approx_mode=False,
fp32_dest_acc_en=True,
packer_l1_acc=False,
)
# Conv2d projection weights (unfolded)
self.proj_weight = Parameter(total_shape=[in_channels * patch_size * patch_size, embed_dim], device=mesh_device)
self.proj_bias = Parameter(total_shape=[1, 1, 1, embed_dim], device=mesh_device)
def _prepare_torch_state(self, state: dict[str, torch.Tensor]) -> None:
conv_weight = state.pop("proj.weight", None)
if conv_weight is not None:
# Convert from (out_channels, in_channels, kh, kw) to (kh*kw*in_channels, out_channels)
out_channels, in_c, kh, kw = conv_weight.shape
conv_weight = conv_weight.permute(2, 3, 1, 0) # (kh, kw, in_c, out_channels)
conv_weight = conv_weight.reshape(kh * kw * in_c, out_channels)
state["proj_weight"] = conv_weight
conv_bias = state.pop("proj.bias", None)
if conv_bias is not None:
state["proj_bias"] = conv_bias.reshape([1, 1, 1, -1])
def forward(self, latent_1BNI: ttnn.Tensor) -> ttnn.Tensor:
"""
latent_1BNI: (1, batch, T * patches_height * patches_width, patch_size * patch_size * in_channels
returns:
latent_1BND: (1, batch, T * patches_height * patches_width, embed_dim)
"""
# Apply unfolded conv2d projection
latent_1BND = ttnn.linear(
latent_1BNI,
self.proj_weight.data,
bias=self.proj_bias.data,
dtype=ttnn.bfloat16,
compute_kernel_config=self.compute_kernel_config,
)
return latent_1BND
class WanPatchEmbed(Module):
def __init__(
self,
patch_size,
in_channels,
embed_dim,
mesh_device=None,
init=False,
tp_mesh_axis=None,
):
super().__init__()
self.patch_size = patch_size
self.in_channels = in_channels
self.embed_dim = embed_dim
self.mesh_device = mesh_device
# Optionally output tensor parallel
self.tp_mesh_axis = tp_mesh_axis
assert not init, "WanPatchEmbed does not support initialization"
# Compute kernel config for linear operations
self.compute_kernel_config = ttnn.init_device_compute_kernel_config(
mesh_device.arch(),
math_fidelity=ttnn.MathFidelity.HiFi4,
math_approx_mode=False,
fp32_dest_acc_en=True,
packer_l1_acc=False,
)
self.proj_weight = Parameter(
total_shape=[in_channels * patch_size[0] * patch_size[1] * patch_size[2], embed_dim],
device=mesh_device,
mesh_axes=[None, self.tp_mesh_axis],
)
self.proj_bias = Parameter(
total_shape=[1, embed_dim],
device=mesh_device,
mesh_axes=[None, self.tp_mesh_axis],
)
def _prepare_torch_state(self, state: dict[str, torch.Tensor]) -> None:
conv_weight = state.pop("weight", None)
if conv_weight is not None:
out_channels, in_c, kt, kh, kw = conv_weight.shape
assert out_channels == self.embed_dim, "Output channels do not match embed_dim"
assert in_c == self.in_channels, "Input channels do not match in_channels"
assert kt == self.patch_size[0], "Patch size does not match patch_size[0]"
assert kh == self.patch_size[1], "Patch size does not match patch_size[1]"
assert kw == self.patch_size[2], "Patch size does not match patch_size[2]"
conv_weight = conv_weight.permute(2, 3, 4, 1, 0) # (kt, kh, kw, in_c, out_channels)
conv_weight = conv_weight.reshape(kt * kh * kw * in_c, out_channels)
state["proj_weight"] = conv_weight
conv_weight = state.pop("bias", None)
if conv_weight is not None:
state["proj_bias"] = conv_weight.reshape(1, -1)
def forward(self, latent_1BNI: ttnn.Tensor) -> ttnn.Tensor:
"""
latent_1BNI: (1, batch, pt * ph * pw, kt * kh * kw * in_channels
returns:
latent_1BND: (1, batch, pt * ph * pw, embed_dim), optionally fractured embed_dim on TP
"""
# Apply unfolded conv2d projection
latent_1BND = ttnn.experimental.minimal_matmul(
latent_1BNI,
weight_tensor=self.proj_weight.data,
bias_tensor=self.proj_bias.data,
compute_kernel_config=self.compute_kernel_config,
)
return latent_1BND
class WanTimeTextImageEmbedding(Module):
def __init__(
self,
dim: int,
time_freq_dim: int,
time_proj_dim: int,
text_embed_dim: int,
image_embed_dim: int | None = None,
pos_embed_seq_len: int | None = None,
mesh_device: ttnn.MeshDevice | None = None,
tp_mesh_axis: int | None = None,
ccl_manager: CCLManager | None = None,
timestep_dtype: ttnn.DataType = ttnn.float32,
):
super().__init__()
assert image_embed_dim is None, "WanTimeTextImageEmbedding does not support image embedding"
self.mesh_device = mesh_device
self.tp_mesh_axis = tp_mesh_axis
self.timesteps_proj = Timesteps(num_channels=time_freq_dim, dtype=timestep_dtype, mesh_device=self.mesh_device)
self.time_embedder = TimestepEmbedding(
in_channels=time_freq_dim,
time_embed_dim=dim,
dtype=timestep_dtype,
mesh_device=self.mesh_device,
tp_mesh_axis=tp_mesh_axis,
ccl_manager=ccl_manager,
)
self.pre_time_proj_activation = ttnn.silu
self.time_proj = RowParallelLinear(
dim,
time_proj_dim,
bias=True,
dtype=timestep_dtype,
mesh_device=self.mesh_device,
mesh_axis=tp_mesh_axis,
ccl_manager=ccl_manager,
) # Output is fractured according to the older behaviour when sharding from torch. See _prepare_torch_state(...)
self.text_embedder = PixArtAlphaTextProjection(
text_embed_dim, dim, act_fn="gelu_tanh", mesh_device=self.mesh_device
)
def forward_timestep(self, timestep: ttnn.Tensor, timestep_seq_len: int | None = None) -> ttnn.Tensor:
timestep = self.timesteps_proj(timestep)
if timestep_seq_len is not None:
timestep = unflatten(timestep, 0, (-1, timestep_seq_len))
temb = self.time_embedder(timestep)
timestep_proj = self.time_proj(self.pre_time_proj_activation(temb), use_persistent_buffer=False)
return temb, timestep_proj
def forward_text(self, encoder_hidden_states: ttnn.Tensor) -> ttnn.Tensor:
return self.text_embedder(encoder_hidden_states)
def _prepare_torch_state(self, state: dict[str, torch.Tensor]) -> None:
# prep timestep proj weight for chunking.
time_proj_weight = state.pop("time_proj.weight", None)
time_proj_bias = state.pop("time_proj.bias", None)
# view as expected output shape
time_proj_weight = time_proj_weight.unflatten(0, (6, -1))
time_proj_bias = time_proj_bias.unflatten(0, (6, -1))
# chunk first dimension by number of devices
time_proj_weight_chunks = torch.chunk(time_proj_weight, self.mesh_device.shape[self.tp_mesh_axis], dim=1)
time_proj_bias_chunks = torch.chunk(time_proj_bias, self.mesh_device.shape[self.tp_mesh_axis], dim=1)
# reshape first dimension to ensure weights per device to ensure weights, data and expected output shape are aligned..
time_proj_weight_chunks = [chunk.reshape(-1, time_proj_weight.shape[-1]) for chunk in time_proj_weight_chunks]
time_proj_bias_chunks = [chunk.reshape(-1) for chunk in time_proj_bias_chunks]
# concatenate back to enable sharding to be applied in preparation for the chunking.
state["time_proj.weight"] = torch.cat(time_proj_weight_chunks, 0)
state["time_proj.bias"] = torch.cat(time_proj_bias_chunks, 0)
def forward(
self,
timestep: ttnn.Tensor,
encoder_hidden_states: ttnn.Tensor,
timestep_seq_len: int | None = None,
):
temb, timestep_proj = self.forward_timestep(timestep, timestep_seq_len)
encoder_hidden_states = self.forward_text(encoder_hidden_states)
return temb, timestep_proj, encoder_hidden_states
class Embedding(Module):
def __init__(
self, dictionary_size: int, embedding_size: int, *, device: ttnn.MeshDevice, mesh_axis: int | None = None
) -> None:
super().__init__()
# use row major layout since ttnn.embedding allocates the embedding tensor again otherwise
self.weight = Parameter(
total_shape=[dictionary_size, embedding_size],
mesh_axes=[None, mesh_axis],
layout=ttnn.ROW_MAJOR_LAYOUT,
device=device,
)
def forward(self, x: ttnn.Tensor, /) -> ttnn.Tensor:
return ttnn.embedding(x, self.weight.data, layout=ttnn.TILE_LAYOUT)
class LTXAdaLayerNormSingle(Module):
"""
LTX-2 adaptive layer normalization for timestep conditioning.
Embeds a timestep via sinusoidal projection + MLP, then projects to
`embedding_coefficient * embedding_dim` modulation parameters (shift/scale/gate
groups for self-attn, cross-attn, and feedforward).
Reference: LTX-2 adaln.py AdaLayerNormSingle
"""
def __init__(
self,
embedding_dim: int,
embedding_coefficient: int = 6,
mesh_device=None,
dtype=ttnn.float32,
):
super().__init__()
self.embedding_dim = embedding_dim
self.embedding_coefficient = embedding_coefficient
self.mesh_device = mesh_device
# Timestep embedding: sinusoidal(256) -> MLP(256 -> embedding_dim)
# Use fp32 for precision in sinusoidal projection
self.emb = _LTXTimestepEmbedding(embedding_dim, mesh_device=mesh_device, dtype=dtype)
# Project to modulation params: SiLU -> Linear(embedding_dim, coefficient * embedding_dim)
self.linear = Linear(
embedding_dim,
embedding_coefficient * embedding_dim,
bias=True,
mesh_device=mesh_device,
dtype=dtype,
)
def _prepare_torch_state(self, state: dict[str, torch.Tensor]) -> None:
# Remove "silu" entries (no weights in SiLU activation)
keys_to_remove = [k for k in state if k.startswith("silu")]
for k in keys_to_remove:
del state[k]
def forward(self, timestep: ttnn.Tensor) -> tuple[ttnn.Tensor, ttnn.Tensor]:
"""
Args:
timestep: (B, 1, 1, 1) scalar timestep (already on device)
Returns:
(modulation_params, embedded_timestep) where:
- modulation_params: (1, 1, B, coefficient * embedding_dim)
- embedded_timestep: (1, 1, B, embedding_dim)
"""
embedded_timestep = self.emb(timestep)
modulation_params = self.linear(ttnn.silu(embedded_timestep))
return modulation_params, embedded_timestep
class _LTXTimestepEmbedding(Module):
"""
Internal: PixArtAlphaCombinedTimestepSizeEmbeddings for LTX-2.
Timesteps(256, flip_sin_to_cos=True, downscale_freq_shift=0) + TimestepEmbedding(256, dim).
"""
def __init__(self, embedding_dim: int, mesh_device=None, dtype=ttnn.bfloat16):
super().__init__()
self.mesh_device = mesh_device
self.timestep_embedder = TimestepEmbedding(
in_channels=256,
time_embed_dim=embedding_dim,
mesh_device=mesh_device,
dtype=dtype,
)
# Sinusoidal with cos first, no freq shift (matching LTX-2's flip_sin_to_cos=True)
self.time_proj = Timesteps(
num_channels=256,
cos_first=True,
downscale_freq_shift=0,
mesh_device=mesh_device,
dtype=dtype,
)
def forward(self, timestep: ttnn.Tensor) -> ttnn.Tensor:
timesteps_proj = self.time_proj(timestep)
return self.timestep_embedder(timesteps_proj)