Skip to content
FastVideo
Esc
↑↓navigate↵open⌘Jpreview
On this page

ltx2vae

LTX-2 Video VAE implementation

fastvideo.models.vaes.ltx2vae.DiagonalGaussianDistribution

DiagonalGaussianDistribution(parameters: torch.Tensor, deterministic: bool = False)

fastvideo.models.vaes.ltx2vae.DiagonalGaussianDistribution.sample

sample(generator: torch.Generator | None = None) -> torch.Tensor

fastvideo.models.vaes.ltx2vae.DiagonalGaussianDistribution.kl

kl(other: Optional[DiagonalGaussianDistribution] = None) -> torch.Tensor

fastvideo.models.vaes.ltx2vae.DiagonalGaussianDistribution.nll

nll(sample: torch.Tensor, dims: tuple[int, ...] = (1, 2, 3)) -> torch.Tensor

fastvideo.models.vaes.ltx2vae.DiagonalGaussianDistribution.mode

mode() -> torch.Tensor

fastvideo.models.vaes.ltx2vae.NormLayerType

NormLayerType()
Source
class NormLayerType(Enum):
    GROUP_NORM = "group_norm"
    PIXEL_NORM = "pixel_norm"

fastvideo.models.vaes.ltx2vae.LogVarianceType

LogVarianceType()
Source
class LogVarianceType(Enum):
    PER_CHANNEL = "per_channel"
    UNIFORM = "uniform"
    CONSTANT = "constant"
    NONE = "none"

fastvideo.models.vaes.ltx2vae.PaddingModeType

PaddingModeType()
Source
class PaddingModeType(Enum):
    ZEROS = "zeros"
    REFLECT = "reflect"
    REPLICATE = "replicate"
    CIRCULAR = "circular"

fastvideo.models.vaes.ltx2vae.SpatialTilingConfig

SpatialTilingConfig(tile_size_in_pixels: int, tile_overlap_in_pixels: int = 0)

Configuration for dividing each frame into spatial tiles with optional overlap.

Source
@dataclass(frozen=True)
class SpatialTilingConfig:
    """Configuration for dividing each frame into spatial tiles with optional overlap."""
    tile_size_in_pixels: int
    tile_overlap_in_pixels: int = 0
def __post_init__(self) -> None:
    if self.tile_size_in_pixels < 64:
        raise ValueError(f"tile_size_in_pixels must be at least 64, got {self.tile_size_in_pixels}")
    if self.tile_size_in_pixels % 32 != 0:
        raise ValueError(f"tile_size_in_pixels must be divisible by 32, got {self.tile_size_in_pixels}")
    if self.tile_overlap_in_pixels % 32 != 0:
        raise ValueError(f"tile_overlap_in_pixels must be divisible by 32, got {self.tile_overlap_in_pixels}")
    if self.tile_overlap_in_pixels >= self.tile_size_in_pixels:
        raise ValueError(
            f"Overlap must be less than tile size, got {self.tile_overlap_in_pixels} and {self.tile_size_in_pixels}"
        )</code></pre></details>

fastvideo.models.vaes.ltx2vae.TemporalTilingConfig

TemporalTilingConfig(tile_size_in_frames: int, tile_overlap_in_frames: int = 0)

Configuration for dividing a video into temporal tiles (chunks of frames) with optional overlap.

Source
@dataclass(frozen=True)
class TemporalTilingConfig:
    """Configuration for dividing a video into temporal tiles (chunks of frames) with optional overlap."""
    tile_size_in_frames: int
    tile_overlap_in_frames: int = 0
def __post_init__(self) -&gt; None:
    if self.tile_size_in_frames &lt; 16:
        raise ValueError(f&quot;tile_size_in_frames must be at least 16, got &#123;self.tile_size_in_frames&#125;&quot;)
    if self.tile_size_in_frames % 8 != 0:
        raise ValueError(f&quot;tile_size_in_frames must be divisible by 8, got &#123;self.tile_size_in_frames&#125;&quot;)
    if self.tile_overlap_in_frames % 8 != 0:
        raise ValueError(f&quot;tile_overlap_in_frames must be divisible by 8, got &#123;self.tile_overlap_in_frames&#125;&quot;)
    if self.tile_overlap_in_frames &gt;= self.tile_size_in_frames:
        raise ValueError(
            f&quot;Overlap must be less than tile size, got &#123;self.tile_overlap_in_frames&#125; and &#123;self.tile_size_in_frames&#125;&quot;
        )</code></pre></details>

fastvideo.models.vaes.ltx2vae.TilingConfig

TilingConfig(spatial_config: SpatialTilingConfig | None = None, temporal_config: TemporalTilingConfig | None = None)

Configuration for splitting video into tiles with optional overlap.

Source
@dataclass(frozen=True)
class TilingConfig:
    """Configuration for splitting video into tiles with optional overlap."""
    spatial_config: SpatialTilingConfig | None = None
    temporal_config: TemporalTilingConfig | None = None
@classmethod
def default(cls) -&gt; &quot;TilingConfig&quot;:
    return cls(
        spatial_config=SpatialTilingConfig(tile_size_in_pixels=512, tile_overlap_in_pixels=64),
        temporal_config=TemporalTilingConfig(tile_size_in_frames=64, tile_overlap_in_frames=24),
    )</code></pre></details>

fastvideo.models.vaes.ltx2vae.TilingConfig.default

default() -> 'TilingConfig'
Source
@classmethod
def default(cls) -> "TilingConfig":
    return cls(
        spatial_config=SpatialTilingConfig(tile_size_in_pixels=512, tile_overlap_in_pixels=64),
        temporal_config=TemporalTilingConfig(tile_size_in_frames=64, tile_overlap_in_frames=24),
    )

fastvideo.models.vaes.ltx2vae.DimensionIntervals

DimensionIntervals(starts: List[int], ends: List[int], left_ramps: List[int], right_ramps: List[int])

Intervals which a single dimension of the latent space is split into.

Source
@dataclass(frozen=True)
class DimensionIntervals:
    """Intervals which a single dimension of the latent space is split into."""
    starts: List[int]
    ends: List[int]
    left_ramps: List[int]
    right_ramps: List[int]

fastvideo.models.vaes.ltx2vae.LatentIntervals

LatentIntervals(original_shape: torch.Size, dimension_intervals: Tuple[DimensionIntervals, ...])

Intervals which the latent tensor of given shape is split into.

Source
@dataclass(frozen=True)
class LatentIntervals:
    """Intervals which the latent tensor of given shape is split into."""
    original_shape: torch.Size
    dimension_intervals: Tuple[DimensionIntervals, ...]

fastvideo.models.vaes.ltx2vae.VideoLatentShape

VideoLatentShape()

Shape of the tensor representing video in VAE latent space.

Source
class VideoLatentShape(NamedTuple):
    """Shape of the tensor representing video in VAE latent space."""
    batch: int
    channels: int
    frames: int
    height: int
    width: int
def to_torch_shape(self) -&gt; torch.Size:
    return torch.Size(&#91;self.batch, self.channels, self.frames, self.height, self.width&#93;)

@staticmethod
def from_torch_shape(shape: torch.Size) -&gt; &quot;VideoLatentShape&quot;:
    return VideoLatentShape(
        batch=shape&#91;0&#93;,
        channels=shape&#91;1&#93;,
        frames=shape&#91;2&#93;,
        height=shape&#91;3&#93;,
        width=shape&#91;4&#93;,
    )

def upscale(self, time_scale: int, spatial_scale: int) -&gt; &quot;VideoLatentShape&quot;:
    return self._replace(
        channels=3,
        frames=(self.frames - 1) * time_scale + 1,
        height=self.height * spatial_scale,
        width=self.width * spatial_scale,
    )</code></pre></details>

fastvideo.models.vaes.ltx2vae.VideoLatentShape.to_torch_shape

to_torch_shape() -> torch.Size
Source
def to_torch_shape(self) -> torch.Size:
    return torch.Size([self.batch, self.channels, self.frames, self.height, self.width])

fastvideo.models.vaes.ltx2vae.VideoLatentShape.from_torch_shape

from_torch_shape(shape: torch.Size) -> 'VideoLatentShape'
Source
@staticmethod
def from_torch_shape(shape: torch.Size) -> "VideoLatentShape":
    return VideoLatentShape(
        batch=shape[0],
        channels=shape[1],
        frames=shape[2],
        height=shape[3],
        width=shape[4],
    )

fastvideo.models.vaes.ltx2vae.VideoLatentShape.upscale

upscale(time_scale: int, spatial_scale: int) -> 'VideoLatentShape'
Source
def upscale(self, time_scale: int, spatial_scale: int) -> "VideoLatentShape":
    return self._replace(
        channels=3,
        frames=(self.frames - 1) * time_scale + 1,
        height=self.height * spatial_scale,
        width=self.width * spatial_scale,
    )

fastvideo.models.vaes.ltx2vae.Tile

Tile()

Represents a single tile.

Source
class Tile(NamedTuple):
    """Represents a single tile."""
    in_coords: Tuple[slice, ...]
    out_coords: Tuple[slice, ...]
    masks_1d: Tuple[torch.Tensor | None, ...]
@property
def blend_mask(self) -&gt; torch.Tensor:
    num_dims = len(self.out_coords)
    per_dimension_masks: List&#91;torch.Tensor&#93; = &#91;&#93;

    for dim_idx in range(num_dims):
        mask_1d = self.masks_1d&#91;dim_idx&#93;
        view_shape = &#91;1&#93; * num_dims
        if mask_1d is None:
            one = torch.ones(1)
            view_shape&#91;dim_idx&#93; = 1
            per_dimension_masks.append(one.view(*view_shape))
            continue

        view_shape&#91;dim_idx&#93; = mask_1d.shape&#91;0&#93;
        per_dimension_masks.append(mask_1d.view(*view_shape))

    combined_mask = per_dimension_masks&#91;0&#93;
    for mask in per_dimension_masks&#91;1:&#93;:
        combined_mask = combined_mask * mask

    return combined_mask</code></pre></details>

fastvideo.models.vaes.ltx2vae.compute_trapezoidal_mask_1d

compute_trapezoidal_mask_1d(length: int, ramp_left: int, ramp_right: int, left_starts_from_0: bool = False) -> torch.Tensor

Generate a 1D trapezoidal blending mask with linear ramps.

Source
def compute_trapezoidal_mask_1d(
    length: int,
    ramp_left: int,
    ramp_right: int,
    left_starts_from_0: bool = False,
) -> torch.Tensor:
    """Generate a 1D trapezoidal blending mask with linear ramps."""
    if length <= 0:
        raise ValueError("Mask length must be positive.")
ramp_left = max(0, min(ramp_left, length))
ramp_right = max(0, min(ramp_right, length))

mask = torch.ones(length)

if ramp_left &gt; 0:
    interval_length = ramp_left + 1 if left_starts_from_0 else ramp_left + 2
    fade_in = torch.linspace(0.0, 1.0, interval_length)&#91;:-1&#93;
    if not left_starts_from_0:
        fade_in = fade_in&#91;1:&#93;
    mask&#91;:ramp_left&#93; *= fade_in

if ramp_right &gt; 0:
    fade_out = torch.linspace(1.0, 0.0, steps=ramp_right + 2)&#91;1:-1&#93;
    mask&#91;-ramp_right:&#93; *= fade_out

return mask.clamp_(0, 1)</code></pre></details>

fastvideo.models.vaes.ltx2vae.default_split_operation

default_split_operation(length: int) -> DimensionIntervals
Source
def default_split_operation(length: int) -> DimensionIntervals:
    return DimensionIntervals(starts=[0], ends=[length], left_ramps=[0], right_ramps=[0])

fastvideo.models.vaes.ltx2vae.default_mapping_operation

default_mapping_operation(_intervals: DimensionIntervals) -> Tuple[List[slice], List[torch.Tensor | None]]
Source
def default_mapping_operation(_intervals: DimensionIntervals, ) -> Tuple[List[slice], List[torch.Tensor | None]]:
    return [slice(0, None)], [None]

fastvideo.models.vaes.ltx2vae.split_in_spatial

split_in_spatial(size: int, overlap: int) -> SplitOperation
Source
def split_in_spatial(size: int, overlap: int) -> SplitOperation:
def split(dimension_size: int) -&gt; DimensionIntervals:
    if dimension_size &lt;= size:
        return DEFAULT_SPLIT_OPERATION(dimension_size)
    amount = (dimension_size + size - 2 * overlap - 1) // (size - overlap)
    starts = &#91;i * (size - overlap) for i in range(amount)&#93;
    ends = &#91;start + size for start in starts&#93;
    ends&#91;-1&#93; = dimension_size
    left_ramps = &#91;0&#93; + &#91;overlap&#93; * (amount - 1)
    right_ramps = &#91;overlap&#93; * (amount - 1) + &#91;0&#93;
    return DimensionIntervals(starts=starts, ends=ends, left_ramps=left_ramps, right_ramps=right_ramps)

return split</code></pre></details>

fastvideo.models.vaes.ltx2vae.split_in_temporal

split_in_temporal(size: int, overlap: int) -> SplitOperation
Source
def split_in_temporal(size: int, overlap: int) -> SplitOperation:
    non_causal_split = split_in_spatial(size, overlap)
def split(dimension_size: int) -&gt; DimensionIntervals:
    if dimension_size &lt;= size:
        return DEFAULT_SPLIT_OPERATION(dimension_size)
    intervals = non_causal_split(dimension_size)
    starts = list(intervals.starts)
    starts&#91;1:&#93; = &#91;s - 1 for s in starts&#91;1:&#93;&#93;
    left_ramps = list(intervals.left_ramps)
    left_ramps&#91;1:&#93; = &#91;r + 1 for r in left_ramps&#91;1:&#93;&#93;
    return replace(intervals, starts=starts, left_ramps=left_ramps)

return split</code></pre></details>

fastvideo.models.vaes.ltx2vae.map_temporal_slice

map_temporal_slice(begin: int, end: int, left_ramp: int, right_ramp: int, scale: int) -> Tuple[slice, torch.Tensor]
Source
def map_temporal_slice(begin: int, end: int, left_ramp: int, right_ramp: int, scale: int) -> Tuple[slice, torch.Tensor]:
    start = begin * scale
    stop = 1 + (end - 1) * scale
    left_ramp_scaled = 1 + (left_ramp - 1) * scale
    right_ramp_scaled = right_ramp * scale
return slice(start, stop), compute_trapezoidal_mask_1d(stop - start, left_ramp_scaled, right_ramp_scaled, True)</code></pre></details>

fastvideo.models.vaes.ltx2vae.map_spatial_slice

map_spatial_slice(begin: int, end: int, left_ramp: int, right_ramp: int, scale: int) -> Tuple[slice, torch.Tensor]
Source
def map_spatial_slice(begin: int, end: int, left_ramp: int, right_ramp: int, scale: int) -> Tuple[slice, torch.Tensor]:
    start = begin * scale
    stop = end * scale
    left_ramp_scaled = left_ramp * scale
    right_ramp_scaled = right_ramp * scale
return slice(start, stop), compute_trapezoidal_mask_1d(stop - start, left_ramp_scaled, right_ramp_scaled, False)</code></pre></details>

fastvideo.models.vaes.ltx2vae.to_mapping_operation

to_mapping_operation(map_func: Callable[[int, int, int, int, int], Tuple[slice, torch.Tensor]], scale: int) -> MappingOperation
Source
def to_mapping_operation(
    map_func: Callable[[int, int, int, int, int], Tuple[slice, torch.Tensor]],
    scale: int,
) -> MappingOperation:
def map_op(intervals: DimensionIntervals) -&gt; Tuple&#91;List&#91;slice&#93;, List&#91;torch.Tensor | None&#93;&#93;:
    output_slices: List&#91;slice&#93; = &#91;&#93;
    masks_1d: List&#91;torch.Tensor | None&#93; = &#91;&#93;
    number_of_slices = len(intervals.starts)
    for i in range(number_of_slices):
        start = intervals.starts&#91;i&#93;
        end = intervals.ends&#91;i&#93;
        left_ramp = intervals.left_ramps&#91;i&#93;
        right_ramp = intervals.right_ramps&#91;i&#93;
        output_slice, mask_1d = map_func(start, end, left_ramp, right_ramp, scale)
        output_slices.append(output_slice)
        masks_1d.append(mask_1d)
    return output_slices, masks_1d

return map_op</code></pre></details>

fastvideo.models.vaes.ltx2vae.create_tiles_from_intervals_and_mappers

create_tiles_from_intervals_and_mappers(intervals: LatentIntervals, mappers: List[MappingOperation]) -> List[Tile]
Source
def create_tiles_from_intervals_and_mappers(
    intervals: LatentIntervals,
    mappers: List[MappingOperation],
) -> List[Tile]:
    full_dim_input_slices = []
    full_dim_output_slices = []
    full_dim_masks_1d = []
    for axis_index in range(len(intervals.original_shape)):
        dimension_intervals = intervals.dimension_intervals[axis_index]
        starts = dimension_intervals.starts
        ends = dimension_intervals.ends
        input_slices = [slice(s, e) for s, e in zip(starts, ends, strict=True)]
        output_slices, masks_1d = mappers[axis_index](dimension_intervals)
        full_dim_input_slices.append(input_slices)
        full_dim_output_slices.append(output_slices)
        full_dim_masks_1d.append(masks_1d)
tiles = &#91;&#93;
tile_in_coords = list(itertools.product(*full_dim_input_slices))
tile_out_coords = list(itertools.product(*full_dim_output_slices))
tile_mask_1ds = list(itertools.product(*full_dim_masks_1d))
for in_coord, out_coord, mask_1d in zip(tile_in_coords, tile_out_coords, tile_mask_1ds, strict=True):
    tiles.append(Tile(
        in_coords=in_coord,
        out_coords=out_coord,
        masks_1d=mask_1d,
    ))
return tiles</code></pre></details>

fastvideo.models.vaes.ltx2vae.create_tiles

create_tiles(latent_shape: torch.Size, splitters: List[SplitOperation], mappers: List[MappingOperation]) -> List[Tile]
Source
def create_tiles(
    latent_shape: torch.Size,
    splitters: List[SplitOperation],
    mappers: List[MappingOperation],
) -> List[Tile]:
    if len(splitters) != len(latent_shape):
        raise ValueError(f"Number of splitters must be equal to number of dimensions in latent shape, "
                         f"got {len(splitters)} and {len(latent_shape)}")
    if len(mappers) != len(latent_shape):
        raise ValueError(f"Number of mappers must be equal to number of dimensions in latent shape, "
                         f"got {len(mappers)} and {len(latent_shape)}")
    intervals = [splitter(length) for splitter, length in zip(splitters, latent_shape, strict=True)]
    latent_intervals = LatentIntervals(original_shape=latent_shape, dimension_intervals=tuple(intervals))
    return create_tiles_from_intervals_and_mappers(latent_intervals, mappers)

fastvideo.models.vaes.ltx2vae.patchify

patchify(x: torch.Tensor, patch_size_hw: int, patch_size_t: int = 1) -> torch.Tensor

Rearrange spatial dimensions into channels (space-to-depth).

Parameters

x: torch.Tensor

Input tensor (4D or 5D)

patch_size_hw: int

Spatial patch size for height and width.

patch_size_t: int

Temporal patch size. Default=1 (no temporal patching).

For 5D: (B, C, F, H, W) -> (B, Cpatch_size_hw^2patch_size_t, F/patch_size_t, H/patch_size_hw, W/patch_size_hw)

Source
def patchify(x: torch.Tensor, patch_size_hw: int, patch_size_t: int = 1) -> torch.Tensor:
    """
    Rearrange spatial dimensions into channels (space-to-depth).
Args:
    x: Input tensor (4D or 5D)
    patch_size_hw: Spatial patch size for height and width.
    patch_size_t: Temporal patch size. Default=1 (no temporal patching).

For 5D: (B, C, F, H, W) -&gt; (B, C*patch_size_hw^2*patch_size_t, F/patch_size_t, H/patch_size_hw, W/patch_size_hw)
&quot;&quot;&quot;
if patch_size_hw == 1 and patch_size_t == 1:
    return x
if x.dim() == 4:
    x = rearrange(x, &quot;b c (h q) (w r) -&gt; b (c r q) h w&quot;, q=patch_size_hw, r=patch_size_hw)
elif x.dim() == 5:
    x = rearrange(
        x,
        &quot;b c (f p) (h q) (w r) -&gt; b (c p r q) f h w&quot;,
        p=patch_size_t,
        q=patch_size_hw,
        r=patch_size_hw,
    )
else:
    raise ValueError(f&quot;Invalid input shape: &#123;x.shape&#125;&quot;)
return x</code></pre></details>

fastvideo.models.vaes.ltx2vae.unpatchify

unpatchify(x: torch.Tensor, patch_size_hw: int, patch_size_t: int = 1) -> torch.Tensor

Rearrange channels back into spatial dimensions (depth-to-space).

Parameters

x: torch.Tensor

Input tensor (4D or 5D)

patch_size_hw: int

Spatial patch size for height and width.

patch_size_t: int

Temporal patch size. Default=1 (no temporal expansion).

For 5D: (B, Cpatch_size_hw^2patch_size_t, F, H, W) -> (B, C, Fpatch_size_t, Hpatch_size_hw, W*patch_size_hw)

Source
def unpatchify(x: torch.Tensor, patch_size_hw: int, patch_size_t: int = 1) -> torch.Tensor:
    """
    Rearrange channels back into spatial dimensions (depth-to-space).
Args:
    x: Input tensor (4D or 5D)
    patch_size_hw: Spatial patch size for height and width.
    patch_size_t: Temporal patch size. Default=1 (no temporal expansion).

For 5D: (B, C*patch_size_hw^2*patch_size_t, F, H, W) -&gt; (B, C, F*patch_size_t, H*patch_size_hw, W*patch_size_hw)
&quot;&quot;&quot;
if patch_size_hw == 1 and patch_size_t == 1:
    return x

if x.dim() == 4:
    x = rearrange(x, &quot;b (c r q) h w -&gt; b c (h q) (w r)&quot;, q=patch_size_hw, r=patch_size_hw)
elif x.dim() == 5:
    x = rearrange(
        x,
        &quot;b (c p r q) f h w -&gt; b c (f p) (h q) (w r)&quot;,
        p=patch_size_t,
        q=patch_size_hw,
        r=patch_size_hw,
    )
return x</code></pre></details>

fastvideo.models.vaes.ltx2vae.PixelNorm

PixelNorm(dim: int = 1, eps: float = 1e-08)

Per-pixel (per-location) RMS normalization layer. Normalizes along the channel dimension using root-mean-square.

Source
class PixelNorm(nn.Module):
    """
    Per-pixel (per-location) RMS normalization layer.
    Normalizes along the channel dimension using root-mean-square.
    """
def __init__(self, dim: int = 1, eps: float = 1e-8) -&gt; None:
    super().__init__()
    self.dim = dim
    self.eps = eps

def forward(self, x: torch.Tensor) -&gt; torch.Tensor:
    mean_sq = torch.mean(x**2, dim=self.dim, keepdim=True)
    return x / torch.sqrt(mean_sq + self.eps)</code></pre></details>

fastvideo.models.vaes.ltx2vae.PixelNorm.forward

forward(x: torch.Tensor) -> torch.Tensor
Source
def forward(self, x: torch.Tensor) -> torch.Tensor:
    mean_sq = torch.mean(x**2, dim=self.dim, keepdim=True)
    return x / torch.sqrt(mean_sq + self.eps)

fastvideo.models.vaes.ltx2vae.PerChannelStatistics

PerChannelStatistics(latent_channels: int = 128)

Per-channel statistics for normalizing and denormalizing the latent representation. Statistics are computed over the dataset and stored in the model checkpoint.

Source
class PerChannelStatistics(nn.Module):
    """
    Per-channel statistics for normalizing and denormalizing the latent representation.
    Statistics are computed over the dataset and stored in the model checkpoint.
    """
def __init__(self, latent_channels: int = 128):
    super().__init__()
    self.register_buffer(&quot;std-of-means&quot;, torch.ones(latent_channels))
    self.register_buffer(&quot;mean-of-means&quot;, torch.zeros(latent_channels))
    self.register_buffer(&quot;mean-of-stds&quot;, torch.ones(latent_channels))
    self.register_buffer(&quot;mean-of-stds_over_std-of-means&quot;, torch.ones(latent_channels))
    self.register_buffer(&quot;channel&quot;, torch.arange(latent_channels, dtype=torch.float32))

def un_normalize(self, x: torch.Tensor) -&gt; torch.Tensor:
    std = self.get_buffer(&quot;std-of-means&quot;).view(1, -1, 1, 1, 1).to(x)
    mean = self.get_buffer(&quot;mean-of-means&quot;).view(1, -1, 1, 1, 1).to(x)
    return x * std + mean

def normalize(self, x: torch.Tensor) -&gt; torch.Tensor:
    std = self.get_buffer(&quot;std-of-means&quot;).view(1, -1, 1, 1, 1).to(x)
    mean = self.get_buffer(&quot;mean-of-means&quot;).view(1, -1, 1, 1, 1).to(x)
    return (x - mean) / std</code></pre></details>

fastvideo.models.vaes.ltx2vae.PerChannelStatistics.un_normalize

un_normalize(x: torch.Tensor) -> torch.Tensor
Source
def un_normalize(self, x: torch.Tensor) -> torch.Tensor:
    std = self.get_buffer("std-of-means").view(1, -1, 1, 1, 1).to(x)
    mean = self.get_buffer("mean-of-means").view(1, -1, 1, 1, 1).to(x)
    return x * std + mean

fastvideo.models.vaes.ltx2vae.PerChannelStatistics.normalize

normalize(x: torch.Tensor) -> torch.Tensor
Source
def normalize(self, x: torch.Tensor) -> torch.Tensor:
    std = self.get_buffer("std-of-means").view(1, -1, 1, 1, 1).to(x)
    mean = self.get_buffer("mean-of-means").view(1, -1, 1, 1, 1).to(x)
    return (x - mean) / std

fastvideo.models.vaes.ltx2vae.CausalConv3d

CausalConv3d(in_channels: int, out_channels: int, kernel_size: int = 3, stride: int | Tuple[int, int, int] = 1, dilation: int = 1, groups: int = 1, bias: bool = True, spatial_padding_mode: PaddingModeType = PaddingModeType.ZEROS)

Causal 3D convolution that pads temporally by repeating the first frame.

Source
class CausalConv3d(nn.Module):
    """
    Causal 3D convolution that pads temporally by repeating the first frame.
    """
def __init__(
    self,
    in_channels: int,
    out_channels: int,
    kernel_size: int = 3,
    stride: int | Tuple&#91;int, int, int&#93; = 1,
    dilation: int = 1,
    groups: int = 1,
    bias: bool = True,
    spatial_padding_mode: PaddingModeType = PaddingModeType.ZEROS,
) -&gt; None:
    super().__init__()

    self.in_channels = in_channels
    self.out_channels = out_channels

    kernel_size = (kernel_size, kernel_size, kernel_size)
    self.time_kernel_size = kernel_size&#91;0&#93;

    dilation = (dilation, 1, 1)

    height_pad = kernel_size&#91;1&#93; // 2
    width_pad = kernel_size&#91;2&#93; // 2
    padding = (0, height_pad, width_pad)

    self.conv = nn.Conv3d(
        in_channels,
        out_channels,
        kernel_size,
        stride=stride,
        dilation=dilation,
        padding=padding,
        padding_mode=spatial_padding_mode.value,
        groups=groups,
        bias=bias,
    )

def forward(self, x: torch.Tensor, causal: bool = True) -&gt; torch.Tensor:
    if causal:
        first_frame_pad = x&#91;:, :, :1, :, :&#93;.repeat((1, 1, self.time_kernel_size - 1, 1, 1))
        x = torch.cat((first_frame_pad, x), dim=2)
    else:
        first_frame_pad = x&#91;:, :, :1, :, :&#93;.repeat((1, 1, (self.time_kernel_size - 1) // 2, 1, 1))
        last_frame_pad = x&#91;:, :, -1:, :, :&#93;.repeat((1, 1, (self.time_kernel_size - 1) // 2, 1, 1))
        x = torch.cat((first_frame_pad, x, last_frame_pad), dim=2)
    x = self.conv(x)
    return x

@property
def weight(self) -&gt; torch.Tensor:
    return self.conv.weight</code></pre></details>

fastvideo.models.vaes.ltx2vae.CausalConv3d.forward

forward(x: torch.Tensor, causal: bool = True) -> torch.Tensor
Source
def forward(self, x: torch.Tensor, causal: bool = True) -> torch.Tensor:
    if causal:
        first_frame_pad = x[:, :, :1, :, :].repeat((1, 1, self.time_kernel_size - 1, 1, 1))
        x = torch.cat((first_frame_pad, x), dim=2)
    else:
        first_frame_pad = x[:, :, :1, :, :].repeat((1, 1, (self.time_kernel_size - 1) // 2, 1, 1))
        last_frame_pad = x[:, :, -1:, :, :].repeat((1, 1, (self.time_kernel_size - 1) // 2, 1, 1))
        x = torch.cat((first_frame_pad, x, last_frame_pad), dim=2)
    x = self.conv(x)
    return x

fastvideo.models.vaes.ltx2vae.make_conv_nd

make_conv_nd(dims: int, in_channels: int, out_channels: int, kernel_size: int, stride: int | Tuple[int, int, int] = 1, padding: int = 0, dilation: int = 1, groups: int = 1, bias: bool = True, causal: bool = False, spatial_padding_mode: PaddingModeType = PaddingModeType.ZEROS) -> nn.Module

Create a convolution layer (2D or 3D, causal or not).

Source
def make_conv_nd(
    dims: int,
    in_channels: int,
    out_channels: int,
    kernel_size: int,
    stride: int | Tuple[int, int, int] = 1,
    padding: int = 0,
    dilation: int = 1,
    groups: int = 1,
    bias: bool = True,
    causal: bool = False,
    spatial_padding_mode: PaddingModeType = PaddingModeType.ZEROS,
) -> nn.Module:
    """Create a convolution layer (2D or 3D, causal or not)."""
    if dims == 2:
        return nn.Conv2d(
            in_channels=in_channels,
            out_channels=out_channels,
            kernel_size=kernel_size,
            stride=stride,
            padding=padding,
            dilation=dilation,
            groups=groups,
            bias=bias,
            padding_mode=spatial_padding_mode.value,
        )
    elif dims == 3:
        if causal:
            return CausalConv3d(
                in_channels=in_channels,
                out_channels=out_channels,
                kernel_size=kernel_size,
                stride=stride,
                dilation=dilation,
                groups=groups,
                bias=bias,
                spatial_padding_mode=spatial_padding_mode,
            )
        return nn.Conv3d(
            in_channels=in_channels,
            out_channels=out_channels,
            kernel_size=kernel_size,
            stride=stride,
            padding=padding,
            dilation=dilation,
            groups=groups,
            bias=bias,
            padding_mode=spatial_padding_mode.value,
        )
    else:
        raise ValueError(f"unsupported dimensions: {dims}")

fastvideo.models.vaes.ltx2vae.make_linear_nd

make_linear_nd(dims: int, in_channels: int, out_channels: int, bias: bool = True) -> nn.Module

Create a 1x1 convolution (pointwise linear).

Source
def make_linear_nd(
    dims: int,
    in_channels: int,
    out_channels: int,
    bias: bool = True,
) -> nn.Module:
    """Create a 1x1 convolution (pointwise linear)."""
    if dims == 2:
        return nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=1, bias=bias)
    elif dims == 3:
        return nn.Conv3d(in_channels=in_channels, out_channels=out_channels, kernel_size=1, bias=bias)
    else:
        raise ValueError(f"unsupported dimensions: {dims}")

fastvideo.models.vaes.ltx2vae.ResnetBlock3D

ResnetBlock3D(dims: int, in_channels: int, out_channels: int | None = None, dropout: float = 0.0, groups: int = 32, eps: float = 1e-06, norm_layer: NormLayerType = NormLayerType.PIXEL_NORM, inject_noise: bool = False, timestep_conditioning: bool = False, spatial_padding_mode: PaddingModeType = PaddingModeType.ZEROS)

A 3D ResNet block with optional timestep conditioning and noise injection.

Source
class ResnetBlock3D(nn.Module):
    """A 3D ResNet block with optional timestep conditioning and noise injection."""
def __init__(
    self,
    dims: int,
    in_channels: int,
    out_channels: int | None = None,
    dropout: float = 0.0,
    groups: int = 32,
    eps: float = 1e-6,
    norm_layer: NormLayerType = NormLayerType.PIXEL_NORM,
    inject_noise: bool = False,
    timestep_conditioning: bool = False,
    spatial_padding_mode: PaddingModeType = PaddingModeType.ZEROS,
):
    super().__init__()
    self.in_channels = in_channels
    out_channels = in_channels if out_channels is None else out_channels
    self.out_channels = out_channels
    self.inject_noise = inject_noise

    if norm_layer == NormLayerType.GROUP_NORM:
        self.norm1 = nn.GroupNorm(num_groups=groups, num_channels=in_channels, eps=eps, affine=True)
    elif norm_layer == NormLayerType.PIXEL_NORM:
        self.norm1 = PixelNorm()

    self.non_linearity = nn.SiLU()

    self.conv1 = make_conv_nd(
        dims,
        in_channels,
        out_channels,
        kernel_size=3,
        stride=1,
        padding=1,
        causal=True,
        spatial_padding_mode=spatial_padding_mode,
    )

    if inject_noise:
        self.per_channel_scale1 = nn.Parameter(torch.zeros((in_channels, 1, 1)))

    if norm_layer == NormLayerType.GROUP_NORM:
        self.norm2 = nn.GroupNorm(num_groups=groups, num_channels=out_channels, eps=eps, affine=True)
    elif norm_layer == NormLayerType.PIXEL_NORM:
        self.norm2 = PixelNorm()

    self.dropout = nn.Dropout(dropout)

    self.conv2 = make_conv_nd(
        dims,
        out_channels,
        out_channels,
        kernel_size=3,
        stride=1,
        padding=1,
        causal=True,
        spatial_padding_mode=spatial_padding_mode,
    )

    if inject_noise:
        self.per_channel_scale2 = nn.Parameter(torch.zeros((in_channels, 1, 1)))

    self.conv_shortcut = (make_linear_nd(dims=dims, in_channels=in_channels, out_channels=out_channels)
                          if in_channels != out_channels else nn.Identity())

    self.norm3 = (nn.GroupNorm(num_groups=1, num_channels=in_channels, eps=eps, affine=True)
                  if in_channels != out_channels else nn.Identity())

    self.timestep_conditioning = timestep_conditioning

    if timestep_conditioning:
        self.scale_shift_table = nn.Parameter(torch.randn(4, in_channels) / in_channels**0.5)

def _feed_spatial_noise(
    self,
    hidden_states: torch.Tensor,
    per_channel_scale: torch.Tensor,
    generator: torch.Generator | None = None,
) -&gt; torch.Tensor:
    spatial_shape = hidden_states.shape&#91;-2:&#93;
    device = hidden_states.device
    dtype = hidden_states.dtype

    spatial_noise = torch.randn(spatial_shape, device=device, dtype=dtype, generator=generator)&#91;None&#93;
    scaled_noise = (spatial_noise * per_channel_scale)&#91;None, :, None, ...&#93;
    hidden_states = hidden_states + scaled_noise
    return hidden_states

def forward(
    self,
    input_tensor: torch.Tensor,
    causal: bool = True,
    timestep: torch.Tensor | None = None,
    generator: torch.Generator | None = None,
) -&gt; torch.Tensor:
    hidden_states = input_tensor
    batch_size = hidden_states.shape&#91;0&#93;

    hidden_states = self.norm1(hidden_states)
    if self.timestep_conditioning:
        if timestep is None:
            raise ValueError(&quot;&#x27;timestep&#x27; must be provided when &#x27;timestep_conditioning&#x27; is True&quot;)
        ada_values = self.scale_shift_table&#91;None, ..., None, None, None&#93;.to(
            device=hidden_states.device, dtype=hidden_states.dtype) + timestep.reshape(
                batch_size,
                4,
                -1,
                timestep.shape&#91;-3&#93;,
                timestep.shape&#91;-2&#93;,
                timestep.shape&#91;-1&#93;,
            )
        shift1, scale1, shift2, scale2 = ada_values.unbind(dim=1)
        hidden_states = hidden_states * (1 + scale1) + shift1

    hidden_states = self.non_linearity(hidden_states)
    hidden_states = self.conv1(hidden_states, causal=causal)

    if self.inject_noise:
        hidden_states = self._feed_spatial_noise(
            hidden_states,
            self.per_channel_scale1.to(device=hidden_states.device, dtype=hidden_states.dtype),
            generator=generator,
        )

    hidden_states = self.norm2(hidden_states)

    if self.timestep_conditioning:
        hidden_states = hidden_states * (1 + scale2) + shift2

    hidden_states = self.non_linearity(hidden_states)
    hidden_states = self.dropout(hidden_states)
    hidden_states = self.conv2(hidden_states, causal=causal)

    if self.inject_noise:
        hidden_states = self._feed_spatial_noise(
            hidden_states,
            self.per_channel_scale2.to(device=hidden_states.device, dtype=hidden_states.dtype),
            generator=generator,
        )

    input_tensor = self.norm3(input_tensor)
    input_tensor = self.conv_shortcut(input_tensor)
    output_tensor = input_tensor + hidden_states
    return output_tensor</code></pre></details>

fastvideo.models.vaes.ltx2vae.ResnetBlock3D.forward

forward(input_tensor: torch.Tensor, causal: bool = True, timestep: torch.Tensor | None = None, generator: torch.Generator | None = None) -> torch.Tensor
Source
def forward(
    self,
    input_tensor: torch.Tensor,
    causal: bool = True,
    timestep: torch.Tensor | None = None,
    generator: torch.Generator | None = None,
) -> torch.Tensor:
    hidden_states = input_tensor
    batch_size = hidden_states.shape[0]
hidden_states = self.norm1(hidden_states)
if self.timestep_conditioning:
    if timestep is None:
        raise ValueError(&quot;&#x27;timestep&#x27; must be provided when &#x27;timestep_conditioning&#x27; is True&quot;)
    ada_values = self.scale_shift_table&#91;None, ..., None, None, None&#93;.to(
        device=hidden_states.device, dtype=hidden_states.dtype) + timestep.reshape(
            batch_size,
            4,
            -1,
            timestep.shape&#91;-3&#93;,
            timestep.shape&#91;-2&#93;,
            timestep.shape&#91;-1&#93;,
        )
    shift1, scale1, shift2, scale2 = ada_values.unbind(dim=1)
    hidden_states = hidden_states * (1 + scale1) + shift1

hidden_states = self.non_linearity(hidden_states)
hidden_states = self.conv1(hidden_states, causal=causal)

if self.inject_noise:
    hidden_states = self._feed_spatial_noise(
        hidden_states,
        self.per_channel_scale1.to(device=hidden_states.device, dtype=hidden_states.dtype),
        generator=generator,
    )

hidden_states = self.norm2(hidden_states)

if self.timestep_conditioning:
    hidden_states = hidden_states * (1 + scale2) + shift2

hidden_states = self.non_linearity(hidden_states)
hidden_states = self.dropout(hidden_states)
hidden_states = self.conv2(hidden_states, causal=causal)

if self.inject_noise:
    hidden_states = self._feed_spatial_noise(
        hidden_states,
        self.per_channel_scale2.to(device=hidden_states.device, dtype=hidden_states.dtype),
        generator=generator,
    )

input_tensor = self.norm3(input_tensor)
input_tensor = self.conv_shortcut(input_tensor)
output_tensor = input_tensor + hidden_states
return output_tensor</code></pre></details>

fastvideo.models.vaes.ltx2vae.UNetMidBlock3D

UNetMidBlock3D(dims: int, in_channels: int, dropout: float = 0.0, num_layers: int = 1, resnet_eps: float = 1e-06, resnet_groups: int = 32, norm_layer: NormLayerType = NormLayerType.GROUP_NORM, inject_noise: bool = False, timestep_conditioning: bool = False, spatial_padding_mode: PaddingModeType = PaddingModeType.ZEROS, attention_head_dim: int | None = None)

A 3D UNet mid-block with multiple residual blocks.

Source
class UNetMidBlock3D(nn.Module):
    """A 3D UNet mid-block with multiple residual blocks."""
def __init__(
        self,
        dims: int,
        in_channels: int,
        dropout: float = 0.0,
        num_layers: int = 1,
        resnet_eps: float = 1e-6,
        resnet_groups: int = 32,
        norm_layer: NormLayerType = NormLayerType.GROUP_NORM,
        inject_noise: bool = False,
        timestep_conditioning: bool = False,
        spatial_padding_mode: PaddingModeType = PaddingModeType.ZEROS,
        attention_head_dim: int | None = None,  # unused, for compatibility
):
    super().__init__()
    resnet_groups = resnet_groups if resnet_groups is not None else min(in_channels // 4, 32)

    self.timestep_conditioning = timestep_conditioning

    if timestep_conditioning:
        self.time_embedder = PixArtAlphaCombinedTimestepSizeEmbeddings(embedding_dim=in_channels * 4,
                                                                       size_emb_dim=0)

    self.res_blocks = nn.ModuleList(&#91;
        ResnetBlock3D(
            dims=dims,
            in_channels=in_channels,
            out_channels=in_channels,
            eps=resnet_eps,
            groups=resnet_groups,
            dropout=dropout,
            norm_layer=norm_layer,
            inject_noise=inject_noise,
            timestep_conditioning=timestep_conditioning,
            spatial_padding_mode=spatial_padding_mode,
        ) for _ in range(num_layers)
    &#93;)

def forward(
    self,
    hidden_states: torch.Tensor,
    causal: bool = True,
    timestep: torch.Tensor | None = None,
    generator: torch.Generator | None = None,
) -&gt; torch.Tensor:
    timestep_embed = None
    if self.timestep_conditioning:
        if timestep is None:
            raise ValueError(&quot;&#x27;timestep&#x27; must be provided when &#x27;timestep_conditioning&#x27; is True&quot;)
        batch_size = hidden_states.shape&#91;0&#93;
        timestep_embed = self.time_embedder(
            timestep=timestep.flatten(),
            hidden_dtype=hidden_states.dtype,
        )
        timestep_embed = timestep_embed.view(batch_size, timestep_embed.shape&#91;-1&#93;, 1, 1, 1)

    for resnet in self.res_blocks:
        hidden_states = resnet(
            hidden_states,
            causal=causal,
            timestep=timestep_embed,
            generator=generator,
        )

    return hidden_states</code></pre></details>

fastvideo.models.vaes.ltx2vae.UNetMidBlock3D.forward

forward(hidden_states: torch.Tensor, causal: bool = True, timestep: torch.Tensor | None = None, generator: torch.Generator | None = None) -> torch.Tensor
Source
def forward(
    self,
    hidden_states: torch.Tensor,
    causal: bool = True,
    timestep: torch.Tensor | None = None,
    generator: torch.Generator | None = None,
) -> torch.Tensor:
    timestep_embed = None
    if self.timestep_conditioning:
        if timestep is None:
            raise ValueError("'timestep' must be provided when 'timestep_conditioning' is True")
        batch_size = hidden_states.shape[0]
        timestep_embed = self.time_embedder(
            timestep=timestep.flatten(),
            hidden_dtype=hidden_states.dtype,
        )
        timestep_embed = timestep_embed.view(batch_size, timestep_embed.shape[-1], 1, 1, 1)
for resnet in self.res_blocks:
    hidden_states = resnet(
        hidden_states,
        causal=causal,
        timestep=timestep_embed,
        generator=generator,
    )

return hidden_states</code></pre></details>

fastvideo.models.vaes.ltx2vae.PixArtAlphaCombinedTimestepSizeEmbeddings

PixArtAlphaCombinedTimestepSizeEmbeddings(embedding_dim: int, size_emb_dim: int = 0)

Timestep embeddings for decoder conditioning.

Source
class PixArtAlphaCombinedTimestepSizeEmbeddings(nn.Module):
    """Timestep embeddings for decoder conditioning."""
def __init__(self, embedding_dim: int, size_emb_dim: int = 0):
    super().__init__()
    self.time_proj = Timesteps(num_channels=256, flip_sin_to_cos=True, downscale_freq_shift=0)
    self.timestep_embedder = TimestepEmbedding(in_channels=256, time_embed_dim=embedding_dim)

def forward(self, timestep: torch.Tensor, hidden_dtype: torch.dtype) -&gt; torch.Tensor:
    timesteps_proj = self.time_proj(timestep)
    timesteps_emb = self.timestep_embedder(timesteps_proj.to(dtype=hidden_dtype))
    return timesteps_emb</code></pre></details>

fastvideo.models.vaes.ltx2vae.PixArtAlphaCombinedTimestepSizeEmbeddings.forward

forward(timestep: torch.Tensor, hidden_dtype: torch.dtype) -> torch.Tensor
Source
def forward(self, timestep: torch.Tensor, hidden_dtype: torch.dtype) -> torch.Tensor:
    timesteps_proj = self.time_proj(timestep)
    timesteps_emb = self.timestep_embedder(timesteps_proj.to(dtype=hidden_dtype))
    return timesteps_emb

fastvideo.models.vaes.ltx2vae.Timesteps

Timesteps(num_channels: int, flip_sin_to_cos: bool = True, downscale_freq_shift: float = 0)

Sinusoidal timestep embeddings.

Source
class Timesteps(nn.Module):
    """Sinusoidal timestep embeddings."""
def __init__(self, num_channels: int, flip_sin_to_cos: bool = True, downscale_freq_shift: float = 0):
    super().__init__()
    self.num_channels = num_channels
    self.flip_sin_to_cos = flip_sin_to_cos
    self.downscale_freq_shift = downscale_freq_shift

def forward(self, timesteps: torch.Tensor) -&gt; torch.Tensor:
    half_dim = self.num_channels // 2
    exponent = -math.log(10000) * torch.arange(start=0, end=half_dim, dtype=torch.float32, device=timesteps.device)
    exponent = exponent / (half_dim - self.downscale_freq_shift)
    emb = torch.exp(exponent)
    emb = timesteps&#91;:, None&#93;.float() * emb&#91;None, :&#93;
    emb = torch.cat(&#91;torch.sin(emb), torch.cos(emb)&#93;, dim=-1)
    if self.flip_sin_to_cos:
        emb = torch.cat(&#91;emb&#91;:, half_dim:&#93;, emb&#91;:, :half_dim&#93;&#93;, dim=-1)
    return emb</code></pre></details>

fastvideo.models.vaes.ltx2vae.Timesteps.forward

forward(timesteps: torch.Tensor) -> torch.Tensor
Source
def forward(self, timesteps: torch.Tensor) -> torch.Tensor:
    half_dim = self.num_channels // 2
    exponent = -math.log(10000) * torch.arange(start=0, end=half_dim, dtype=torch.float32, device=timesteps.device)
    exponent = exponent / (half_dim - self.downscale_freq_shift)
    emb = torch.exp(exponent)
    emb = timesteps[:, None].float() * emb[None, :]
    emb = torch.cat([torch.sin(emb), torch.cos(emb)], dim=-1)
    if self.flip_sin_to_cos:
        emb = torch.cat([emb[:, half_dim:], emb[:, :half_dim]], dim=-1)
    return emb

fastvideo.models.vaes.ltx2vae.TimestepEmbedding

TimestepEmbedding(in_channels: int, time_embed_dim: int)

MLP for timestep embeddings.

Source
class TimestepEmbedding(nn.Module):
    """MLP for timestep embeddings."""
def __init__(self, in_channels: int, time_embed_dim: int):
    super().__init__()
    self.linear_1 = nn.Linear(in_channels, time_embed_dim)
    self.act = nn.SiLU()
    self.linear_2 = nn.Linear(time_embed_dim, time_embed_dim)

def forward(self, sample: torch.Tensor) -&gt; torch.Tensor:
    sample = self.linear_1(sample)
    sample = self.act(sample)
    sample = self.linear_2(sample)
    return sample</code></pre></details>

fastvideo.models.vaes.ltx2vae.TimestepEmbedding.forward

forward(sample: torch.Tensor) -> torch.Tensor
Source
def forward(self, sample: torch.Tensor) -> torch.Tensor:
    sample = self.linear_1(sample)
    sample = self.act(sample)
    sample = self.linear_2(sample)
    return sample

fastvideo.models.vaes.ltx2vae.SpaceToDepthDownsample

SpaceToDepthDownsample(dims: int, in_channels: int, out_channels: int, stride: Tuple[int, int, int], spatial_padding_mode: PaddingModeType = PaddingModeType.ZEROS)

Downsampling via space-to-depth with residual connection.

Source
class SpaceToDepthDownsample(nn.Module):
    """Downsampling via space-to-depth with residual connection."""
def __init__(
    self,
    dims: int,
    in_channels: int,
    out_channels: int,
    stride: Tuple&#91;int, int, int&#93;,
    spatial_padding_mode: PaddingModeType = PaddingModeType.ZEROS,
):
    super().__init__()
    self.stride = stride
    self.group_size = in_channels * math.prod(stride) // out_channels
    self.conv = make_conv_nd(
        dims=dims,
        in_channels=in_channels,
        out_channels=out_channels // math.prod(stride),
        kernel_size=3,
        stride=1,
        causal=True,
        spatial_padding_mode=spatial_padding_mode,
    )

def forward(self, x: torch.Tensor, causal: bool = True) -&gt; torch.Tensor:
    if self.stride&#91;0&#93; == 2:
        x = torch.cat(&#91;x&#91;:, :, :1, :, :&#93;, x&#93;, dim=2)

    x_in = rearrange(
        x,
        &quot;b c (d p1) (h p2) (w p3) -&gt; b (c p1 p2 p3) d h w&quot;,
        p1=self.stride&#91;0&#93;,
        p2=self.stride&#91;1&#93;,
        p3=self.stride&#91;2&#93;,
    )
    x_in = rearrange(x_in, &quot;b (c g) d h w -&gt; b c g d h w&quot;, g=self.group_size)
    x_in = x_in.mean(dim=2)

    x = self.conv(x, causal=causal)
    x = rearrange(
        x,
        &quot;b c (d p1) (h p2) (w p3) -&gt; b (c p1 p2 p3) d h w&quot;,
        p1=self.stride&#91;0&#93;,
        p2=self.stride&#91;1&#93;,
        p3=self.stride&#91;2&#93;,
    )

    x = x + x_in
    return x</code></pre></details>

fastvideo.models.vaes.ltx2vae.SpaceToDepthDownsample.forward

forward(x: torch.Tensor, causal: bool = True) -> torch.Tensor
Source
def forward(self, x: torch.Tensor, causal: bool = True) -> torch.Tensor:
    if self.stride[0] == 2:
        x = torch.cat([x[:, :, :1, :, :], x], dim=2)
x_in = rearrange(
    x,
    &quot;b c (d p1) (h p2) (w p3) -&gt; b (c p1 p2 p3) d h w&quot;,
    p1=self.stride&#91;0&#93;,
    p2=self.stride&#91;1&#93;,
    p3=self.stride&#91;2&#93;,
)
x_in = rearrange(x_in, &quot;b (c g) d h w -&gt; b c g d h w&quot;, g=self.group_size)
x_in = x_in.mean(dim=2)

x = self.conv(x, causal=causal)
x = rearrange(
    x,
    &quot;b c (d p1) (h p2) (w p3) -&gt; b (c p1 p2 p3) d h w&quot;,
    p1=self.stride&#91;0&#93;,
    p2=self.stride&#91;1&#93;,
    p3=self.stride&#91;2&#93;,
)

x = x + x_in
return x</code></pre></details>

fastvideo.models.vaes.ltx2vae.DepthToSpaceUpsample

DepthToSpaceUpsample(dims: int, in_channels: int, stride: Tuple[int, int, int], residual: bool = False, out_channels_reduction_factor: int = 1, spatial_padding_mode: PaddingModeType = PaddingModeType.ZEROS)

Upsampling via depth-to-space (pixel shuffle).

Source
class DepthToSpaceUpsample(nn.Module):
    """Upsampling via depth-to-space (pixel shuffle)."""
def __init__(
    self,
    dims: int,
    in_channels: int,
    stride: Tuple&#91;int, int, int&#93;,
    residual: bool = False,
    out_channels_reduction_factor: int = 1,
    spatial_padding_mode: PaddingModeType = PaddingModeType.ZEROS,
):
    super().__init__()
    self.stride = stride
    self.out_channels = math.prod(stride) * in_channels // out_channels_reduction_factor
    self.conv = make_conv_nd(
        dims=dims,
        in_channels=in_channels,
        out_channels=self.out_channels,
        kernel_size=3,
        stride=1,
        causal=True,
        spatial_padding_mode=spatial_padding_mode,
    )
    self.residual = residual
    self.out_channels_reduction_factor = out_channels_reduction_factor

def forward(self, x: torch.Tensor, causal: bool = True) -&gt; torch.Tensor:
    if self.residual:
        x_in = rearrange(
            x,
            &quot;b (c p1 p2 p3) d h w -&gt; b c (d p1) (h p2) (w p3)&quot;,
            p1=self.stride&#91;0&#93;,
            p2=self.stride&#91;1&#93;,
            p3=self.stride&#91;2&#93;,
        )
        num_repeat = math.prod(self.stride) // self.out_channels_reduction_factor
        x_in = x_in.repeat(1, num_repeat, 1, 1, 1)
        if self.stride&#91;0&#93; == 2:
            x_in = x_in&#91;:, :, 1:, :, :&#93;

    x = self.conv(x, causal=causal)
    x = rearrange(
        x,
        &quot;b (c p1 p2 p3) d h w -&gt; b c (d p1) (h p2) (w p3)&quot;,
        p1=self.stride&#91;0&#93;,
        p2=self.stride&#91;1&#93;,
        p3=self.stride&#91;2&#93;,
    )
    if self.stride&#91;0&#93; == 2:
        x = x&#91;:, :, 1:, :, :&#93;
    if self.residual:
        x = x + x_in
    return x</code></pre></details>

fastvideo.models.vaes.ltx2vae.DepthToSpaceUpsample.forward

forward(x: torch.Tensor, causal: bool = True) -> torch.Tensor
Source
def forward(self, x: torch.Tensor, causal: bool = True) -> torch.Tensor:
    if self.residual:
        x_in = rearrange(
            x,
            "b (c p1 p2 p3) d h w -> b c (d p1) (h p2) (w p3)",
            p1=self.stride[0],
            p2=self.stride[1],
            p3=self.stride[2],
        )
        num_repeat = math.prod(self.stride) // self.out_channels_reduction_factor
        x_in = x_in.repeat(1, num_repeat, 1, 1, 1)
        if self.stride[0] == 2:
            x_in = x_in[:, :, 1:, :, :]
x = self.conv(x, causal=causal)
x = rearrange(
    x,
    &quot;b (c p1 p2 p3) d h w -&gt; b c (d p1) (h p2) (w p3)&quot;,
    p1=self.stride&#91;0&#93;,
    p2=self.stride&#91;1&#93;,
    p3=self.stride&#91;2&#93;,
)
if self.stride&#91;0&#93; == 2:
    x = x&#91;:, :, 1:, :, :&#93;
if self.residual:
    x = x + x_in
return x</code></pre></details>

fastvideo.models.vaes.ltx2vae.VideoEncoder

VideoEncoder(convolution_dimensions: int = 3, in_channels: int = 3, out_channels: int = 128, encoder_blocks: list[tuple[str, int]] | list[tuple[str, dict[str, Any]]] = [], patch_size: int = 4, norm_layer: NormLayerType = NormLayerType.PIXEL_NORM, latent_log_var: LogVarianceType = LogVarianceType.UNIFORM, encoder_spatial_padding_mode: PaddingModeType = PaddingModeType.ZEROS)

LTX-2 Video Encoder. Encodes video frames into a latent representation.

Source
class VideoEncoder(nn.Module):
    """
    LTX-2 Video Encoder. Encodes video frames into a latent representation.
    """
_DEFAULT_NORM_NUM_GROUPS = 32

def __init__(
    self,
    convolution_dimensions: int = 3,
    in_channels: int = 3,
    out_channels: int = 128,
    encoder_blocks: list&#91;tuple&#91;str, int&#93;&#93; | list&#91;tuple&#91;str, dict&#91;str, Any&#93;&#93;&#93; = &#91;&#93;,
    patch_size: int = 4,
    norm_layer: NormLayerType = NormLayerType.PIXEL_NORM,
    latent_log_var: LogVarianceType = LogVarianceType.UNIFORM,
    encoder_spatial_padding_mode: PaddingModeType = PaddingModeType.ZEROS,
):
    super().__init__()

    self.patch_size = patch_size
    self.norm_layer = norm_layer
    self.latent_channels = out_channels
    self.latent_log_var = latent_log_var
    self._norm_num_groups = self._DEFAULT_NORM_NUM_GROUPS

    self.per_channel_statistics = PerChannelStatistics(latent_channels=out_channels)

    in_channels = in_channels * patch_size**2
    feature_channels = out_channels

    self.conv_in = make_conv_nd(
        dims=convolution_dimensions,
        in_channels=in_channels,
        out_channels=feature_channels,
        kernel_size=3,
        stride=1,
        padding=1,
        causal=True,
        spatial_padding_mode=encoder_spatial_padding_mode,
    )

    self.down_blocks = nn.ModuleList(&#91;&#93;)

    for block_name, block_params in encoder_blocks:
        block_config = &#123;&quot;num_layers&quot;: block_params&#125; if isinstance(block_params, int) else block_params

        block, feature_channels = _make_encoder_block(
            block_name=block_name,
            block_config=block_config,
            in_channels=feature_channels,
            convolution_dimensions=convolution_dimensions,
            norm_layer=norm_layer,
            norm_num_groups=self._norm_num_groups,
            spatial_padding_mode=encoder_spatial_padding_mode,
        )

        self.down_blocks.append(block)

    if norm_layer == NormLayerType.GROUP_NORM:
        self.conv_norm_out = nn.GroupNorm(num_channels=feature_channels, num_groups=self._norm_num_groups, eps=1e-6)
    elif norm_layer == NormLayerType.PIXEL_NORM:
        self.conv_norm_out = PixelNorm()

    self.conv_act = nn.SiLU()

    conv_out_channels = out_channels
    if latent_log_var == LogVarianceType.PER_CHANNEL:
        conv_out_channels *= 2
    elif latent_log_var in &#123;LogVarianceType.UNIFORM, LogVarianceType.CONSTANT&#125;:
        conv_out_channels += 1
    elif latent_log_var != LogVarianceType.NONE:
        raise ValueError(f&quot;Invalid latent_log_var: &#123;latent_log_var&#125;&quot;)

    self.conv_out = make_conv_nd(
        dims=convolution_dimensions,
        in_channels=feature_channels,
        out_channels=conv_out_channels,
        kernel_size=3,
        padding=1,
        causal=True,
        spatial_padding_mode=encoder_spatial_padding_mode,
    )

def forward(self, sample: torch.Tensor) -&gt; torch.Tensor:
    frames_count = sample.shape&#91;2&#93;
    if ((frames_count - 1) % 8) != 0:
        raise ValueError(&quot;Invalid number of frames: Encode input must have 1 + 8 * x frames &quot;
                         &quot;(e.g., 1, 9, 17, ...). Please check your input.&quot;)

    sample = patchify(sample, patch_size_hw=self.patch_size, patch_size_t=1)
    sample = self.conv_in(sample)

    for down_block in self.down_blocks:
        sample = down_block(sample)

    sample = self.conv_norm_out(sample)
    sample = self.conv_act(sample)
    sample = self.conv_out(sample)

    if self.latent_log_var == LogVarianceType.UNIFORM:
        if sample.shape&#91;1&#93; &lt; 2:
            raise ValueError(f&quot;Invalid channel count for UNIFORM mode: expected at least 2, got &#123;sample.shape&#91;1&#93;&#125;&quot;)
        means = sample&#91;:, :-1, ...&#93;
        logvar = sample&#91;:, -1:, ...&#93;
        num_channels = means.shape&#91;1&#93;
        repeat_shape = &#91;1, num_channels&#93; + &#91;1&#93; * (sample.ndim - 2)
        repeated_logvar = logvar.repeat(*repeat_shape)
        sample = torch.cat(&#91;means, repeated_logvar&#93;, dim=1)
    elif self.latent_log_var == LogVarianceType.CONSTANT:
        sample = sample&#91;:, :-1, ...&#93;
        approx_ln_0 = -30
        sample = torch.cat(
            &#91;sample, torch.ones_like(sample, device=sample.device) * approx_ln_0&#93;,
            dim=1,
        )

    means, _ = torch.chunk(sample, 2, dim=1)
    return self.per_channel_statistics.normalize(means)</code></pre></details>

fastvideo.models.vaes.ltx2vae.VideoEncoder.forward

forward(sample: torch.Tensor) -> torch.Tensor
Source
def forward(self, sample: torch.Tensor) -> torch.Tensor:
    frames_count = sample.shape[2]
    if ((frames_count - 1) % 8) != 0:
        raise ValueError("Invalid number of frames: Encode input must have 1 + 8 * x frames "
                         "(e.g., 1, 9, 17, ...). Please check your input.")
sample = patchify(sample, patch_size_hw=self.patch_size, patch_size_t=1)
sample = self.conv_in(sample)

for down_block in self.down_blocks:
    sample = down_block(sample)

sample = self.conv_norm_out(sample)
sample = self.conv_act(sample)
sample = self.conv_out(sample)

if self.latent_log_var == LogVarianceType.UNIFORM:
    if sample.shape&#91;1&#93; &lt; 2:
        raise ValueError(f&quot;Invalid channel count for UNIFORM mode: expected at least 2, got &#123;sample.shape&#91;1&#93;&#125;&quot;)
    means = sample&#91;:, :-1, ...&#93;
    logvar = sample&#91;:, -1:, ...&#93;
    num_channels = means.shape&#91;1&#93;
    repeat_shape = &#91;1, num_channels&#93; + &#91;1&#93; * (sample.ndim - 2)
    repeated_logvar = logvar.repeat(*repeat_shape)
    sample = torch.cat(&#91;means, repeated_logvar&#93;, dim=1)
elif self.latent_log_var == LogVarianceType.CONSTANT:
    sample = sample&#91;:, :-1, ...&#93;
    approx_ln_0 = -30
    sample = torch.cat(
        &#91;sample, torch.ones_like(sample, device=sample.device) * approx_ln_0&#93;,
        dim=1,
    )

means, _ = torch.chunk(sample, 2, dim=1)
return self.per_channel_statistics.normalize(means)</code></pre></details>

fastvideo.models.vaes.ltx2vae.VideoDecoder

VideoDecoder(convolution_dimensions: int = 3, in_channels: int = 128, out_channels: int = 3, decoder_blocks: list[tuple[str, int | dict]] = [], patch_size: int = 4, norm_layer: NormLayerType = NormLayerType.PIXEL_NORM, causal: bool = False, timestep_conditioning: bool = False, decoder_spatial_padding_mode: PaddingModeType = PaddingModeType.REFLECT, base_channels: int = 128)

LTX-2 Video Decoder. Decodes latent representation into video frames.

Source
class VideoDecoder(nn.Module):
    """
    LTX-2 Video Decoder. Decodes latent representation into video frames.
    """
_DEFAULT_NORM_NUM_GROUPS = 32

def __init__(
    self,
    convolution_dimensions: int = 3,
    in_channels: int = 128,
    out_channels: int = 3,
    decoder_blocks: list&#91;tuple&#91;str, int | dict&#93;&#93; = &#91;&#93;,
    patch_size: int = 4,
    norm_layer: NormLayerType = NormLayerType.PIXEL_NORM,
    causal: bool = False,
    timestep_conditioning: bool = False,
    decoder_spatial_padding_mode: PaddingModeType = PaddingModeType.REFLECT,
    base_channels: int = 128,
):
    super().__init__()

    self.patch_size = patch_size
    out_channels = out_channels * patch_size**2
    self.causal = causal
    self.timestep_conditioning = timestep_conditioning
    self._norm_num_groups = self._DEFAULT_NORM_NUM_GROUPS

    self.per_channel_statistics = PerChannelStatistics(latent_channels=in_channels)

    self.decode_noise_scale = 0.025
    self.decode_timestep = 0.05

    feature_channels = base_channels
    for block_name, block_params in list(reversed(decoder_blocks)):
        block_config = block_params if isinstance(block_params, dict) else &#123;&#125;
        if block_name == &quot;res_x_y&quot;:
            feature_channels = feature_channels * block_config.get(&quot;multiplier&quot;, 2)
        if block_name == &quot;compress_all&quot;:
            feature_channels = feature_channels * block_config.get(&quot;multiplier&quot;, 1)
        if block_name == &quot;compress_space&quot;:
            feature_channels = feature_channels * block_config.get(&quot;multiplier&quot;, 1)
        if block_name == &quot;compress_time&quot;:
            feature_channels = feature_channels * block_config.get(&quot;multiplier&quot;, 1)

    self.conv_in = make_conv_nd(
        dims=convolution_dimensions,
        in_channels=in_channels,
        out_channels=feature_channels,
        kernel_size=3,
        stride=1,
        padding=1,
        causal=True,
        spatial_padding_mode=decoder_spatial_padding_mode,
    )

    self.up_blocks = nn.ModuleList(&#91;&#93;)

    for block_name, block_params in list(reversed(decoder_blocks)):
        block_config = &#123;&quot;num_layers&quot;: block_params&#125; if isinstance(block_params, int) else block_params

        block, feature_channels = _make_decoder_block(
            block_name=block_name,
            block_config=block_config,
            in_channels=feature_channels,
            convolution_dimensions=convolution_dimensions,
            norm_layer=norm_layer,
            timestep_conditioning=timestep_conditioning,
            norm_num_groups=self._norm_num_groups,
            spatial_padding_mode=decoder_spatial_padding_mode,
        )

        self.up_blocks.append(block)

    if norm_layer == NormLayerType.GROUP_NORM:
        self.conv_norm_out = nn.GroupNorm(num_channels=feature_channels, num_groups=self._norm_num_groups, eps=1e-6)
    elif norm_layer == NormLayerType.PIXEL_NORM:
        self.conv_norm_out = PixelNorm()

    self.conv_act = nn.SiLU()
    self.conv_out = make_conv_nd(
        dims=convolution_dimensions,
        in_channels=feature_channels,
        out_channels=out_channels,
        kernel_size=3,
        padding=1,
        causal=True,
        spatial_padding_mode=decoder_spatial_padding_mode,
    )

    if timestep_conditioning:
        self.timestep_scale_multiplier = nn.Parameter(torch.tensor(1000.0))
        self.last_time_embedder = PixArtAlphaCombinedTimestepSizeEmbeddings(embedding_dim=feature_channels * 2,
                                                                            size_emb_dim=0)
        self.last_scale_shift_table = nn.Parameter(torch.empty(2, feature_channels))

def forward(
    self,
    sample: torch.Tensor,
    timestep: torch.Tensor | None = None,
    generator: torch.Generator | None = None,
) -&gt; torch.Tensor:
    batch_size = sample.shape&#91;0&#93;

    if self.timestep_conditioning:
        noise = (torch.randn(
            sample.size(),
            generator=generator,
            dtype=sample.dtype,
            device=sample.device,
        ) * self.decode_noise_scale)
        sample = noise + (1.0 - self.decode_noise_scale) * sample

    sample = self.per_channel_statistics.un_normalize(sample)

    if timestep is None and self.timestep_conditioning:
        timestep = torch.full((batch_size, ), self.decode_timestep, device=sample.device, dtype=sample.dtype)

    sample = self.conv_in(sample, causal=self.causal)

    scaled_timestep = None
    if self.timestep_conditioning:
        if timestep is None:
            raise ValueError(&quot;&#x27;timestep&#x27; must be provided when &#x27;timestep_conditioning&#x27; is True&quot;)
        scaled_timestep = timestep * self.timestep_scale_multiplier.to(sample)

    for up_block in self.up_blocks:
        if isinstance(up_block, UNetMidBlock3D):
            block_kwargs = &#123;
                &quot;causal&quot;: self.causal,
                &quot;timestep&quot;: scaled_timestep if self.timestep_conditioning else None,
                &quot;generator&quot;: generator,
            &#125;
            sample = up_block(sample, **block_kwargs)
        elif isinstance(up_block, ResnetBlock3D):
            sample = up_block(sample, causal=self.causal, generator=generator)
        else:
            sample = up_block(sample, causal=self.causal)

    sample = self.conv_norm_out(sample)

    if self.timestep_conditioning:
        embedded_timestep = self.last_time_embedder(
            timestep=scaled_timestep.flatten(),
            hidden_dtype=sample.dtype,
        )
        embedded_timestep = embedded_timestep.view(batch_size, embedded_timestep.shape&#91;-1&#93;, 1, 1, 1)
        ada_values = self.last_scale_shift_table&#91;None, ..., None, None, None&#93;.to(
            device=sample.device, dtype=sample.dtype) + embedded_timestep.reshape(
                batch_size,
                2,
                -1,
                embedded_timestep.shape&#91;-3&#93;,
                embedded_timestep.shape&#91;-2&#93;,
                embedded_timestep.shape&#91;-1&#93;,
            )
        shift, scale = ada_values.unbind(dim=1)
        sample = sample * (1 + scale) + shift

    sample = self.conv_act(sample)
    sample = self.conv_out(sample, causal=self.causal)
    sample = unpatchify(sample, patch_size_hw=self.patch_size, patch_size_t=1)

    return sample</code></pre></details>

fastvideo.models.vaes.ltx2vae.VideoDecoder.forward

forward(sample: torch.Tensor, timestep: torch.Tensor | None = None, generator: torch.Generator | None = None) -> torch.Tensor
Source
def forward(
    self,
    sample: torch.Tensor,
    timestep: torch.Tensor | None = None,
    generator: torch.Generator | None = None,
) -> torch.Tensor:
    batch_size = sample.shape[0]
if self.timestep_conditioning:
    noise = (torch.randn(
        sample.size(),
        generator=generator,
        dtype=sample.dtype,
        device=sample.device,
    ) * self.decode_noise_scale)
    sample = noise + (1.0 - self.decode_noise_scale) * sample

sample = self.per_channel_statistics.un_normalize(sample)

if timestep is None and self.timestep_conditioning:
    timestep = torch.full((batch_size, ), self.decode_timestep, device=sample.device, dtype=sample.dtype)

sample = self.conv_in(sample, causal=self.causal)

scaled_timestep = None
if self.timestep_conditioning:
    if timestep is None:
        raise ValueError(&quot;&#x27;timestep&#x27; must be provided when &#x27;timestep_conditioning&#x27; is True&quot;)
    scaled_timestep = timestep * self.timestep_scale_multiplier.to(sample)

for up_block in self.up_blocks:
    if isinstance(up_block, UNetMidBlock3D):
        block_kwargs = &#123;
            &quot;causal&quot;: self.causal,
            &quot;timestep&quot;: scaled_timestep if self.timestep_conditioning else None,
            &quot;generator&quot;: generator,
        &#125;
        sample = up_block(sample, **block_kwargs)
    elif isinstance(up_block, ResnetBlock3D):
        sample = up_block(sample, causal=self.causal, generator=generator)
    else:
        sample = up_block(sample, causal=self.causal)

sample = self.conv_norm_out(sample)

if self.timestep_conditioning:
    embedded_timestep = self.last_time_embedder(
        timestep=scaled_timestep.flatten(),
        hidden_dtype=sample.dtype,
    )
    embedded_timestep = embedded_timestep.view(batch_size, embedded_timestep.shape&#91;-1&#93;, 1, 1, 1)
    ada_values = self.last_scale_shift_table&#91;None, ..., None, None, None&#93;.to(
        device=sample.device, dtype=sample.dtype) + embedded_timestep.reshape(
            batch_size,
            2,
            -1,
            embedded_timestep.shape&#91;-3&#93;,
            embedded_timestep.shape&#91;-2&#93;,
            embedded_timestep.shape&#91;-1&#93;,
        )
    shift, scale = ada_values.unbind(dim=1)
    sample = sample * (1 + scale) + shift

sample = self.conv_act(sample)
sample = self.conv_out(sample, causal=self.causal)
sample = unpatchify(sample, patch_size_hw=self.patch_size, patch_size_t=1)

return sample</code></pre></details>

fastvideo.models.vaes.ltx2vae.VideoEncoderConfigurator

VideoEncoderConfigurator()

Configurator for creating a video VAE Encoder from a configuration dictionary.

Source
class VideoEncoderConfigurator:
    """Configurator for creating a video VAE Encoder from a configuration dictionary."""
@classmethod
def from_config(cls, config: dict) -&gt; VideoEncoder:
    config = config.get(&quot;vae&quot;, config)
    convolution_dimensions = config.get(&quot;dims&quot;, 3)
    in_channels = config.get(&quot;in_channels&quot;, 3)
    latent_channels = config.get(&quot;latent_channels&quot;, 128)
    encoder_spatial_padding_mode = PaddingModeType(config.get(&quot;encoder_spatial_padding_mode&quot;, &quot;zeros&quot;))
    encoder_blocks = config.get(&quot;encoder_blocks&quot;, &#91;&#93;)
    patch_size = config.get(&quot;patch_size&quot;, 4)
    norm_layer_str = config.get(&quot;norm_layer&quot;, &quot;pixel_norm&quot;)
    latent_log_var_str = config.get(&quot;latent_log_var&quot;, &quot;uniform&quot;)

    return VideoEncoder(
        convolution_dimensions=convolution_dimensions,
        in_channels=in_channels,
        out_channels=latent_channels,
        encoder_blocks=encoder_blocks,
        patch_size=patch_size,
        norm_layer=NormLayerType(norm_layer_str),
        latent_log_var=LogVarianceType(latent_log_var_str),
        encoder_spatial_padding_mode=encoder_spatial_padding_mode,
    )</code></pre></details>

fastvideo.models.vaes.ltx2vae.VideoEncoderConfigurator.from_config

from_config(config: dict) -> VideoEncoder
Source
@classmethod
def from_config(cls, config: dict) -> VideoEncoder:
    config = config.get("vae", config)
    convolution_dimensions = config.get("dims", 3)
    in_channels = config.get("in_channels", 3)
    latent_channels = config.get("latent_channels", 128)
    encoder_spatial_padding_mode = PaddingModeType(config.get("encoder_spatial_padding_mode", "zeros"))
    encoder_blocks = config.get("encoder_blocks", [])
    patch_size = config.get("patch_size", 4)
    norm_layer_str = config.get("norm_layer", "pixel_norm")
    latent_log_var_str = config.get("latent_log_var", "uniform")
return VideoEncoder(
    convolution_dimensions=convolution_dimensions,
    in_channels=in_channels,
    out_channels=latent_channels,
    encoder_blocks=encoder_blocks,
    patch_size=patch_size,
    norm_layer=NormLayerType(norm_layer_str),
    latent_log_var=LogVarianceType(latent_log_var_str),
    encoder_spatial_padding_mode=encoder_spatial_padding_mode,
)</code></pre></details>

fastvideo.models.vaes.ltx2vae.VideoDecoderConfigurator

VideoDecoderConfigurator()

Configurator for creating a video VAE Decoder from a configuration dictionary.

Source
class VideoDecoderConfigurator:
    """Configurator for creating a video VAE Decoder from a configuration dictionary."""
@classmethod
def from_config(cls, config: dict) -&gt; VideoDecoder:
    config = config.get(&quot;vae&quot;, config)
    convolution_dimensions = config.get(&quot;dims&quot;, 3)
    latent_channels = config.get(&quot;latent_channels&quot;, 128)
    decoder_spatial_padding_mode = PaddingModeType(config.get(&quot;decoder_spatial_padding_mode&quot;, &quot;reflect&quot;))
    out_channels = config.get(&quot;out_channels&quot;, 3)
    decoder_blocks = config.get(&quot;decoder_blocks&quot;, &#91;&#93;)
    patch_size = config.get(&quot;patch_size&quot;, 4)
    norm_layer_str = config.get(&quot;norm_layer&quot;, &quot;pixel_norm&quot;)
    causal = config.get(&quot;causal_decoder&quot;, False)
    timestep_conditioning = config.get(&quot;timestep_conditioning&quot;, True)
    base_channels = config.get(&quot;decoder_base_channels&quot;, 128)

    return VideoDecoder(
        convolution_dimensions=convolution_dimensions,
        in_channels=latent_channels,
        out_channels=out_channels,
        decoder_blocks=decoder_blocks,
        patch_size=patch_size,
        norm_layer=NormLayerType(norm_layer_str),
        causal=causal,
        timestep_conditioning=timestep_conditioning,
        decoder_spatial_padding_mode=decoder_spatial_padding_mode,
        base_channels=base_channels,
    )</code></pre></details>

fastvideo.models.vaes.ltx2vae.VideoDecoderConfigurator.from_config

from_config(config: dict) -> VideoDecoder
Source
@classmethod
def from_config(cls, config: dict) -> VideoDecoder:
    config = config.get("vae", config)
    convolution_dimensions = config.get("dims", 3)
    latent_channels = config.get("latent_channels", 128)
    decoder_spatial_padding_mode = PaddingModeType(config.get("decoder_spatial_padding_mode", "reflect"))
    out_channels = config.get("out_channels", 3)
    decoder_blocks = config.get("decoder_blocks", [])
    patch_size = config.get("patch_size", 4)
    norm_layer_str = config.get("norm_layer", "pixel_norm")
    causal = config.get("causal_decoder", False)
    timestep_conditioning = config.get("timestep_conditioning", True)
    base_channels = config.get("decoder_base_channels", 128)
return VideoDecoder(
    convolution_dimensions=convolution_dimensions,
    in_channels=latent_channels,
    out_channels=out_channels,
    decoder_blocks=decoder_blocks,
    patch_size=patch_size,
    norm_layer=NormLayerType(norm_layer_str),
    causal=causal,
    timestep_conditioning=timestep_conditioning,
    decoder_spatial_padding_mode=decoder_spatial_padding_mode,
    base_channels=base_channels,
)</code></pre></details>

fastvideo.models.vaes.ltx2vae.LTX2VideoEncoder

LTX2VideoEncoder(config: dict[str, Any])

LTX-2 Video Encoder wrapper for FastVideo compatibility.

Source
class LTX2VideoEncoder(nn.Module):
    """LTX-2 Video Encoder wrapper for FastVideo compatibility."""
def __init__(self, config: dict&#91;str, Any&#93;):
    super().__init__()
    self.model: VideoEncoder = VideoEncoderConfigurator.from_config(config)

def forward(self, sample: torch.Tensor) -&gt; torch.Tensor:
    return self.model(sample)</code></pre></details>

fastvideo.models.vaes.ltx2vae.LTX2VideoEncoder.forward

forward(sample: torch.Tensor) -> torch.Tensor
Source
def forward(self, sample: torch.Tensor) -> torch.Tensor:
    return self.model(sample)

fastvideo.models.vaes.ltx2vae.LTX2VideoDecoder

LTX2VideoDecoder(config: dict[str, Any])

LTX-2 Video Decoder wrapper for FastVideo compatibility.

Source
class LTX2VideoDecoder(nn.Module):
    """LTX-2 Video Decoder wrapper for FastVideo compatibility."""
def __init__(self, config: dict&#91;str, Any&#93;):
    super().__init__()
    self.model: VideoDecoder = VideoDecoderConfigurator.from_config(config)

def forward(
    self,
    sample: torch.Tensor,
    timestep: torch.Tensor | None = None,
    generator: torch.Generator | None = None,
) -&gt; torch.Tensor:
    return self.model(sample, timestep=timestep, generator=generator)</code></pre></details>

fastvideo.models.vaes.ltx2vae.LTX2VideoDecoder.forward

forward(sample: torch.Tensor, timestep: torch.Tensor | None = None, generator: torch.Generator | None = None) -> torch.Tensor
Source
def forward(
    self,
    sample: torch.Tensor,
    timestep: torch.Tensor | None = None,
    generator: torch.Generator | None = None,
) -> torch.Tensor:
    return self.model(sample, timestep=timestep, generator=generator)

fastvideo.models.vaes.ltx2vae.LTX2CausalVideoAutoencoder

LTX2CausalVideoAutoencoder(config: dict[str, Any])

LTX-2 VAE that exposes FastVideo's VAE encode/decode interface. Supports tiled decoding to reduce memory usage for high-resolution videos.

Source
class LTX2CausalVideoAutoencoder(nn.Module):
    """
    LTX-2 VAE that exposes FastVideo's VAE encode/decode interface.
    Supports tiled decoding to reduce memory usage for high-resolution videos.
    """
# LTX-2 VAE scale factors
TIME_SCALE: int = 8
SPATIAL_SCALE: int = 32
_compile_conditions = &#91;_is_ltx2_vae_codec&#93;

def __init__(self, config: dict&#91;str, Any&#93;):
    super().__init__()
    self.config = config
    self.encoder = VideoEncoderConfigurator.from_config(config)
    self.decoder = VideoDecoderConfigurator.from_config(config)
    self._use_tiling: bool = False
    self._use_channels_last_3d: bool = False

    if envs.FASTVIDEO_LTX2_VAE_CHANNELS_LAST_3D.get():
        self.enable_channels_last_3d()

def _as_channels_last_3d(self, tensor: torch.Tensor) -&gt; torch.Tensor:
    if not self._use_channels_last_3d or tensor.ndim != 5 or tensor.is_contiguous(
            memory_format=torch.channels_last_3d):
        return tensor
    return tensor.contiguous(memory_format=torch.channels_last_3d)

def enable_channels_last_3d(self) -&gt; None:
    &quot;&quot;&quot;Enable channels-last layout for 3D VAE convolutions.&quot;&quot;&quot;
    self._use_channels_last_3d = True
    self.encoder.to(memory_format=torch.channels_last_3d)
    self.decoder.to(memory_format=torch.channels_last_3d)

def disable_channels_last_3d(self) -&gt; None:
    &quot;&quot;&quot;Restore contiguous layout for 3D VAE convolutions.&quot;&quot;&quot;
    self._use_channels_last_3d = False
    self.encoder.to(memory_format=torch.contiguous_format)
    self.decoder.to(memory_format=torch.contiguous_format)

def encode(self, x: torch.Tensor) -&gt; DiagonalGaussianDistribution:
    x = self._as_channels_last_3d(x)
    means = self.encoder(x)
    zeros = torch.zeros_like(means)
    return DiagonalGaussianDistribution(torch.cat(&#91;means, zeros&#93;, dim=1), deterministic=True)

def decode(
    self,
    z: torch.Tensor,
    timestep: torch.Tensor | None = None,
    generator: torch.Generator | None = None,
) -&gt; torch.Tensor:
    &quot;&quot;&quot;Decode latents to video, using tiling if enabled.&quot;&quot;&quot;
    z = self._as_channels_last_3d(z)
    if self._use_tiling:
        # Collect all chunks from tiled decode and concatenate
        chunks = list(self.tiled_decode(z, TilingConfig.default(), timestep, generator))
        return torch.cat(chunks, dim=2)  # Concatenate along temporal dimension
    return self.decoder(z, timestep=timestep, generator=generator)

def enable_tiling(self) -&gt; None:
    &quot;&quot;&quot;Enable tiled decoding with default configuration.&quot;&quot;&quot;
    self._use_tiling = True

def disable_tiling(self) -&gt; None:
    &quot;&quot;&quot;Disable tiled decoding.&quot;&quot;&quot;
    self._use_tiling = False

def _prepare_tiles(
    self,
    latent: torch.Tensor,
    tiling_config: TilingConfig | None,
) -&gt; List&#91;Tile&#93;:
    &quot;&quot;&quot;Prepare tiles for tiled decoding based on tiling configuration.&quot;&quot;&quot;
    splitters: List&#91;SplitOperation&#93; = &#91;DEFAULT_SPLIT_OPERATION&#93; * 5
    mappers: List&#91;MappingOperation&#93; = &#91;DEFAULT_MAPPING_OPERATION&#93; * 5

    if tiling_config is not None and tiling_config.spatial_config is not None:
        cfg = tiling_config.spatial_config
        tile_size = cfg.tile_size_in_pixels // self.SPATIAL_SCALE
        overlap = cfg.tile_overlap_in_pixels // self.SPATIAL_SCALE
        splitters&#91;3&#93; = split_in_spatial(tile_size, overlap)
        splitters&#91;4&#93; = split_in_spatial(tile_size, overlap)
        mappers&#91;3&#93; = to_mapping_operation(map_spatial_slice, self.SPATIAL_SCALE)
        mappers&#91;4&#93; = to_mapping_operation(map_spatial_slice, self.SPATIAL_SCALE)

    if tiling_config is not None and tiling_config.temporal_config is not None:
        cfg = tiling_config.temporal_config
        tile_size = cfg.tile_size_in_frames // self.TIME_SCALE
        overlap = cfg.tile_overlap_in_frames // self.TIME_SCALE
        splitters&#91;2&#93; = split_in_temporal(tile_size, overlap)
        mappers&#91;2&#93; = to_mapping_operation(map_temporal_slice, self.TIME_SCALE)

    return create_tiles(latent.shape, splitters, mappers)

def _group_tiles_by_temporal_slice(self, tiles: List&#91;Tile&#93;) -&gt; List&#91;List&#91;Tile&#93;&#93;:
    &quot;&quot;&quot;Group tiles by their temporal output slice.&quot;&quot;&quot;
    if not tiles:
        return &#91;&#93;

    groups = &#91;&#93;
    current_slice = tiles&#91;0&#93;.out_coords&#91;2&#93;
    current_group = &#91;&#93;

    for tile in tiles:
        tile_slice = tile.out_coords&#91;2&#93;
        if tile_slice == current_slice:
            current_group.append(tile)
        else:
            groups.append(current_group)
            current_slice = tile_slice
            current_group = &#91;tile&#93;

    if current_group:
        groups.append(current_group)

    return groups

def _accumulate_temporal_group_into_buffer(
    self,
    group_tiles: List&#91;Tile&#93;,
    buffer: torch.Tensor,
    latent: torch.Tensor,
    timestep: torch.Tensor | None,
    generator: torch.Generator | None,
) -&gt; torch.Tensor:
    &quot;&quot;&quot;Decode and accumulate all tiles of a temporal group into a local buffer.&quot;&quot;&quot;
    temporal_slice = group_tiles&#91;0&#93;.out_coords&#91;2&#93;
    weights = torch.zeros_like(buffer)

    for tile in group_tiles:
        decoded_tile = self.decoder(latent&#91;tile.in_coords&#93;, timestep, generator)
        mask = tile.blend_mask.to(device=buffer.device, dtype=buffer.dtype)
        temporal_offset = tile.out_coords&#91;2&#93;.start - temporal_slice.start
        expected_temporal_len = tile.out_coords&#91;2&#93;.stop - tile.out_coords&#91;2&#93;.start
        decoded_temporal_len = decoded_tile.shape&#91;2&#93;

        actual_temporal_len = min(expected_temporal_len, decoded_temporal_len, buffer.shape&#91;2&#93; - temporal_offset)

        chunk_coords = (
            slice(None),  # batch
            slice(None),  # channels
            slice(temporal_offset, temporal_offset + actual_temporal_len),
            tile.out_coords&#91;3&#93;,  # height
            tile.out_coords&#91;4&#93;,  # width
        )

        decoded_slice = decoded_tile&#91;:, :, :actual_temporal_len, :, :&#93;
        mask_slice = mask&#91;:, :, :actual_temporal_len, :, :&#93; if mask.shape&#91;2&#93; &gt; 1 else mask

        buffer&#91;chunk_coords&#93; += decoded_slice * mask_slice
        weights&#91;chunk_coords&#93; += mask_slice

    return weights

def tiled_decode(
    self,
    latent: torch.Tensor,
    tiling_config: TilingConfig | None = None,
    timestep: torch.Tensor | None = None,
    generator: torch.Generator | None = None,
) -&gt; Iterator&#91;torch.Tensor&#93;:
    &quot;&quot;&quot;
    Decode a latent tensor into video frames using tiled processing.
    Splits the latent tensor into tiles, decodes each tile individually,
    and yields video chunks as they become available.

    Args:
        latent: Input latent tensor (B, C, F&#x27;, H&#x27;, W&#x27;).
        tiling_config: Tiling configuration for the latent tensor.
        timestep: Optional timestep for decoder conditioning.
        generator: Optional random generator for deterministic decoding.

    Yields:
        Video chunks (B, C, T, H, W) by temporal slices.
    &quot;&quot;&quot;
    full_video_shape = VideoLatentShape.from_torch_shape(latent.shape).upscale(self.TIME_SCALE, self.SPATIAL_SCALE)
    tiles = self._prepare_tiles(latent, tiling_config)
    temporal_groups = self._group_tiles_by_temporal_slice(tiles)

    previous_chunk = None
    previous_weights = None
    previous_temporal_slice = None

    for temporal_group_tiles in temporal_groups:
        curr_temporal_slice = temporal_group_tiles&#91;0&#93;.out_coords&#91;2&#93;

        temporal_tile_buffer_shape = full_video_shape._replace(frames=curr_temporal_slice.stop -
                                                               curr_temporal_slice.start, )

        buffer = torch.zeros(
            temporal_tile_buffer_shape.to_torch_shape(),
            device=latent.device,
            dtype=latent.dtype,
        )

        curr_weights = self._accumulate_temporal_group_into_buffer(
            group_tiles=temporal_group_tiles,
            buffer=buffer,
            latent=latent,
            timestep=timestep,
            generator=generator,
        )

        # Blend with previous temporal chunk if it exists
        if previous_chunk is not None:
            if previous_temporal_slice.stop &gt; curr_temporal_slice.start:
                overlap_len = previous_temporal_slice.stop - curr_temporal_slice.start
                temporal_overlap_slice = slice(curr_temporal_slice.start - previous_temporal_slice.start, None)

                previous_chunk&#91;:, :, temporal_overlap_slice, :, :&#93; += buffer&#91;:, :, slice(0, overlap_len), :, :&#93;
                previous_weights&#91;:, :, temporal_overlap_slice, :, :&#93; += curr_weights&#91;:, :,
                                                                                     slice(0, overlap_len), :, :&#93;

                buffer&#91;:, :, slice(0, overlap_len), :, :&#93; = previous_chunk&#91;:, :, temporal_overlap_slice, :, :&#93;
                curr_weights&#91;:, :, slice(0, overlap_len), :, :&#93; = previous_weights&#91;:, :,
                                                                                   temporal_overlap_slice, :, :&#93;

            # Yield the non-overlapping part of the previous chunk
            previous_weights = previous_weights.clamp(min=1e-8)
            yield_len = curr_temporal_slice.start - previous_temporal_slice.start
            yield (previous_chunk / previous_weights)&#91;:, :, :yield_len, :, :&#93;

        # Update state for next iteration
        previous_chunk = buffer
        previous_weights = curr_weights
        previous_temporal_slice = curr_temporal_slice

    # Yield any remaining chunk
    if previous_chunk is not None:
        previous_weights = previous_weights.clamp(min=1e-8)
        yield previous_chunk / previous_weights</code></pre></details>

fastvideo.models.vaes.ltx2vae.LTX2CausalVideoAutoencoder.enable_channels_last_3d

enable_channels_last_3d() -> None

Enable channels-last layout for 3D VAE convolutions.

Source
def enable_channels_last_3d(self) -> None:
    """Enable channels-last layout for 3D VAE convolutions."""
    self._use_channels_last_3d = True
    self.encoder.to(memory_format=torch.channels_last_3d)
    self.decoder.to(memory_format=torch.channels_last_3d)

fastvideo.models.vaes.ltx2vae.LTX2CausalVideoAutoencoder.disable_channels_last_3d

disable_channels_last_3d() -> None

Restore contiguous layout for 3D VAE convolutions.

Source
def disable_channels_last_3d(self) -> None:
    """Restore contiguous layout for 3D VAE convolutions."""
    self._use_channels_last_3d = False
    self.encoder.to(memory_format=torch.contiguous_format)
    self.decoder.to(memory_format=torch.contiguous_format)

fastvideo.models.vaes.ltx2vae.LTX2CausalVideoAutoencoder.encode

encode(x: torch.Tensor) -> DiagonalGaussianDistribution
Source
def encode(self, x: torch.Tensor) -> DiagonalGaussianDistribution:
    x = self._as_channels_last_3d(x)
    means = self.encoder(x)
    zeros = torch.zeros_like(means)
    return DiagonalGaussianDistribution(torch.cat([means, zeros], dim=1), deterministic=True)

fastvideo.models.vaes.ltx2vae.LTX2CausalVideoAutoencoder.decode

decode(z: torch.Tensor, timestep: torch.Tensor | None = None, generator: torch.Generator | None = None) -> torch.Tensor

Decode latents to video, using tiling if enabled.

Source
def decode(
    self,
    z: torch.Tensor,
    timestep: torch.Tensor | None = None,
    generator: torch.Generator | None = None,
) -> torch.Tensor:
    """Decode latents to video, using tiling if enabled."""
    z = self._as_channels_last_3d(z)
    if self._use_tiling:
        # Collect all chunks from tiled decode and concatenate
        chunks = list(self.tiled_decode(z, TilingConfig.default(), timestep, generator))
        return torch.cat(chunks, dim=2)  # Concatenate along temporal dimension
    return self.decoder(z, timestep=timestep, generator=generator)

fastvideo.models.vaes.ltx2vae.LTX2CausalVideoAutoencoder.enable_tiling

enable_tiling() -> None

Enable tiled decoding with default configuration.

Source
def enable_tiling(self) -> None:
    """Enable tiled decoding with default configuration."""
    self._use_tiling = True

fastvideo.models.vaes.ltx2vae.LTX2CausalVideoAutoencoder.disable_tiling

disable_tiling() -> None

Disable tiled decoding.

Source
def disable_tiling(self) -> None:
    """Disable tiled decoding."""
    self._use_tiling = False

fastvideo.models.vaes.ltx2vae.LTX2CausalVideoAutoencoder.tiled_decode

tiled_decode(latent: torch.Tensor, tiling_config: TilingConfig | None = None, timestep: torch.Tensor | None = None, generator: torch.Generator | None = None) -> Iterator[torch.Tensor]

Decode a latent tensor into video frames using tiled processing. Splits the latent tensor into tiles, decodes each tile individually, and yields video chunks as they become available.

Parameters

latent: torch.Tensor

Input latent tensor (B, C, F', H', W').

tiling_config: TilingConfig | None

Tiling configuration for the latent tensor.

timestep: torch.Tensor | None

Optional timestep for decoder conditioning.

generator: torch.Generator | None

Optional random generator for deterministic decoding.

Yields

torch.Tensor

Video chunks (B, C, T, H, W) by temporal slices.

Source
def tiled_decode(
    self,
    latent: torch.Tensor,
    tiling_config: TilingConfig | None = None,
    timestep: torch.Tensor | None = None,
    generator: torch.Generator | None = None,
) -> Iterator[torch.Tensor]:
    """
    Decode a latent tensor into video frames using tiled processing.
    Splits the latent tensor into tiles, decodes each tile individually,
    and yields video chunks as they become available.
Args:
    latent: Input latent tensor (B, C, F&#x27;, H&#x27;, W&#x27;).
    tiling_config: Tiling configuration for the latent tensor.
    timestep: Optional timestep for decoder conditioning.
    generator: Optional random generator for deterministic decoding.

Yields:
    Video chunks (B, C, T, H, W) by temporal slices.
&quot;&quot;&quot;
full_video_shape = VideoLatentShape.from_torch_shape(latent.shape).upscale(self.TIME_SCALE, self.SPATIAL_SCALE)
tiles = self._prepare_tiles(latent, tiling_config)
temporal_groups = self._group_tiles_by_temporal_slice(tiles)

previous_chunk = None
previous_weights = None
previous_temporal_slice = None

for temporal_group_tiles in temporal_groups:
    curr_temporal_slice = temporal_group_tiles&#91;0&#93;.out_coords&#91;2&#93;

    temporal_tile_buffer_shape = full_video_shape._replace(frames=curr_temporal_slice.stop -
                                                           curr_temporal_slice.start, )

    buffer = torch.zeros(
        temporal_tile_buffer_shape.to_torch_shape(),
        device=latent.device,
        dtype=latent.dtype,
    )

    curr_weights = self._accumulate_temporal_group_into_buffer(
        group_tiles=temporal_group_tiles,
        buffer=buffer,
        latent=latent,
        timestep=timestep,
        generator=generator,
    )

    # Blend with previous temporal chunk if it exists
    if previous_chunk is not None:
        if previous_temporal_slice.stop &gt; curr_temporal_slice.start:
            overlap_len = previous_temporal_slice.stop - curr_temporal_slice.start
            temporal_overlap_slice = slice(curr_temporal_slice.start - previous_temporal_slice.start, None)

            previous_chunk&#91;:, :, temporal_overlap_slice, :, :&#93; += buffer&#91;:, :, slice(0, overlap_len), :, :&#93;
            previous_weights&#91;:, :, temporal_overlap_slice, :, :&#93; += curr_weights&#91;:, :,
                                                                                 slice(0, overlap_len), :, :&#93;

            buffer&#91;:, :, slice(0, overlap_len), :, :&#93; = previous_chunk&#91;:, :, temporal_overlap_slice, :, :&#93;
            curr_weights&#91;:, :, slice(0, overlap_len), :, :&#93; = previous_weights&#91;:, :,
                                                                               temporal_overlap_slice, :, :&#93;

        # Yield the non-overlapping part of the previous chunk
        previous_weights = previous_weights.clamp(min=1e-8)
        yield_len = curr_temporal_slice.start - previous_temporal_slice.start
        yield (previous_chunk / previous_weights)&#91;:, :, :yield_len, :, :&#93;

    # Update state for next iteration
    previous_chunk = buffer
    previous_weights = curr_weights
    previous_temporal_slice = curr_temporal_slice

# Yield any remaining chunk
if previous_chunk is not None:
    previous_weights = previous_weights.clamp(min=1e-8)
    yield previous_chunk / previous_weights</code></pre></details>

Was this page helpful?