---
title: ltx2vae
---

<div class="python-reference">
<a id="fastvideo.models.vaes.ltx2vae-classes"></a>
<a id="fastvideo.models.vaes.ltx2vae-functions"></a>
<a id="fastvideo.models.vaes.ltx2vae.LTX2CausalVideoAutoencoder-functions"></a>
<a id="fastvideo.models.vaes.ltx2vae"></a>
<p>LTX-2 Video VAE implementation</p>
<h2 id="fastvideo.models.vaes.ltx2vae.DiagonalGaussianDistribution"><code>fastvideo.models.vaes.ltx2vae.DiagonalGaussianDistribution</code></h2>
<pre class="doc-signature"><code>DiagonalGaussianDistribution(parameters: torch.Tensor, deterministic: bool = False)</code></pre>


<h3 id="fastvideo.models.vaes.ltx2vae.DiagonalGaussianDistribution.sample"><code>fastvideo.models.vaes.ltx2vae.DiagonalGaussianDistribution.sample</code></h3>
<pre class="doc-signature"><code>sample(generator: torch.Generator | None = None) -&gt; torch.Tensor</code></pre>

<h3 id="fastvideo.models.vaes.ltx2vae.DiagonalGaussianDistribution.kl"><code>fastvideo.models.vaes.ltx2vae.DiagonalGaussianDistribution.kl</code></h3>
<pre class="doc-signature"><code>kl(other: Optional&#91;DiagonalGaussianDistribution&#93; = None) -&gt; torch.Tensor</code></pre>

<h3 id="fastvideo.models.vaes.ltx2vae.DiagonalGaussianDistribution.nll"><code>fastvideo.models.vaes.ltx2vae.DiagonalGaussianDistribution.nll</code></h3>
<pre class="doc-signature"><code>nll(sample: torch.Tensor, dims: tuple&#91;int, ...&#93; = (1, 2, 3)) -&gt; torch.Tensor</code></pre>

<h3 id="fastvideo.models.vaes.ltx2vae.DiagonalGaussianDistribution.mode"><code>fastvideo.models.vaes.ltx2vae.DiagonalGaussianDistribution.mode</code></h3>
<pre class="doc-signature"><code>mode() -&gt; torch.Tensor</code></pre>

<h2 id="fastvideo.models.vaes.ltx2vae.NormLayerType"><code>fastvideo.models.vaes.ltx2vae.NormLayerType</code></h2>
<pre class="doc-signature"><code>NormLayerType()</code></pre>

<details><summary>Source</summary><pre><code>class NormLayerType(Enum):
    GROUP_NORM = &quot;group_norm&quot;
    PIXEL_NORM = &quot;pixel_norm&quot;</code></pre></details>
<h2 id="fastvideo.models.vaes.ltx2vae.LogVarianceType"><code>fastvideo.models.vaes.ltx2vae.LogVarianceType</code></h2>
<pre class="doc-signature"><code>LogVarianceType()</code></pre>

<details><summary>Source</summary><pre><code>class LogVarianceType(Enum):
    PER_CHANNEL = &quot;per_channel&quot;
    UNIFORM = &quot;uniform&quot;
    CONSTANT = &quot;constant&quot;
    NONE = &quot;none&quot;</code></pre></details>
<h2 id="fastvideo.models.vaes.ltx2vae.PaddingModeType"><code>fastvideo.models.vaes.ltx2vae.PaddingModeType</code></h2>
<pre class="doc-signature"><code>PaddingModeType()</code></pre>

<details><summary>Source</summary><pre><code>class PaddingModeType(Enum):
    ZEROS = &quot;zeros&quot;
    REFLECT = &quot;reflect&quot;
    REPLICATE = &quot;replicate&quot;
    CIRCULAR = &quot;circular&quot;</code></pre></details>
<h2 id="fastvideo.models.vaes.ltx2vae.SpatialTilingConfig"><code>fastvideo.models.vaes.ltx2vae.SpatialTilingConfig</code></h2>
<pre class="doc-signature"><code>SpatialTilingConfig(tile_size_in_pixels: int, tile_overlap_in_pixels: int = 0)</code></pre>
<p>Configuration for dividing each frame into spatial tiles with optional overlap.</p>

<details><summary>Source</summary><pre><code>@dataclass(frozen=True)
class SpatialTilingConfig:
    &quot;&quot;&quot;Configuration for dividing each frame into spatial tiles with optional overlap.&quot;&quot;&quot;
    tile_size_in_pixels: int
    tile_overlap_in_pixels: int = 0

    def __post_init__(self) -&gt; None:
        if self.tile_size_in_pixels &lt; 64:
            raise ValueError(f&quot;tile_size_in_pixels must be at least 64, got &#123;self.tile_size_in_pixels&#125;&quot;)
        if self.tile_size_in_pixels % 32 != 0:
            raise ValueError(f&quot;tile_size_in_pixels must be divisible by 32, got &#123;self.tile_size_in_pixels&#125;&quot;)
        if self.tile_overlap_in_pixels % 32 != 0:
            raise ValueError(f&quot;tile_overlap_in_pixels must be divisible by 32, got &#123;self.tile_overlap_in_pixels&#125;&quot;)
        if self.tile_overlap_in_pixels &gt;= self.tile_size_in_pixels:
            raise ValueError(
                f&quot;Overlap must be less than tile size, got &#123;self.tile_overlap_in_pixels&#125; and &#123;self.tile_size_in_pixels&#125;&quot;
            )</code></pre></details>
<h2 id="fastvideo.models.vaes.ltx2vae.TemporalTilingConfig"><code>fastvideo.models.vaes.ltx2vae.TemporalTilingConfig</code></h2>
<pre class="doc-signature"><code>TemporalTilingConfig(tile_size_in_frames: int, tile_overlap_in_frames: int = 0)</code></pre>
<p>Configuration for dividing a video into temporal tiles (chunks of frames) with optional overlap.</p>

<details><summary>Source</summary><pre><code>@dataclass(frozen=True)
class TemporalTilingConfig:
    &quot;&quot;&quot;Configuration for dividing a video into temporal tiles (chunks of frames) with optional overlap.&quot;&quot;&quot;
    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>
<h2 id="fastvideo.models.vaes.ltx2vae.TilingConfig"><code>fastvideo.models.vaes.ltx2vae.TilingConfig</code></h2>
<pre class="doc-signature"><code>TilingConfig(spatial_config: SpatialTilingConfig | None = None, temporal_config: TemporalTilingConfig | None = None)</code></pre>
<p>Configuration for splitting video into tiles with optional overlap.</p>

<details><summary>Source</summary><pre><code>@dataclass(frozen=True)
class TilingConfig:
    &quot;&quot;&quot;Configuration for splitting video into tiles with optional overlap.&quot;&quot;&quot;
    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>
<h3 id="fastvideo.models.vaes.ltx2vae.TilingConfig.default"><code>fastvideo.models.vaes.ltx2vae.TilingConfig.default</code></h3>
<pre class="doc-signature"><code>default() -&gt; &#x27;TilingConfig&#x27;</code></pre>

<details><summary>Source</summary><pre><code>@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>
<h2 id="fastvideo.models.vaes.ltx2vae.DimensionIntervals"><code>fastvideo.models.vaes.ltx2vae.DimensionIntervals</code></h2>
<pre class="doc-signature"><code>DimensionIntervals(starts: List&#91;int&#93;, ends: List&#91;int&#93;, left_ramps: List&#91;int&#93;, right_ramps: List&#91;int&#93;)</code></pre>
<p>Intervals which a single dimension of the latent space is split into.</p>

<details><summary>Source</summary><pre><code>@dataclass(frozen=True)
class DimensionIntervals:
    &quot;&quot;&quot;Intervals which a single dimension of the latent space is split into.&quot;&quot;&quot;
    starts: List&#91;int&#93;
    ends: List&#91;int&#93;
    left_ramps: List&#91;int&#93;
    right_ramps: List&#91;int&#93;</code></pre></details>
<h2 id="fastvideo.models.vaes.ltx2vae.LatentIntervals"><code>fastvideo.models.vaes.ltx2vae.LatentIntervals</code></h2>
<pre class="doc-signature"><code>LatentIntervals(original_shape: torch.Size, dimension_intervals: Tuple&#91;DimensionIntervals, ...&#93;)</code></pre>
<p>Intervals which the latent tensor of given shape is split into.</p>

<details><summary>Source</summary><pre><code>@dataclass(frozen=True)
class LatentIntervals:
    &quot;&quot;&quot;Intervals which the latent tensor of given shape is split into.&quot;&quot;&quot;
    original_shape: torch.Size
    dimension_intervals: Tuple&#91;DimensionIntervals, ...&#93;</code></pre></details>
<h2 id="fastvideo.models.vaes.ltx2vae.VideoLatentShape"><code>fastvideo.models.vaes.ltx2vae.VideoLatentShape</code></h2>
<pre class="doc-signature"><code>VideoLatentShape()</code></pre>
<p>Shape of the tensor representing video in VAE latent space.</p>
<details><summary>Source</summary><pre><code>class VideoLatentShape(NamedTuple):
    &quot;&quot;&quot;Shape of the tensor representing video in VAE latent space.&quot;&quot;&quot;
    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>
<h3 id="fastvideo.models.vaes.ltx2vae.VideoLatentShape.to_torch_shape"><code>fastvideo.models.vaes.ltx2vae.VideoLatentShape.to_torch_shape</code></h3>
<pre class="doc-signature"><code>to_torch_shape() -&gt; torch.Size</code></pre>

<details><summary>Source</summary><pre><code>def to_torch_shape(self) -&gt; torch.Size:
    return torch.Size(&#91;self.batch, self.channels, self.frames, self.height, self.width&#93;)</code></pre></details>
<h3 id="fastvideo.models.vaes.ltx2vae.VideoLatentShape.from_torch_shape"><code>fastvideo.models.vaes.ltx2vae.VideoLatentShape.from_torch_shape</code></h3>
<pre class="doc-signature"><code>from_torch_shape(shape: torch.Size) -&gt; &#x27;VideoLatentShape&#x27;</code></pre>

<details><summary>Source</summary><pre><code>@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;,
    )</code></pre></details>
<h3 id="fastvideo.models.vaes.ltx2vae.VideoLatentShape.upscale"><code>fastvideo.models.vaes.ltx2vae.VideoLatentShape.upscale</code></h3>
<pre class="doc-signature"><code>upscale(time_scale: int, spatial_scale: int) -&gt; &#x27;VideoLatentShape&#x27;</code></pre>

<details><summary>Source</summary><pre><code>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>
<h2 id="fastvideo.models.vaes.ltx2vae.Tile"><code>fastvideo.models.vaes.ltx2vae.Tile</code></h2>
<pre class="doc-signature"><code>Tile()</code></pre>
<p>Represents a single tile.</p>
<details><summary>Source</summary><pre><code>class Tile(NamedTuple):
    &quot;&quot;&quot;Represents a single tile.&quot;&quot;&quot;
    in_coords: Tuple&#91;slice, ...&#93;
    out_coords: Tuple&#91;slice, ...&#93;
    masks_1d: Tuple&#91;torch.Tensor | None, ...&#93;

    @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>
<h2 id="fastvideo.models.vaes.ltx2vae.compute_trapezoidal_mask_1d"><code>fastvideo.models.vaes.ltx2vae.compute_trapezoidal_mask_1d</code></h2>
<pre class="doc-signature"><code>compute_trapezoidal_mask_1d(length: int, ramp_left: int, ramp_right: int, left_starts_from_0: bool = False) -&gt; torch.Tensor</code></pre>
<p>Generate a 1D trapezoidal blending mask with linear ramps.</p>
<details><summary>Source</summary><pre><code>def compute_trapezoidal_mask_1d(
    length: int,
    ramp_left: int,
    ramp_right: int,
    left_starts_from_0: bool = False,
) -&gt; torch.Tensor:
    &quot;&quot;&quot;Generate a 1D trapezoidal blending mask with linear ramps.&quot;&quot;&quot;
    if length &lt;= 0:
        raise ValueError(&quot;Mask length must be positive.&quot;)

    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>
<h2 id="fastvideo.models.vaes.ltx2vae.default_split_operation"><code>fastvideo.models.vaes.ltx2vae.default_split_operation</code></h2>
<pre class="doc-signature"><code>default_split_operation(length: int) -&gt; DimensionIntervals</code></pre>

<details><summary>Source</summary><pre><code>def default_split_operation(length: int) -&gt; DimensionIntervals:
    return DimensionIntervals(starts=&#91;0&#93;, ends=&#91;length&#93;, left_ramps=&#91;0&#93;, right_ramps=&#91;0&#93;)</code></pre></details>
<h2 id="fastvideo.models.vaes.ltx2vae.default_mapping_operation"><code>fastvideo.models.vaes.ltx2vae.default_mapping_operation</code></h2>
<pre class="doc-signature"><code>default_mapping_operation(_intervals: DimensionIntervals) -&gt; Tuple&#91;List&#91;slice&#93;, List&#91;torch.Tensor | None&#93;&#93;</code></pre>

<details><summary>Source</summary><pre><code>def default_mapping_operation(_intervals: DimensionIntervals, ) -&gt; Tuple&#91;List&#91;slice&#93;, List&#91;torch.Tensor | None&#93;&#93;:
    return &#91;slice(0, None)&#93;, &#91;None&#93;</code></pre></details>
<h2 id="fastvideo.models.vaes.ltx2vae.split_in_spatial"><code>fastvideo.models.vaes.ltx2vae.split_in_spatial</code></h2>
<pre class="doc-signature"><code>split_in_spatial(size: int, overlap: int) -&gt; SplitOperation</code></pre>

<details><summary>Source</summary><pre><code>def split_in_spatial(size: int, overlap: int) -&gt; 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>
<h2 id="fastvideo.models.vaes.ltx2vae.split_in_temporal"><code>fastvideo.models.vaes.ltx2vae.split_in_temporal</code></h2>
<pre class="doc-signature"><code>split_in_temporal(size: int, overlap: int) -&gt; SplitOperation</code></pre>

<details><summary>Source</summary><pre><code>def split_in_temporal(size: int, overlap: int) -&gt; 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>
<h2 id="fastvideo.models.vaes.ltx2vae.map_temporal_slice"><code>fastvideo.models.vaes.ltx2vae.map_temporal_slice</code></h2>
<pre class="doc-signature"><code>map_temporal_slice(begin: int, end: int, left_ramp: int, right_ramp: int, scale: int) -&gt; Tuple&#91;slice, torch.Tensor&#93;</code></pre>

<details><summary>Source</summary><pre><code>def map_temporal_slice(begin: int, end: int, left_ramp: int, right_ramp: int, scale: int) -&gt; Tuple&#91;slice, torch.Tensor&#93;:
    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>
<h2 id="fastvideo.models.vaes.ltx2vae.map_spatial_slice"><code>fastvideo.models.vaes.ltx2vae.map_spatial_slice</code></h2>
<pre class="doc-signature"><code>map_spatial_slice(begin: int, end: int, left_ramp: int, right_ramp: int, scale: int) -&gt; Tuple&#91;slice, torch.Tensor&#93;</code></pre>

<details><summary>Source</summary><pre><code>def map_spatial_slice(begin: int, end: int, left_ramp: int, right_ramp: int, scale: int) -&gt; Tuple&#91;slice, torch.Tensor&#93;:
    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>
<h2 id="fastvideo.models.vaes.ltx2vae.to_mapping_operation"><code>fastvideo.models.vaes.ltx2vae.to_mapping_operation</code></h2>
<pre class="doc-signature"><code>to_mapping_operation(map_func: Callable&#91;&#91;int, int, int, int, int&#93;, Tuple&#91;slice, torch.Tensor&#93;&#93;, scale: int) -&gt; MappingOperation</code></pre>

<details><summary>Source</summary><pre><code>def to_mapping_operation(
    map_func: Callable&#91;&#91;int, int, int, int, int&#93;, Tuple&#91;slice, torch.Tensor&#93;&#93;,
    scale: int,
) -&gt; 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>
<h2 id="fastvideo.models.vaes.ltx2vae.create_tiles_from_intervals_and_mappers"><code>fastvideo.models.vaes.ltx2vae.create_tiles_from_intervals_and_mappers</code></h2>
<pre class="doc-signature"><code>create_tiles_from_intervals_and_mappers(intervals: LatentIntervals, mappers: List&#91;MappingOperation&#93;) -&gt; List&#91;Tile&#93;</code></pre>

<details><summary>Source</summary><pre><code>def create_tiles_from_intervals_and_mappers(
    intervals: LatentIntervals,
    mappers: List&#91;MappingOperation&#93;,
) -&gt; List&#91;Tile&#93;:
    full_dim_input_slices = &#91;&#93;
    full_dim_output_slices = &#91;&#93;
    full_dim_masks_1d = &#91;&#93;
    for axis_index in range(len(intervals.original_shape)):
        dimension_intervals = intervals.dimension_intervals&#91;axis_index&#93;
        starts = dimension_intervals.starts
        ends = dimension_intervals.ends
        input_slices = &#91;slice(s, e) for s, e in zip(starts, ends, strict=True)&#93;
        output_slices, masks_1d = mappers&#91;axis_index&#93;(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>
<h2 id="fastvideo.models.vaes.ltx2vae.create_tiles"><code>fastvideo.models.vaes.ltx2vae.create_tiles</code></h2>
<pre class="doc-signature"><code>create_tiles(latent_shape: torch.Size, splitters: List&#91;SplitOperation&#93;, mappers: List&#91;MappingOperation&#93;) -&gt; List&#91;Tile&#93;</code></pre>

<details><summary>Source</summary><pre><code>def create_tiles(
    latent_shape: torch.Size,
    splitters: List&#91;SplitOperation&#93;,
    mappers: List&#91;MappingOperation&#93;,
) -&gt; List&#91;Tile&#93;:
    if len(splitters) != len(latent_shape):
        raise ValueError(f&quot;Number of splitters must be equal to number of dimensions in latent shape, &quot;
                         f&quot;got &#123;len(splitters)&#125; and &#123;len(latent_shape)&#125;&quot;)
    if len(mappers) != len(latent_shape):
        raise ValueError(f&quot;Number of mappers must be equal to number of dimensions in latent shape, &quot;
                         f&quot;got &#123;len(mappers)&#125; and &#123;len(latent_shape)&#125;&quot;)
    intervals = &#91;splitter(length) for splitter, length in zip(splitters, latent_shape, strict=True)&#93;
    latent_intervals = LatentIntervals(original_shape=latent_shape, dimension_intervals=tuple(intervals))
    return create_tiles_from_intervals_and_mappers(latent_intervals, mappers)</code></pre></details>
<h2 id="fastvideo.models.vaes.ltx2vae.patchify"><code>fastvideo.models.vaes.ltx2vae.patchify</code></h2>
<pre class="doc-signature"><code>patchify(x: torch.Tensor, patch_size_hw: int, patch_size_t: int = 1) -&gt; torch.Tensor</code></pre>
<p>Rearrange spatial dimensions into channels (space-to-depth).</p>
<h3>Parameters</h3>
<dl>
<dt><code>x: torch.Tensor</code></dt><dd><p>Input tensor (4D or 5D)</p></dd>
<dt><code>patch_size_hw: int</code></dt><dd><p>Spatial patch size for height and width.</p></dd>
<dt><code>patch_size_t: int</code></dt><dd><p>Temporal patch size. Default=1 (no temporal patching).</p></dd>
</dl>
<p>For 5D: (B, C, F, H, W) -&gt; (B, C<em>patch_size_hw^2</em>patch_size_t, F/patch_size_t, H/patch_size_hw, W/patch_size_hw)</p>
<details><summary>Source</summary><pre><code>def patchify(x: torch.Tensor, patch_size_hw: int, patch_size_t: int = 1) -&gt; torch.Tensor:
    &quot;&quot;&quot;
    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>
<h2 id="fastvideo.models.vaes.ltx2vae.unpatchify"><code>fastvideo.models.vaes.ltx2vae.unpatchify</code></h2>
<pre class="doc-signature"><code>unpatchify(x: torch.Tensor, patch_size_hw: int, patch_size_t: int = 1) -&gt; torch.Tensor</code></pre>
<p>Rearrange channels back into spatial dimensions (depth-to-space).</p>
<h3>Parameters</h3>
<dl>
<dt><code>x: torch.Tensor</code></dt><dd><p>Input tensor (4D or 5D)</p></dd>
<dt><code>patch_size_hw: int</code></dt><dd><p>Spatial patch size for height and width.</p></dd>
<dt><code>patch_size_t: int</code></dt><dd><p>Temporal patch size. Default=1 (no temporal expansion).</p></dd>
</dl>
<p>For 5D: (B, C<em>patch_size_hw^2</em>patch_size_t, F, H, W) -&gt; (B, C, F<em>patch_size_t, H</em>patch_size_hw, W*patch_size_hw)</p>
<details><summary>Source</summary><pre><code>def unpatchify(x: torch.Tensor, patch_size_hw: int, patch_size_t: int = 1) -&gt; torch.Tensor:
    &quot;&quot;&quot;
    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>
<h2 id="fastvideo.models.vaes.ltx2vae.PixelNorm"><code>fastvideo.models.vaes.ltx2vae.PixelNorm</code></h2>
<pre class="doc-signature"><code>PixelNorm(dim: int = 1, eps: float = 1e-08)</code></pre>
<p>Per-pixel (per-location) RMS normalization layer.
Normalizes along the channel dimension using root-mean-square.</p>

<details><summary>Source</summary><pre><code>class PixelNorm(nn.Module):
    &quot;&quot;&quot;
    Per-pixel (per-location) RMS normalization layer.
    Normalizes along the channel dimension using root-mean-square.
    &quot;&quot;&quot;

    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>
<h3 id="fastvideo.models.vaes.ltx2vae.PixelNorm.forward"><code>fastvideo.models.vaes.ltx2vae.PixelNorm.forward</code></h3>
<pre class="doc-signature"><code>forward(x: torch.Tensor) -&gt; torch.Tensor</code></pre>

<details><summary>Source</summary><pre><code>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>
<h2 id="fastvideo.models.vaes.ltx2vae.PerChannelStatistics"><code>fastvideo.models.vaes.ltx2vae.PerChannelStatistics</code></h2>
<pre class="doc-signature"><code>PerChannelStatistics(latent_channels: int = 128)</code></pre>
<p>Per-channel statistics for normalizing and denormalizing the latent representation.
Statistics are computed over the dataset and stored in the model checkpoint.</p>

<details><summary>Source</summary><pre><code>class PerChannelStatistics(nn.Module):
    &quot;&quot;&quot;
    Per-channel statistics for normalizing and denormalizing the latent representation.
    Statistics are computed over the dataset and stored in the model checkpoint.
    &quot;&quot;&quot;

    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>
<h3 id="fastvideo.models.vaes.ltx2vae.PerChannelStatistics.un_normalize"><code>fastvideo.models.vaes.ltx2vae.PerChannelStatistics.un_normalize</code></h3>
<pre class="doc-signature"><code>un_normalize(x: torch.Tensor) -&gt; torch.Tensor</code></pre>

<details><summary>Source</summary><pre><code>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</code></pre></details>
<h3 id="fastvideo.models.vaes.ltx2vae.PerChannelStatistics.normalize"><code>fastvideo.models.vaes.ltx2vae.PerChannelStatistics.normalize</code></h3>
<pre class="doc-signature"><code>normalize(x: torch.Tensor) -&gt; torch.Tensor</code></pre>

<details><summary>Source</summary><pre><code>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>
<h2 id="fastvideo.models.vaes.ltx2vae.CausalConv3d"><code>fastvideo.models.vaes.ltx2vae.CausalConv3d</code></h2>
<pre class="doc-signature"><code>CausalConv3d(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)</code></pre>
<p>Causal 3D convolution that pads temporally by repeating the first frame.</p>

<details><summary>Source</summary><pre><code>class CausalConv3d(nn.Module):
    &quot;&quot;&quot;
    Causal 3D convolution that pads temporally by repeating the first frame.
    &quot;&quot;&quot;

    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>
<h3 id="fastvideo.models.vaes.ltx2vae.CausalConv3d.forward"><code>fastvideo.models.vaes.ltx2vae.CausalConv3d.forward</code></h3>
<pre class="doc-signature"><code>forward(x: torch.Tensor, causal: bool = True) -&gt; torch.Tensor</code></pre>

<details><summary>Source</summary><pre><code>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</code></pre></details>
<h2 id="fastvideo.models.vaes.ltx2vae.make_conv_nd"><code>fastvideo.models.vaes.ltx2vae.make_conv_nd</code></h2>
<pre class="doc-signature"><code>make_conv_nd(dims: int, in_channels: int, out_channels: int, kernel_size: int, stride: int | Tuple&#91;int, int, int&#93; = 1, padding: int = 0, dilation: int = 1, groups: int = 1, bias: bool = True, causal: bool = False, spatial_padding_mode: PaddingModeType = PaddingModeType.ZEROS) -&gt; nn.Module</code></pre>
<p>Create a convolution layer (2D or 3D, causal or not).</p>
<details><summary>Source</summary><pre><code>def make_conv_nd(
    dims: int,
    in_channels: int,
    out_channels: int,
    kernel_size: int,
    stride: int | Tuple&#91;int, int, int&#93; = 1,
    padding: int = 0,
    dilation: int = 1,
    groups: int = 1,
    bias: bool = True,
    causal: bool = False,
    spatial_padding_mode: PaddingModeType = PaddingModeType.ZEROS,
) -&gt; nn.Module:
    &quot;&quot;&quot;Create a convolution layer (2D or 3D, causal or not).&quot;&quot;&quot;
    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&quot;unsupported dimensions: &#123;dims&#125;&quot;)</code></pre></details>
<h2 id="fastvideo.models.vaes.ltx2vae.make_linear_nd"><code>fastvideo.models.vaes.ltx2vae.make_linear_nd</code></h2>
<pre class="doc-signature"><code>make_linear_nd(dims: int, in_channels: int, out_channels: int, bias: bool = True) -&gt; nn.Module</code></pre>
<p>Create a 1x1 convolution (pointwise linear).</p>
<details><summary>Source</summary><pre><code>def make_linear_nd(
    dims: int,
    in_channels: int,
    out_channels: int,
    bias: bool = True,
) -&gt; nn.Module:
    &quot;&quot;&quot;Create a 1x1 convolution (pointwise linear).&quot;&quot;&quot;
    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&quot;unsupported dimensions: &#123;dims&#125;&quot;)</code></pre></details>
<h2 id="fastvideo.models.vaes.ltx2vae.ResnetBlock3D"><code>fastvideo.models.vaes.ltx2vae.ResnetBlock3D</code></h2>
<pre class="doc-signature"><code>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)</code></pre>
<p>A 3D ResNet block with optional timestep conditioning and noise injection.</p>

<details><summary>Source</summary><pre><code>class ResnetBlock3D(nn.Module):
    &quot;&quot;&quot;A 3D ResNet block with optional timestep conditioning and noise injection.&quot;&quot;&quot;

    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>
<h3 id="fastvideo.models.vaes.ltx2vae.ResnetBlock3D.forward"><code>fastvideo.models.vaes.ltx2vae.ResnetBlock3D.forward</code></h3>
<pre class="doc-signature"><code>forward(input_tensor: torch.Tensor, causal: bool = True, timestep: torch.Tensor | None = None, generator: torch.Generator | None = None) -&gt; torch.Tensor</code></pre>

<details><summary>Source</summary><pre><code>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>
<h2 id="fastvideo.models.vaes.ltx2vae.UNetMidBlock3D"><code>fastvideo.models.vaes.ltx2vae.UNetMidBlock3D</code></h2>
<pre class="doc-signature"><code>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)</code></pre>
<p>A 3D UNet mid-block with multiple residual blocks.</p>

<details><summary>Source</summary><pre><code>class UNetMidBlock3D(nn.Module):
    &quot;&quot;&quot;A 3D UNet mid-block with multiple residual blocks.&quot;&quot;&quot;

    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>
<h3 id="fastvideo.models.vaes.ltx2vae.UNetMidBlock3D.forward"><code>fastvideo.models.vaes.ltx2vae.UNetMidBlock3D.forward</code></h3>
<pre class="doc-signature"><code>forward(hidden_states: torch.Tensor, causal: bool = True, timestep: torch.Tensor | None = None, generator: torch.Generator | None = None) -&gt; torch.Tensor</code></pre>

<details><summary>Source</summary><pre><code>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>
<h2 id="fastvideo.models.vaes.ltx2vae.PixArtAlphaCombinedTimestepSizeEmbeddings"><code>fastvideo.models.vaes.ltx2vae.PixArtAlphaCombinedTimestepSizeEmbeddings</code></h2>
<pre class="doc-signature"><code>PixArtAlphaCombinedTimestepSizeEmbeddings(embedding_dim: int, size_emb_dim: int = 0)</code></pre>
<p>Timestep embeddings for decoder conditioning.</p>

<details><summary>Source</summary><pre><code>class PixArtAlphaCombinedTimestepSizeEmbeddings(nn.Module):
    &quot;&quot;&quot;Timestep embeddings for decoder conditioning.&quot;&quot;&quot;

    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>
<h3 id="fastvideo.models.vaes.ltx2vae.PixArtAlphaCombinedTimestepSizeEmbeddings.forward"><code>fastvideo.models.vaes.ltx2vae.PixArtAlphaCombinedTimestepSizeEmbeddings.forward</code></h3>
<pre class="doc-signature"><code>forward(timestep: torch.Tensor, hidden_dtype: torch.dtype) -&gt; torch.Tensor</code></pre>

<details><summary>Source</summary><pre><code>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>
<h2 id="fastvideo.models.vaes.ltx2vae.Timesteps"><code>fastvideo.models.vaes.ltx2vae.Timesteps</code></h2>
<pre class="doc-signature"><code>Timesteps(num_channels: int, flip_sin_to_cos: bool = True, downscale_freq_shift: float = 0)</code></pre>
<p>Sinusoidal timestep embeddings.</p>

<details><summary>Source</summary><pre><code>class Timesteps(nn.Module):
    &quot;&quot;&quot;Sinusoidal timestep embeddings.&quot;&quot;&quot;

    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>
<h3 id="fastvideo.models.vaes.ltx2vae.Timesteps.forward"><code>fastvideo.models.vaes.ltx2vae.Timesteps.forward</code></h3>
<pre class="doc-signature"><code>forward(timesteps: torch.Tensor) -&gt; torch.Tensor</code></pre>

<details><summary>Source</summary><pre><code>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>
<h2 id="fastvideo.models.vaes.ltx2vae.TimestepEmbedding"><code>fastvideo.models.vaes.ltx2vae.TimestepEmbedding</code></h2>
<pre class="doc-signature"><code>TimestepEmbedding(in_channels: int, time_embed_dim: int)</code></pre>
<p>MLP for timestep embeddings.</p>

<details><summary>Source</summary><pre><code>class TimestepEmbedding(nn.Module):
    &quot;&quot;&quot;MLP for timestep embeddings.&quot;&quot;&quot;

    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>
<h3 id="fastvideo.models.vaes.ltx2vae.TimestepEmbedding.forward"><code>fastvideo.models.vaes.ltx2vae.TimestepEmbedding.forward</code></h3>
<pre class="doc-signature"><code>forward(sample: torch.Tensor) -&gt; torch.Tensor</code></pre>

<details><summary>Source</summary><pre><code>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>
<h2 id="fastvideo.models.vaes.ltx2vae.SpaceToDepthDownsample"><code>fastvideo.models.vaes.ltx2vae.SpaceToDepthDownsample</code></h2>
<pre class="doc-signature"><code>SpaceToDepthDownsample(dims: int, in_channels: int, out_channels: int, stride: Tuple&#91;int, int, int&#93;, spatial_padding_mode: PaddingModeType = PaddingModeType.ZEROS)</code></pre>
<p>Downsampling via space-to-depth with residual connection.</p>

<details><summary>Source</summary><pre><code>class SpaceToDepthDownsample(nn.Module):
    &quot;&quot;&quot;Downsampling via space-to-depth with residual connection.&quot;&quot;&quot;

    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>
<h3 id="fastvideo.models.vaes.ltx2vae.SpaceToDepthDownsample.forward"><code>fastvideo.models.vaes.ltx2vae.SpaceToDepthDownsample.forward</code></h3>
<pre class="doc-signature"><code>forward(x: torch.Tensor, causal: bool = True) -&gt; torch.Tensor</code></pre>

<details><summary>Source</summary><pre><code>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>
<h2 id="fastvideo.models.vaes.ltx2vae.DepthToSpaceUpsample"><code>fastvideo.models.vaes.ltx2vae.DepthToSpaceUpsample</code></h2>
<pre class="doc-signature"><code>DepthToSpaceUpsample(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)</code></pre>
<p>Upsampling via depth-to-space (pixel shuffle).</p>

<details><summary>Source</summary><pre><code>class DepthToSpaceUpsample(nn.Module):
    &quot;&quot;&quot;Upsampling via depth-to-space (pixel shuffle).&quot;&quot;&quot;

    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>
<h3 id="fastvideo.models.vaes.ltx2vae.DepthToSpaceUpsample.forward"><code>fastvideo.models.vaes.ltx2vae.DepthToSpaceUpsample.forward</code></h3>
<pre class="doc-signature"><code>forward(x: torch.Tensor, causal: bool = True) -&gt; torch.Tensor</code></pre>

<details><summary>Source</summary><pre><code>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>
<h2 id="fastvideo.models.vaes.ltx2vae.VideoEncoder"><code>fastvideo.models.vaes.ltx2vae.VideoEncoder</code></h2>
<pre class="doc-signature"><code>VideoEncoder(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)</code></pre>
<p>LTX-2 Video Encoder. Encodes video frames into a latent representation.</p>

<details><summary>Source</summary><pre><code>class VideoEncoder(nn.Module):
    &quot;&quot;&quot;
    LTX-2 Video Encoder. Encodes video frames into a latent representation.
    &quot;&quot;&quot;

    _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>
<h3 id="fastvideo.models.vaes.ltx2vae.VideoEncoder.forward"><code>fastvideo.models.vaes.ltx2vae.VideoEncoder.forward</code></h3>
<pre class="doc-signature"><code>forward(sample: torch.Tensor) -&gt; torch.Tensor</code></pre>

<details><summary>Source</summary><pre><code>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>
<h2 id="fastvideo.models.vaes.ltx2vae.VideoDecoder"><code>fastvideo.models.vaes.ltx2vae.VideoDecoder</code></h2>
<pre class="doc-signature"><code>VideoDecoder(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)</code></pre>
<p>LTX-2 Video Decoder. Decodes latent representation into video frames.</p>

<details><summary>Source</summary><pre><code>class VideoDecoder(nn.Module):
    &quot;&quot;&quot;
    LTX-2 Video Decoder. Decodes latent representation into video frames.
    &quot;&quot;&quot;

    _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>
<h3 id="fastvideo.models.vaes.ltx2vae.VideoDecoder.forward"><code>fastvideo.models.vaes.ltx2vae.VideoDecoder.forward</code></h3>
<pre class="doc-signature"><code>forward(sample: torch.Tensor, timestep: torch.Tensor | None = None, generator: torch.Generator | None = None) -&gt; torch.Tensor</code></pre>

<details><summary>Source</summary><pre><code>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>
<h2 id="fastvideo.models.vaes.ltx2vae.VideoEncoderConfigurator"><code>fastvideo.models.vaes.ltx2vae.VideoEncoderConfigurator</code></h2>
<pre class="doc-signature"><code>VideoEncoderConfigurator()</code></pre>
<p>Configurator for creating a video VAE Encoder from a configuration dictionary.</p>
<details><summary>Source</summary><pre><code>class VideoEncoderConfigurator:
    &quot;&quot;&quot;Configurator for creating a video VAE Encoder from a configuration dictionary.&quot;&quot;&quot;

    @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>
<h3 id="fastvideo.models.vaes.ltx2vae.VideoEncoderConfigurator.from_config"><code>fastvideo.models.vaes.ltx2vae.VideoEncoderConfigurator.from_config</code></h3>
<pre class="doc-signature"><code>from_config(config: dict) -&gt; VideoEncoder</code></pre>

<details><summary>Source</summary><pre><code>@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>
<h2 id="fastvideo.models.vaes.ltx2vae.VideoDecoderConfigurator"><code>fastvideo.models.vaes.ltx2vae.VideoDecoderConfigurator</code></h2>
<pre class="doc-signature"><code>VideoDecoderConfigurator()</code></pre>
<p>Configurator for creating a video VAE Decoder from a configuration dictionary.</p>
<details><summary>Source</summary><pre><code>class VideoDecoderConfigurator:
    &quot;&quot;&quot;Configurator for creating a video VAE Decoder from a configuration dictionary.&quot;&quot;&quot;

    @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>
<h3 id="fastvideo.models.vaes.ltx2vae.VideoDecoderConfigurator.from_config"><code>fastvideo.models.vaes.ltx2vae.VideoDecoderConfigurator.from_config</code></h3>
<pre class="doc-signature"><code>from_config(config: dict) -&gt; VideoDecoder</code></pre>

<details><summary>Source</summary><pre><code>@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>
<h2 id="fastvideo.models.vaes.ltx2vae.LTX2VideoEncoder"><code>fastvideo.models.vaes.ltx2vae.LTX2VideoEncoder</code></h2>
<pre class="doc-signature"><code>LTX2VideoEncoder(config: dict&#91;str, Any&#93;)</code></pre>
<p>LTX-2 Video Encoder wrapper for FastVideo compatibility.</p>

<details><summary>Source</summary><pre><code>class LTX2VideoEncoder(nn.Module):
    &quot;&quot;&quot;LTX-2 Video Encoder wrapper for FastVideo compatibility.&quot;&quot;&quot;

    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>
<h3 id="fastvideo.models.vaes.ltx2vae.LTX2VideoEncoder.forward"><code>fastvideo.models.vaes.ltx2vae.LTX2VideoEncoder.forward</code></h3>
<pre class="doc-signature"><code>forward(sample: torch.Tensor) -&gt; torch.Tensor</code></pre>

<details><summary>Source</summary><pre><code>def forward(self, sample: torch.Tensor) -&gt; torch.Tensor:
    return self.model(sample)</code></pre></details>
<h2 id="fastvideo.models.vaes.ltx2vae.LTX2VideoDecoder"><code>fastvideo.models.vaes.ltx2vae.LTX2VideoDecoder</code></h2>
<pre class="doc-signature"><code>LTX2VideoDecoder(config: dict&#91;str, Any&#93;)</code></pre>
<p>LTX-2 Video Decoder wrapper for FastVideo compatibility.</p>

<details><summary>Source</summary><pre><code>class LTX2VideoDecoder(nn.Module):
    &quot;&quot;&quot;LTX-2 Video Decoder wrapper for FastVideo compatibility.&quot;&quot;&quot;

    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>
<h3 id="fastvideo.models.vaes.ltx2vae.LTX2VideoDecoder.forward"><code>fastvideo.models.vaes.ltx2vae.LTX2VideoDecoder.forward</code></h3>
<pre class="doc-signature"><code>forward(sample: torch.Tensor, timestep: torch.Tensor | None = None, generator: torch.Generator | None = None) -&gt; torch.Tensor</code></pre>

<details><summary>Source</summary><pre><code>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>
<h2 id="fastvideo.models.vaes.ltx2vae.LTX2CausalVideoAutoencoder"><code>fastvideo.models.vaes.ltx2vae.LTX2CausalVideoAutoencoder</code></h2>
<pre class="doc-signature"><code>LTX2CausalVideoAutoencoder(config: dict&#91;str, Any&#93;)</code></pre>
<p>LTX-2 VAE that exposes FastVideo's VAE encode/decode interface.
Supports tiled decoding to reduce memory usage for high-resolution videos.</p>

<details><summary>Source</summary><pre><code>class LTX2CausalVideoAutoencoder(nn.Module):
    &quot;&quot;&quot;
    LTX-2 VAE that exposes FastVideo&#x27;s VAE encode/decode interface.
    Supports tiled decoding to reduce memory usage for high-resolution videos.
    &quot;&quot;&quot;

    # 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>
<h3 id="fastvideo.models.vaes.ltx2vae.LTX2CausalVideoAutoencoder.enable_channels_last_3d"><code>fastvideo.models.vaes.ltx2vae.LTX2CausalVideoAutoencoder.enable_channels_last_3d</code></h3>
<pre class="doc-signature"><code>enable_channels_last_3d() -&gt; None</code></pre>
<p>Enable channels-last layout for 3D VAE convolutions.</p>
<details><summary>Source</summary><pre><code>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)</code></pre></details>
<h3 id="fastvideo.models.vaes.ltx2vae.LTX2CausalVideoAutoencoder.disable_channels_last_3d"><code>fastvideo.models.vaes.ltx2vae.LTX2CausalVideoAutoencoder.disable_channels_last_3d</code></h3>
<pre class="doc-signature"><code>disable_channels_last_3d() -&gt; None</code></pre>
<p>Restore contiguous layout for 3D VAE convolutions.</p>
<details><summary>Source</summary><pre><code>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)</code></pre></details>
<h3 id="fastvideo.models.vaes.ltx2vae.LTX2CausalVideoAutoencoder.encode"><code>fastvideo.models.vaes.ltx2vae.LTX2CausalVideoAutoencoder.encode</code></h3>
<pre class="doc-signature"><code>encode(x: torch.Tensor) -&gt; DiagonalGaussianDistribution</code></pre>

<details><summary>Source</summary><pre><code>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)</code></pre></details>
<h3 id="fastvideo.models.vaes.ltx2vae.LTX2CausalVideoAutoencoder.decode"><code>fastvideo.models.vaes.ltx2vae.LTX2CausalVideoAutoencoder.decode</code></h3>
<pre class="doc-signature"><code>decode(z: torch.Tensor, timestep: torch.Tensor | None = None, generator: torch.Generator | None = None) -&gt; torch.Tensor</code></pre>
<p>Decode latents to video, using tiling if enabled.</p>
<details><summary>Source</summary><pre><code>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)</code></pre></details>
<h3 id="fastvideo.models.vaes.ltx2vae.LTX2CausalVideoAutoencoder.enable_tiling"><code>fastvideo.models.vaes.ltx2vae.LTX2CausalVideoAutoencoder.enable_tiling</code></h3>
<pre class="doc-signature"><code>enable_tiling() -&gt; None</code></pre>
<p>Enable tiled decoding with default configuration.</p>
<details><summary>Source</summary><pre><code>def enable_tiling(self) -&gt; None:
    &quot;&quot;&quot;Enable tiled decoding with default configuration.&quot;&quot;&quot;
    self._use_tiling = True</code></pre></details>
<h3 id="fastvideo.models.vaes.ltx2vae.LTX2CausalVideoAutoencoder.disable_tiling"><code>fastvideo.models.vaes.ltx2vae.LTX2CausalVideoAutoencoder.disable_tiling</code></h3>
<pre class="doc-signature"><code>disable_tiling() -&gt; None</code></pre>
<p>Disable tiled decoding.</p>
<details><summary>Source</summary><pre><code>def disable_tiling(self) -&gt; None:
    &quot;&quot;&quot;Disable tiled decoding.&quot;&quot;&quot;
    self._use_tiling = False</code></pre></details>
<h3 id="fastvideo.models.vaes.ltx2vae.LTX2CausalVideoAutoencoder.tiled_decode"><code>fastvideo.models.vaes.ltx2vae.LTX2CausalVideoAutoencoder.tiled_decode</code></h3>
<pre class="doc-signature"><code>tiled_decode(latent: torch.Tensor, tiling_config: TilingConfig | None = None, timestep: torch.Tensor | None = None, generator: torch.Generator | None = None) -&gt; Iterator&#91;torch.Tensor&#93;</code></pre>
<p>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.</p>
<h4>Parameters</h4>
<dl>
<dt><code>latent: torch.Tensor</code></dt><dd><p>Input latent tensor (B, C, F', H', W').</p></dd>
<dt><code>tiling_config: TilingConfig | None</code></dt><dd><p>Tiling configuration for the latent tensor.</p></dd>
<dt><code>timestep: torch.Tensor | None</code></dt><dd><p>Optional timestep for decoder conditioning.</p></dd>
<dt><code>generator: torch.Generator | None</code></dt><dd><p>Optional random generator for deterministic decoding.</p></dd>
</dl>
<h4>Yields</h4>
<dl>
<dt><code>torch.Tensor</code></dt><dd><p>Video chunks (B, C, T, H, W) by temporal slices.</p></dd>
</dl>
<details><summary>Source</summary><pre><code>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>
</div>
