---
title: cosmos25wanvae
---

<div class="python-reference">
<a id="fastvideo.models.vaes.cosmos25wanvae-classes"></a>
<a id="fastvideo.models.vaes.cosmos25wanvae.Cosmos25WanVAE-functions"></a>
<a id="fastvideo.models.vaes.cosmos25wanvae.Cosmos25WanVAEAdapter-functions"></a>
<a id="fastvideo.models.vaes.cosmos25wanvae"></a>
<p>Cosmos 2.5 / Wan2.1 VAE adapter.</p>
<p>Why this exists:
- Cosmos2.5 uses a Wan2.1-style VAE, but the <em>diffusion model</em> operates in a
  <strong>normalized latent space</strong>:
    z_norm = (z - mean) / std</p>
<p>Meanwhile, FastVideo's <code>AutoencoderKLWan</code> operates in the VAE's native latent
  space (denormalized):
    z = z_norm * std + mean</p>
<p>This adapter provides a single, stable interface for FastVideo pipelines:
- <code>encode(x)</code> returns an object with <code>.mean</code> / <code>.sample()</code> / <code>.mode()</code>
- <code>decode(z)</code> returns a tensor in pixel space</p>
<p>It also exposes flags used by pipeline stages to avoid double (de)normalization:
- <code>handles_latent_norm = True</code>   -&gt; stages should NOT normalize encoder latents
- <code>handles_latent_denorm = True</code> -&gt; stages should NOT denormalize before decode</p>
<h2 id="fastvideo.models.vaes.cosmos25wanvae.Cosmos25WanVAEAdapter"><code>fastvideo.models.vaes.cosmos25wanvae.Cosmos25WanVAEAdapter</code></h2>
<pre class="doc-signature"><code>Cosmos25WanVAEAdapter(inner: Any, *, latents_mean: Optional&#91;torch.Tensor&#93; = None, latents_std: Optional&#91;torch.Tensor&#93; = None)</code></pre>
<p>Adapter that makes a Wan2.1-style VAE follow Cosmos2.5's latent contract:
- <code>encode()</code> returns <strong>normalized</strong> latents
- <code>decode()</code> expects <strong>normalized</strong> latents</p>

<details><summary>Source</summary><pre><code>class Cosmos25WanVAEAdapter(nn.Module):
    &quot;&quot;&quot;
    Adapter that makes a Wan2.1-style VAE follow Cosmos2.5&#x27;s latent contract:
    - `encode()` returns **normalized** latents
    - `decode()` expects **normalized** latents
    &quot;&quot;&quot;

    # Pipeline stage hints (see latent_preparation.py / decoding.py / image_encoding.py)
    handles_latent_norm: bool = True
    handles_latent_denorm: bool = True
    latent_norm_mode: str = &quot;internal&quot;  # informational

    def __init__(
        self,
        inner: Any,
        *,
        latents_mean: Optional&#91;torch.Tensor&#93; = None,
        latents_std: Optional&#91;torch.Tensor&#93; = None,
    ) -&gt; None:
        super().__init__()
        self.inner = inner

        # Preserve `config` when available; some pipeline utilities expect it.
        self.config = getattr(inner, &quot;config&quot;, None)

        # If not provided, try to derive from `config.latents_mean/std`.
        cfg = self.config
        if latents_mean is None and cfg is not None and hasattr(cfg, &quot;latents_mean&quot;):
            latents_mean = torch.tensor(cfg.latents_mean, dtype=torch.float32).view(1, -1, 1, 1, 1)
        if latents_std is None and cfg is not None and hasattr(cfg, &quot;latents_std&quot;):
            latents_std = torch.tensor(cfg.latents_std, dtype=torch.float32).view(1, -1, 1, 1, 1)

        if latents_mean is None or latents_std is None:
            raise RuntimeError(
                &quot;Cosmos25WanVAEAdapter requires latents_mean/latents_std (either passed explicitly or available on inner.config).&quot;
            )

        # Register as buffers so `.to(...)` moves them with the module.
        self.register_buffer(&quot;_latents_mean&quot;, latents_mean, persistent=False)
        self.register_buffer(&quot;_latents_std&quot;, latents_std, persistent=False)

    def _to_latent_stats(self, like: torch.Tensor) -&gt; tuple&#91;torch.Tensor, torch.Tensor&#93;:
        mean = self._latents_mean.to(device=like.device, dtype=like.dtype)
        std = self._latents_std.to(device=like.device, dtype=like.dtype)
        return mean, std

    def get_latent_num_frames(self, num_pixel_frames: int) -&gt; int:
        # Keep parity with official interface.
        if hasattr(self.inner, &quot;get_latent_num_frames&quot;):
            return int(self.inner.get_latent_num_frames(num_pixel_frames))
        return 1 + (num_pixel_frames - 1) // 4

    def encode(self, x: torch.Tensor) -&gt; _TensorLatentDist:
        &quot;&quot;&quot;
        Returns *normalized* latents (Cosmos contract).
        &quot;&quot;&quot;
        enc_out = self.inner.encode(x)

        # Support common encoder output shapes:
        # - DiagonalGaussianDistribution (FastVideo VAE): has `.mean` / `.sample()` / `.mode()`
        # - diffusers EncoderOutput: has `.latent_dist`
        # - raw tensor
        if hasattr(enc_out, &quot;latent_dist&quot;):
            dist = enc_out.latent_dist
            z_mean = dist.mode() if hasattr(dist, &quot;mode&quot;) else dist.mean
        elif hasattr(enc_out, &quot;mode&quot;) and hasattr(enc_out, &quot;mean&quot;):
            z_mean = enc_out.mode()
        elif isinstance(enc_out, torch.Tensor):
            z_mean = enc_out
        else:
            attrs = &#91;a for a in dir(enc_out) if not a.startswith(&quot;_&quot;)&#93;
            raise RuntimeError(f&quot;Unsupported VAE encoder output type: &#123;type(enc_out)&#125;. attrs=&#123;attrs&#125;&quot;)

        mean, std = self._to_latent_stats(z_mean)
        z_norm = (z_mean - mean) / std
        return _TensorLatentDist(z_norm)

    def decode(self, z: torch.Tensor) -&gt; torch.Tensor:
        &quot;&quot;&quot;
        Expects *normalized* latents (Cosmos contract).
        &quot;&quot;&quot;
        mean, std = self._to_latent_stats(z)
        z_denorm = z * std + mean
        out = self.inner.decode(z_denorm)
        return out.sample if hasattr(out, &quot;sample&quot;) else out</code></pre></details>
<h3 id="fastvideo.models.vaes.cosmos25wanvae.Cosmos25WanVAEAdapter.get_latent_num_frames"><code>fastvideo.models.vaes.cosmos25wanvae.Cosmos25WanVAEAdapter.get_latent_num_frames</code></h3>
<pre class="doc-signature"><code>get_latent_num_frames(num_pixel_frames: int) -&gt; int</code></pre>

<details><summary>Source</summary><pre><code>def get_latent_num_frames(self, num_pixel_frames: int) -&gt; int:
    # Keep parity with official interface.
    if hasattr(self.inner, &quot;get_latent_num_frames&quot;):
        return int(self.inner.get_latent_num_frames(num_pixel_frames))
    return 1 + (num_pixel_frames - 1) // 4</code></pre></details>
<h3 id="fastvideo.models.vaes.cosmos25wanvae.Cosmos25WanVAEAdapter.encode"><code>fastvideo.models.vaes.cosmos25wanvae.Cosmos25WanVAEAdapter.encode</code></h3>
<pre class="doc-signature"><code>encode(x: torch.Tensor) -&gt; _TensorLatentDist</code></pre>
<p>Returns <em>normalized</em> latents (Cosmos contract).</p>
<details><summary>Source</summary><pre><code>def encode(self, x: torch.Tensor) -&gt; _TensorLatentDist:
    &quot;&quot;&quot;
    Returns *normalized* latents (Cosmos contract).
    &quot;&quot;&quot;
    enc_out = self.inner.encode(x)

    # Support common encoder output shapes:
    # - DiagonalGaussianDistribution (FastVideo VAE): has `.mean` / `.sample()` / `.mode()`
    # - diffusers EncoderOutput: has `.latent_dist`
    # - raw tensor
    if hasattr(enc_out, &quot;latent_dist&quot;):
        dist = enc_out.latent_dist
        z_mean = dist.mode() if hasattr(dist, &quot;mode&quot;) else dist.mean
    elif hasattr(enc_out, &quot;mode&quot;) and hasattr(enc_out, &quot;mean&quot;):
        z_mean = enc_out.mode()
    elif isinstance(enc_out, torch.Tensor):
        z_mean = enc_out
    else:
        attrs = &#91;a for a in dir(enc_out) if not a.startswith(&quot;_&quot;)&#93;
        raise RuntimeError(f&quot;Unsupported VAE encoder output type: &#123;type(enc_out)&#125;. attrs=&#123;attrs&#125;&quot;)

    mean, std = self._to_latent_stats(z_mean)
    z_norm = (z_mean - mean) / std
    return _TensorLatentDist(z_norm)</code></pre></details>
<h3 id="fastvideo.models.vaes.cosmos25wanvae.Cosmos25WanVAEAdapter.decode"><code>fastvideo.models.vaes.cosmos25wanvae.Cosmos25WanVAEAdapter.decode</code></h3>
<pre class="doc-signature"><code>decode(z: torch.Tensor) -&gt; torch.Tensor</code></pre>
<p>Expects <em>normalized</em> latents (Cosmos contract).</p>
<details><summary>Source</summary><pre><code>def decode(self, z: torch.Tensor) -&gt; torch.Tensor:
    &quot;&quot;&quot;
    Expects *normalized* latents (Cosmos contract).
    &quot;&quot;&quot;
    mean, std = self._to_latent_stats(z)
    z_denorm = z * std + mean
    out = self.inner.decode(z_denorm)
    return out.sample if hasattr(out, &quot;sample&quot;) else out</code></pre></details>
<h2 id="fastvideo.models.vaes.cosmos25wanvae.Cosmos25CausalConv3d"><code>fastvideo.models.vaes.cosmos25wanvae.Cosmos25CausalConv3d</code></h2>
<pre class="doc-signature"><code>Cosmos25CausalConv3d(*args: Any, **kwargs: Any)</code></pre>
<p>Official-like causal 3D convolution.</p>
<p>Matches <code>CausalConv3d</code> in the official tokenizer: uses explicit F.pad and
supports a <code>cache_x</code> prefix for causal chunking.</p>

<details><summary>Source</summary><pre><code>class Cosmos25CausalConv3d(nn.Conv3d):
    &quot;&quot;&quot;
    Official-like causal 3D convolution.

    Matches `CausalConv3d` in the official tokenizer: uses explicit F.pad and
    supports a `cache_x` prefix for causal chunking.
    &quot;&quot;&quot;

    def __init__(self, *args: Any, **kwargs: Any) -&gt; None:
        super().__init__(*args, **kwargs)
        # padding order for F.pad: (W_left, W_right, H_left, H_right, T_left, T_right)
        self._padding: tuple&#91;int, ...&#93; = (
            self.padding&#91;2&#93;,
            self.padding&#91;2&#93;,
            self.padding&#91;1&#93;,
            self.padding&#91;1&#93;,
            2 * self.padding&#91;0&#93;,
            0,
        )
        self.padding = (0, 0, 0)

    def forward(self, x: torch.Tensor, cache_x: torch.Tensor | None = None) -&gt; torch.Tensor:
        padding = list(self._padding)
        if cache_x is not None and self._padding&#91;4&#93; &gt; 0:
            cache_x = cache_x.to(x.device)
            x = torch.cat(&#91;cache_x, x&#93;, dim=2)
            padding&#91;4&#93; -= cache_x.shape&#91;2&#93;
        x = F.pad(x, padding)
        return super().forward(x)</code></pre></details>
<h3 id="fastvideo.models.vaes.cosmos25wanvae.Cosmos25CausalConv3d.forward"><code>fastvideo.models.vaes.cosmos25wanvae.Cosmos25CausalConv3d.forward</code></h3>
<pre class="doc-signature"><code>forward(x: torch.Tensor, cache_x: torch.Tensor | None = None) -&gt; torch.Tensor</code></pre>

<details><summary>Source</summary><pre><code>def forward(self, x: torch.Tensor, cache_x: torch.Tensor | None = None) -&gt; torch.Tensor:
    padding = list(self._padding)
    if cache_x is not None and self._padding&#91;4&#93; &gt; 0:
        cache_x = cache_x.to(x.device)
        x = torch.cat(&#91;cache_x, x&#93;, dim=2)
        padding&#91;4&#93; -= cache_x.shape&#91;2&#93;
    x = F.pad(x, padding)
    return super().forward(x)</code></pre></details>
<h2 id="fastvideo.models.vaes.cosmos25wanvae.Cosmos25RMSNorm"><code>fastvideo.models.vaes.cosmos25wanvae.Cosmos25RMSNorm</code></h2>
<pre class="doc-signature"><code>Cosmos25RMSNorm(dim: int, channel_first: bool = True, images: bool = True, bias: bool = False)</code></pre>
<p>Official-like RMS_norm (uses learnable gamma and optional bias).</p>

<details><summary>Source</summary><pre><code>class Cosmos25RMSNorm(nn.Module):
    &quot;&quot;&quot;Official-like RMS_norm (uses learnable gamma and optional bias).&quot;&quot;&quot;

    def __init__(self, dim: int, channel_first: bool = True, images: bool = True, bias: bool = False) -&gt; None:
        super().__init__()
        broadcastable_dims = (1, 1, 1) if not images else (1, 1)
        shape = (dim, *broadcastable_dims) if channel_first else (dim, )

        self.channel_first = channel_first
        self.scale = dim**0.5
        self.gamma = nn.Parameter(torch.ones(shape))
        self.bias = nn.Parameter(torch.zeros(shape)) if bias else 0.0

    def forward(self, x: torch.Tensor) -&gt; torch.Tensor:
        dim = 1 if self.channel_first else -1
        return F.normalize(x, dim=dim) * self.scale * self.gamma + self.bias</code></pre></details>
<h3 id="fastvideo.models.vaes.cosmos25wanvae.Cosmos25RMSNorm.forward"><code>fastvideo.models.vaes.cosmos25wanvae.Cosmos25RMSNorm.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:
    dim = 1 if self.channel_first else -1
    return F.normalize(x, dim=dim) * self.scale * self.gamma + self.bias</code></pre></details>
<h2 id="fastvideo.models.vaes.cosmos25wanvae.Cosmos25Upsample"><code>fastvideo.models.vaes.cosmos25wanvae.Cosmos25Upsample</code></h2>
<pre class="doc-signature"><code>Cosmos25Upsample()</code></pre>
<p>Official-like Upsample that is safe for bf16 (casts to fp32 internally).</p>
<details><summary>Source</summary><pre><code>class Cosmos25Upsample(nn.Upsample):
    &quot;&quot;&quot;Official-like Upsample that is safe for bf16 (casts to fp32 internally).&quot;&quot;&quot;

    def forward(self, x: torch.Tensor) -&gt; torch.Tensor:  # type: ignore&#91;override&#93;
        return super().forward(x.float()).type_as(x)</code></pre></details>
<h3 id="fastvideo.models.vaes.cosmos25wanvae.Cosmos25Upsample.forward"><code>fastvideo.models.vaes.cosmos25wanvae.Cosmos25Upsample.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:  # type: ignore&#91;override&#93;
    return super().forward(x.float()).type_as(x)</code></pre></details>
<h2 id="fastvideo.models.vaes.cosmos25wanvae.Cosmos25Resample"><code>fastvideo.models.vaes.cosmos25wanvae.Cosmos25Resample</code></h2>
<pre class="doc-signature"><code>Cosmos25Resample(dim: int, mode: str)</code></pre>
<p>Official-like Resample used for both spatial and temporal up/downsampling.</p>

<details><summary>Source</summary><pre><code>class Cosmos25Resample(nn.Module):
    &quot;&quot;&quot;
    Official-like Resample used for both spatial and temporal up/downsampling.
    &quot;&quot;&quot;

    def __init__(self, dim: int, mode: str) -&gt; None:
        assert mode in (&quot;none&quot;, &quot;upsample2d&quot;, &quot;upsample3d&quot;, &quot;downsample2d&quot;, &quot;downsample3d&quot;)
        super().__init__()
        self.dim = dim
        self.mode = mode

        if mode == &quot;upsample2d&quot;:
            self.resample = nn.Sequential(
                Cosmos25Upsample(scale_factor=(2.0, 2.0), mode=&quot;nearest-exact&quot;),
                nn.Conv2d(dim, dim // 2, 3, padding=1),
            )
        elif mode == &quot;upsample3d&quot;:
            self.resample = nn.Sequential(
                Cosmos25Upsample(scale_factor=(2.0, 2.0), mode=&quot;nearest-exact&quot;),
                nn.Conv2d(dim, dim // 2, 3, padding=1),
            )
            self.time_conv = Cosmos25CausalConv3d(dim, dim * 2, (3, 1, 1), padding=(1, 0, 0))
        elif mode == &quot;downsample2d&quot;:
            self.resample = nn.Sequential(nn.ZeroPad2d((0, 1, 0, 1)), nn.Conv2d(dim, dim, 3, stride=(2, 2)))
        elif mode == &quot;downsample3d&quot;:
            self.resample = nn.Sequential(nn.ZeroPad2d((0, 1, 0, 1)), nn.Conv2d(dim, dim, 3, stride=(2, 2)))
            self.time_conv = Cosmos25CausalConv3d(dim, dim, (3, 1, 1), stride=(2, 1, 1), padding=(0, 0, 0))
        else:
            self.resample = nn.Identity()

    def forward(self, x: torch.Tensor, feat_cache: list&#91;Any&#93; | None = None, feat_idx: list&#91;int&#93; = &#91;0&#93;) -&gt; torch.Tensor:
        b, c, t, h, w = x.size()

        # Temporal upsample uses a time-conv and then interleaves frames.
        if self.mode == &quot;upsample3d&quot; and feat_cache is not None:
            idx = feat_idx&#91;0&#93;
            if feat_cache&#91;idx&#93; is None:
                feat_cache&#91;idx&#93; = &quot;Rep&quot;
                feat_idx&#91;0&#93; += 1
            else:
                cache_x = x&#91;:, :, -CACHE_T:, :, :&#93;.clone()
                if cache_x.shape&#91;2&#93; &lt; 2 and feat_cache&#91;idx&#93; is not None and feat_cache&#91;idx&#93; != &quot;Rep&quot;:
                    cache_x = torch.cat(&#91;feat_cache&#91;idx&#93;&#91;:, :, -1, :, :&#93;.unsqueeze(2).to(cache_x.device), cache_x&#93;,
                                        dim=2)
                if cache_x.shape&#91;2&#93; &lt; 2 and feat_cache&#91;idx&#93; is not None and feat_cache&#91;idx&#93; == &quot;Rep&quot;:
                    cache_x = torch.cat(&#91;torch.zeros_like(cache_x).to(cache_x.device), cache_x&#93;, dim=2)

                x = self.time_conv(x) if feat_cache&#91;idx&#93; == &quot;Rep&quot; else self.time_conv(x, feat_cache&#91;idx&#93;)
                feat_cache&#91;idx&#93; = cache_x
                feat_idx&#91;0&#93; += 1

                x = x.reshape(b, 2, c, t, h, w)
                x = torch.stack((x&#91;:, 0, :, :, :, :&#93;, x&#91;:, 1, :, :, :, :&#93;), 3)
                x = x.reshape(b, c, t * 2, h, w)

        t = x.shape&#91;2&#93;
        x = rearrange(x, &quot;b c t h w -&gt; (b t) c h w&quot;)
        x = self.resample(x)
        x = rearrange(x, &quot;(b t) c h w -&gt; b c t h w&quot;, t=t)

        # Temporal downsample: time_conv consumes last-frame cache.
        if self.mode == &quot;downsample3d&quot; and feat_cache is not None:
            idx = feat_idx&#91;0&#93;
            if feat_cache&#91;idx&#93; is None:
                feat_cache&#91;idx&#93; = x.clone()
                feat_idx&#91;0&#93; += 1
            else:
                cache_x = x&#91;:, :, -1:, :, :&#93;.clone()
                x = self.time_conv(torch.cat(&#91;feat_cache&#91;idx&#93;&#91;:, :, -1:, :, :&#93;, x&#93;, 2))
                feat_cache&#91;idx&#93; = cache_x
                feat_idx&#91;0&#93; += 1

        return x</code></pre></details>
<h3 id="fastvideo.models.vaes.cosmos25wanvae.Cosmos25Resample.forward"><code>fastvideo.models.vaes.cosmos25wanvae.Cosmos25Resample.forward</code></h3>
<pre class="doc-signature"><code>forward(x: torch.Tensor, feat_cache: list&#91;Any&#93; | None = None, feat_idx: list&#91;int&#93; = &#91;0&#93;) -&gt; torch.Tensor</code></pre>

<details><summary>Source</summary><pre><code>def forward(self, x: torch.Tensor, feat_cache: list&#91;Any&#93; | None = None, feat_idx: list&#91;int&#93; = &#91;0&#93;) -&gt; torch.Tensor:
    b, c, t, h, w = x.size()

    # Temporal upsample uses a time-conv and then interleaves frames.
    if self.mode == &quot;upsample3d&quot; and feat_cache is not None:
        idx = feat_idx&#91;0&#93;
        if feat_cache&#91;idx&#93; is None:
            feat_cache&#91;idx&#93; = &quot;Rep&quot;
            feat_idx&#91;0&#93; += 1
        else:
            cache_x = x&#91;:, :, -CACHE_T:, :, :&#93;.clone()
            if cache_x.shape&#91;2&#93; &lt; 2 and feat_cache&#91;idx&#93; is not None and feat_cache&#91;idx&#93; != &quot;Rep&quot;:
                cache_x = torch.cat(&#91;feat_cache&#91;idx&#93;&#91;:, :, -1, :, :&#93;.unsqueeze(2).to(cache_x.device), cache_x&#93;,
                                    dim=2)
            if cache_x.shape&#91;2&#93; &lt; 2 and feat_cache&#91;idx&#93; is not None and feat_cache&#91;idx&#93; == &quot;Rep&quot;:
                cache_x = torch.cat(&#91;torch.zeros_like(cache_x).to(cache_x.device), cache_x&#93;, dim=2)

            x = self.time_conv(x) if feat_cache&#91;idx&#93; == &quot;Rep&quot; else self.time_conv(x, feat_cache&#91;idx&#93;)
            feat_cache&#91;idx&#93; = cache_x
            feat_idx&#91;0&#93; += 1

            x = x.reshape(b, 2, c, t, h, w)
            x = torch.stack((x&#91;:, 0, :, :, :, :&#93;, x&#91;:, 1, :, :, :, :&#93;), 3)
            x = x.reshape(b, c, t * 2, h, w)

    t = x.shape&#91;2&#93;
    x = rearrange(x, &quot;b c t h w -&gt; (b t) c h w&quot;)
    x = self.resample(x)
    x = rearrange(x, &quot;(b t) c h w -&gt; b c t h w&quot;, t=t)

    # Temporal downsample: time_conv consumes last-frame cache.
    if self.mode == &quot;downsample3d&quot; and feat_cache is not None:
        idx = feat_idx&#91;0&#93;
        if feat_cache&#91;idx&#93; is None:
            feat_cache&#91;idx&#93; = x.clone()
            feat_idx&#91;0&#93; += 1
        else:
            cache_x = x&#91;:, :, -1:, :, :&#93;.clone()
            x = self.time_conv(torch.cat(&#91;feat_cache&#91;idx&#93;&#91;:, :, -1:, :, :&#93;, x&#93;, 2))
            feat_cache&#91;idx&#93; = cache_x
            feat_idx&#91;0&#93; += 1

    return x</code></pre></details>
<h2 id="fastvideo.models.vaes.cosmos25wanvae.Cosmos25ResidualBlock"><code>fastvideo.models.vaes.cosmos25wanvae.Cosmos25ResidualBlock</code></h2>
<pre class="doc-signature"><code>Cosmos25ResidualBlock(in_dim: int, out_dim: int, dropout: float = 0.0)</code></pre>


<details><summary>Source</summary><pre><code>class Cosmos25ResidualBlock(nn.Module):

    def __init__(self, in_dim: int, out_dim: int, dropout: float = 0.0) -&gt; None:
        super().__init__()
        self.in_dim = in_dim
        self.out_dim = out_dim
        self.residual = nn.Sequential(
            Cosmos25RMSNorm(in_dim, images=False),
            nn.SiLU(),
            Cosmos25CausalConv3d(in_dim, out_dim, 3, padding=1),
            Cosmos25RMSNorm(out_dim, images=False),
            nn.SiLU(),
            nn.Dropout(dropout),
            Cosmos25CausalConv3d(out_dim, out_dim, 3, padding=1),
        )
        self.shortcut = Cosmos25CausalConv3d(in_dim, out_dim, 1) if in_dim != out_dim else nn.Identity()

    def forward(self, x: torch.Tensor, feat_cache: list&#91;Any&#93; | None = None, feat_idx: list&#91;int&#93; = &#91;0&#93;) -&gt; torch.Tensor:
        h = self.shortcut(x)
        for layer in self.residual:
            if isinstance(layer, Cosmos25CausalConv3d) and feat_cache is not None:
                idx = feat_idx&#91;0&#93;
                cache_x = x&#91;:, :, -CACHE_T:, :, :&#93;.clone()
                if cache_x.shape&#91;2&#93; &lt; 2 and feat_cache&#91;idx&#93; is not None:
                    cache_x = torch.cat(&#91;feat_cache&#91;idx&#93;&#91;:, :, -1, :, :&#93;.unsqueeze(2).to(cache_x.device), cache_x&#93;,
                                        dim=2)
                x = layer(x, feat_cache&#91;idx&#93;)
                feat_cache&#91;idx&#93; = cache_x
                feat_idx&#91;0&#93; += 1
            else:
                x = layer(x)
        return x + h</code></pre></details>
<h3 id="fastvideo.models.vaes.cosmos25wanvae.Cosmos25ResidualBlock.forward"><code>fastvideo.models.vaes.cosmos25wanvae.Cosmos25ResidualBlock.forward</code></h3>
<pre class="doc-signature"><code>forward(x: torch.Tensor, feat_cache: list&#91;Any&#93; | None = None, feat_idx: list&#91;int&#93; = &#91;0&#93;) -&gt; torch.Tensor</code></pre>

<details><summary>Source</summary><pre><code>def forward(self, x: torch.Tensor, feat_cache: list&#91;Any&#93; | None = None, feat_idx: list&#91;int&#93; = &#91;0&#93;) -&gt; torch.Tensor:
    h = self.shortcut(x)
    for layer in self.residual:
        if isinstance(layer, Cosmos25CausalConv3d) and feat_cache is not None:
            idx = feat_idx&#91;0&#93;
            cache_x = x&#91;:, :, -CACHE_T:, :, :&#93;.clone()
            if cache_x.shape&#91;2&#93; &lt; 2 and feat_cache&#91;idx&#93; is not None:
                cache_x = torch.cat(&#91;feat_cache&#91;idx&#93;&#91;:, :, -1, :, :&#93;.unsqueeze(2).to(cache_x.device), cache_x&#93;,
                                    dim=2)
            x = layer(x, feat_cache&#91;idx&#93;)
            feat_cache&#91;idx&#93; = cache_x
            feat_idx&#91;0&#93; += 1
        else:
            x = layer(x)
    return x + h</code></pre></details>
<h2 id="fastvideo.models.vaes.cosmos25wanvae.Cosmos25AttentionBlock"><code>fastvideo.models.vaes.cosmos25wanvae.Cosmos25AttentionBlock</code></h2>
<pre class="doc-signature"><code>Cosmos25AttentionBlock(dim: int)</code></pre>
<p>Official-like causal self-attention with a single head.</p>

<details><summary>Source</summary><pre><code>class Cosmos25AttentionBlock(nn.Module):
    &quot;&quot;&quot;Official-like causal self-attention with a single head.&quot;&quot;&quot;

    def __init__(self, dim: int) -&gt; None:
        super().__init__()
        self.dim = dim
        self.norm = Cosmos25RMSNorm(dim)
        self.to_qkv = nn.Conv2d(dim, dim * 3, 1)
        self.proj = nn.Conv2d(dim, dim, 1)
        nn.init.zeros_(self.proj.weight)

    def forward(self, x: torch.Tensor) -&gt; torch.Tensor:
        identity = x
        b, c, t, h, w = x.size()
        x2 = rearrange(x, &quot;b c t h w -&gt; (b t) c h w&quot;)
        x2 = self.norm(x2)
        q, k, v = (self.to_qkv(x2).reshape(b * t, 1, c * 3, -1).permute(0, 1, 3, 2).contiguous().chunk(3, dim=-1))
        x2 = F.scaled_dot_product_attention(q, k, v)
        x2 = x2.squeeze(1).permute(0, 2, 1).reshape(b * t, c, h, w)
        x2 = self.proj(x2)
        x2 = rearrange(x2, &quot;(b t) c h w-&gt; b c t h w&quot;, t=t)
        return x2 + identity</code></pre></details>
<h3 id="fastvideo.models.vaes.cosmos25wanvae.Cosmos25AttentionBlock.forward"><code>fastvideo.models.vaes.cosmos25wanvae.Cosmos25AttentionBlock.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:
    identity = x
    b, c, t, h, w = x.size()
    x2 = rearrange(x, &quot;b c t h w -&gt; (b t) c h w&quot;)
    x2 = self.norm(x2)
    q, k, v = (self.to_qkv(x2).reshape(b * t, 1, c * 3, -1).permute(0, 1, 3, 2).contiguous().chunk(3, dim=-1))
    x2 = F.scaled_dot_product_attention(q, k, v)
    x2 = x2.squeeze(1).permute(0, 2, 1).reshape(b * t, c, h, w)
    x2 = self.proj(x2)
    x2 = rearrange(x2, &quot;(b t) c h w-&gt; b c t h w&quot;, t=t)
    return x2 + identity</code></pre></details>
<h2 id="fastvideo.models.vaes.cosmos25wanvae.Cosmos25Encoder3d"><code>fastvideo.models.vaes.cosmos25wanvae.Cosmos25Encoder3d</code></h2>
<pre class="doc-signature"><code>Cosmos25Encoder3d(dim: int = 96, z_dim: int = 32, dim_mult: list&#91;int&#93; = &#91;1, 2, 4, 4&#93;, num_res_blocks: int = 2, attn_scales: list&#91;float&#93; = &#91;&#93;, temperal_downsample: list&#91;bool&#93; = &#91;False, True, True&#93;, dropout: float = 0.0)</code></pre>


<details><summary>Source</summary><pre><code>class Cosmos25Encoder3d(nn.Module):

    def __init__(
        self,
        dim: int = 96,
        z_dim: int = 32,
        dim_mult: list&#91;int&#93; = &#91;1, 2, 4, 4&#93;,
        num_res_blocks: int = 2,
        attn_scales: list&#91;float&#93; = &#91;&#93;,
        temperal_downsample: list&#91;bool&#93; = &#91;False, True, True&#93;,
        dropout: float = 0.0,
    ) -&gt; None:
        super().__init__()
        dims = &#91;dim * u for u in &#91;1&#93; + dim_mult&#93;
        scale = 1.0

        self.conv1 = Cosmos25CausalConv3d(3, dims&#91;0&#93;, 3, padding=1)

        downsamples: list&#91;nn.Module&#93; = &#91;&#93;
        for i, (in_dim, out_dim) in enumerate(zip(dims&#91;:-1&#93;, dims&#91;1:&#93;)):
            for _ in range(num_res_blocks):
                downsamples.append(Cosmos25ResidualBlock(in_dim, out_dim, dropout))
                if scale in attn_scales:
                    downsamples.append(Cosmos25AttentionBlock(out_dim))
                in_dim = out_dim

            if i != len(dim_mult) - 1:
                mode = &quot;downsample3d&quot; if temperal_downsample&#91;i&#93; else &quot;downsample2d&quot;
                downsamples.append(Cosmos25Resample(out_dim, mode=mode))
                scale /= 2.0
        self.downsamples = nn.Sequential(*downsamples)

        self.middle = nn.Sequential(
            Cosmos25ResidualBlock(out_dim, out_dim, dropout),
            Cosmos25AttentionBlock(out_dim),
            Cosmos25ResidualBlock(out_dim, out_dim, dropout),
        )

        self.head = nn.Sequential(
            Cosmos25RMSNorm(out_dim, images=False),
            nn.SiLU(),
            Cosmos25CausalConv3d(out_dim, z_dim, 3, padding=1),
        )

    def forward(self, x: torch.Tensor, feat_cache: list&#91;Any&#93; | None = None, feat_idx: list&#91;int&#93; = &#91;0&#93;) -&gt; torch.Tensor:
        if feat_cache is not None:
            idx = feat_idx&#91;0&#93;
            cache_x = x&#91;:, :, -CACHE_T:, :, :&#93;.clone()
            if cache_x.shape&#91;2&#93; &lt; 2 and feat_cache&#91;idx&#93; is not None:
                cache_x = torch.cat(&#91;feat_cache&#91;idx&#93;&#91;:, :, -1, :, :&#93;.unsqueeze(2).to(cache_x.device), cache_x&#93;, dim=2)
            x = self.conv1(x, feat_cache&#91;idx&#93;)
            feat_cache&#91;idx&#93; = cache_x
            feat_idx&#91;0&#93; += 1
        else:
            x = self.conv1(x)

        for layer in self.downsamples:
            x = layer(x, feat_cache, feat_idx) if feat_cache is not None else layer(x)  # type: ignore&#91;misc&#93;

        for layer in self.middle:
            if isinstance(layer, Cosmos25ResidualBlock) and feat_cache is not None:
                x = layer(x, feat_cache, feat_idx)
            else:
                x = layer(x)  # type: ignore&#91;misc&#93;

        for layer in self.head:
            if isinstance(layer, Cosmos25CausalConv3d) and feat_cache is not None:
                idx = feat_idx&#91;0&#93;
                cache_x = x&#91;:, :, -CACHE_T:, :, :&#93;.clone()
                if cache_x.shape&#91;2&#93; &lt; 2 and feat_cache&#91;idx&#93; is not None:
                    cache_x = torch.cat(&#91;feat_cache&#91;idx&#93;&#91;:, :, -1, :, :&#93;.unsqueeze(2).to(cache_x.device), cache_x&#93;,
                                        dim=2)
                x = layer(x, feat_cache&#91;idx&#93;)
                feat_cache&#91;idx&#93; = cache_x
                feat_idx&#91;0&#93; += 1
            else:
                x = layer(x)  # type: ignore&#91;misc&#93;
        return x</code></pre></details>
<h3 id="fastvideo.models.vaes.cosmos25wanvae.Cosmos25Encoder3d.forward"><code>fastvideo.models.vaes.cosmos25wanvae.Cosmos25Encoder3d.forward</code></h3>
<pre class="doc-signature"><code>forward(x: torch.Tensor, feat_cache: list&#91;Any&#93; | None = None, feat_idx: list&#91;int&#93; = &#91;0&#93;) -&gt; torch.Tensor</code></pre>

<details><summary>Source</summary><pre><code>def forward(self, x: torch.Tensor, feat_cache: list&#91;Any&#93; | None = None, feat_idx: list&#91;int&#93; = &#91;0&#93;) -&gt; torch.Tensor:
    if feat_cache is not None:
        idx = feat_idx&#91;0&#93;
        cache_x = x&#91;:, :, -CACHE_T:, :, :&#93;.clone()
        if cache_x.shape&#91;2&#93; &lt; 2 and feat_cache&#91;idx&#93; is not None:
            cache_x = torch.cat(&#91;feat_cache&#91;idx&#93;&#91;:, :, -1, :, :&#93;.unsqueeze(2).to(cache_x.device), cache_x&#93;, dim=2)
        x = self.conv1(x, feat_cache&#91;idx&#93;)
        feat_cache&#91;idx&#93; = cache_x
        feat_idx&#91;0&#93; += 1
    else:
        x = self.conv1(x)

    for layer in self.downsamples:
        x = layer(x, feat_cache, feat_idx) if feat_cache is not None else layer(x)  # type: ignore&#91;misc&#93;

    for layer in self.middle:
        if isinstance(layer, Cosmos25ResidualBlock) and feat_cache is not None:
            x = layer(x, feat_cache, feat_idx)
        else:
            x = layer(x)  # type: ignore&#91;misc&#93;

    for layer in self.head:
        if isinstance(layer, Cosmos25CausalConv3d) and feat_cache is not None:
            idx = feat_idx&#91;0&#93;
            cache_x = x&#91;:, :, -CACHE_T:, :, :&#93;.clone()
            if cache_x.shape&#91;2&#93; &lt; 2 and feat_cache&#91;idx&#93; is not None:
                cache_x = torch.cat(&#91;feat_cache&#91;idx&#93;&#91;:, :, -1, :, :&#93;.unsqueeze(2).to(cache_x.device), cache_x&#93;,
                                    dim=2)
            x = layer(x, feat_cache&#91;idx&#93;)
            feat_cache&#91;idx&#93; = cache_x
            feat_idx&#91;0&#93; += 1
        else:
            x = layer(x)  # type: ignore&#91;misc&#93;
    return x</code></pre></details>
<h2 id="fastvideo.models.vaes.cosmos25wanvae.Cosmos25Decoder3d"><code>fastvideo.models.vaes.cosmos25wanvae.Cosmos25Decoder3d</code></h2>
<pre class="doc-signature"><code>Cosmos25Decoder3d(dim: int = 96, z_dim: int = 16, dim_mult: list&#91;int&#93; = &#91;1, 2, 4, 4&#93;, num_res_blocks: int = 2, attn_scales: list&#91;float&#93; = &#91;&#93;, temperal_upsample: list&#91;bool&#93; = &#91;False, True, True&#93;, dropout: float = 0.0)</code></pre>


<details><summary>Source</summary><pre><code>class Cosmos25Decoder3d(nn.Module):

    def __init__(
        self,
        dim: int = 96,
        z_dim: int = 16,
        dim_mult: list&#91;int&#93; = &#91;1, 2, 4, 4&#93;,
        num_res_blocks: int = 2,
        attn_scales: list&#91;float&#93; = &#91;&#93;,
        temperal_upsample: list&#91;bool&#93; = &#91;False, True, True&#93;,
        dropout: float = 0.0,
    ) -&gt; None:
        super().__init__()
        dims = &#91;dim * u for u in &#91;dim_mult&#91;-1&#93;&#93; + dim_mult&#91;::-1&#93;&#93;
        scale = 1.0 / 2**(len(dim_mult) - 2)

        self.conv1 = Cosmos25CausalConv3d(z_dim, dims&#91;0&#93;, 3, padding=1)
        self.middle = nn.Sequential(
            Cosmos25ResidualBlock(dims&#91;0&#93;, dims&#91;0&#93;, dropout),
            Cosmos25AttentionBlock(dims&#91;0&#93;),
            Cosmos25ResidualBlock(dims&#91;0&#93;, dims&#91;0&#93;, dropout),
        )

        upsamples: list&#91;nn.Module&#93; = &#91;&#93;
        for i, (in_dim, out_dim) in enumerate(zip(dims&#91;:-1&#93;, dims&#91;1:&#93;)):
            if i in (1, 2, 3):
                in_dim = in_dim // 2
            for _ in range(num_res_blocks + 1):
                upsamples.append(Cosmos25ResidualBlock(in_dim, out_dim, dropout))
                if scale in attn_scales:
                    upsamples.append(Cosmos25AttentionBlock(out_dim))
                in_dim = out_dim
            if i != len(dim_mult) - 1:
                mode = &quot;upsample3d&quot; if temperal_upsample&#91;i&#93; else &quot;upsample2d&quot;
                upsamples.append(Cosmos25Resample(out_dim, mode=mode))
                scale *= 2.0
        self.upsamples = nn.Sequential(*upsamples)

        self.head = nn.Sequential(
            Cosmos25RMSNorm(out_dim, images=False),
            nn.SiLU(),
            Cosmos25CausalConv3d(out_dim, 3, 3, padding=1),
        )

    def forward(self, x: torch.Tensor, feat_cache: list&#91;Any&#93; | None = None, feat_idx: list&#91;int&#93; = &#91;0&#93;) -&gt; torch.Tensor:
        if feat_cache is not None:
            idx = feat_idx&#91;0&#93;
            cache_x = x&#91;:, :, -CACHE_T:, :, :&#93;.clone()
            if cache_x.shape&#91;2&#93; &lt; 2 and feat_cache&#91;idx&#93; is not None:
                cache_x = torch.cat(&#91;feat_cache&#91;idx&#93;&#91;:, :, -1, :, :&#93;.unsqueeze(2).to(cache_x.device), cache_x&#93;, dim=2)
            x = self.conv1(x, feat_cache&#91;idx&#93;)
            feat_cache&#91;idx&#93; = cache_x
            feat_idx&#91;0&#93; += 1
        else:
            x = self.conv1(x)

        for layer in self.middle:
            if isinstance(layer, Cosmos25ResidualBlock) and feat_cache is not None:
                x = layer(x, feat_cache, feat_idx)
            else:
                x = layer(x)  # type: ignore&#91;misc&#93;

        for layer in self.upsamples:
            x = layer(x, feat_cache, feat_idx) if feat_cache is not None else layer(x)  # type: ignore&#91;misc&#93;

        for layer in self.head:
            if isinstance(layer, Cosmos25CausalConv3d) and feat_cache is not None:
                idx = feat_idx&#91;0&#93;
                cache_x = x&#91;:, :, -CACHE_T:, :, :&#93;.clone()
                if cache_x.shape&#91;2&#93; &lt; 2 and feat_cache&#91;idx&#93; is not None:
                    cache_x = torch.cat(&#91;feat_cache&#91;idx&#93;&#91;:, :, -1, :, :&#93;.unsqueeze(2).to(cache_x.device), cache_x&#93;,
                                        dim=2)
                x = layer(x, feat_cache&#91;idx&#93;)
                feat_cache&#91;idx&#93; = cache_x
                feat_idx&#91;0&#93; += 1
            else:
                x = layer(x)  # type: ignore&#91;misc&#93;
        return x</code></pre></details>
<h3 id="fastvideo.models.vaes.cosmos25wanvae.Cosmos25Decoder3d.forward"><code>fastvideo.models.vaes.cosmos25wanvae.Cosmos25Decoder3d.forward</code></h3>
<pre class="doc-signature"><code>forward(x: torch.Tensor, feat_cache: list&#91;Any&#93; | None = None, feat_idx: list&#91;int&#93; = &#91;0&#93;) -&gt; torch.Tensor</code></pre>

<details><summary>Source</summary><pre><code>def forward(self, x: torch.Tensor, feat_cache: list&#91;Any&#93; | None = None, feat_idx: list&#91;int&#93; = &#91;0&#93;) -&gt; torch.Tensor:
    if feat_cache is not None:
        idx = feat_idx&#91;0&#93;
        cache_x = x&#91;:, :, -CACHE_T:, :, :&#93;.clone()
        if cache_x.shape&#91;2&#93; &lt; 2 and feat_cache&#91;idx&#93; is not None:
            cache_x = torch.cat(&#91;feat_cache&#91;idx&#93;&#91;:, :, -1, :, :&#93;.unsqueeze(2).to(cache_x.device), cache_x&#93;, dim=2)
        x = self.conv1(x, feat_cache&#91;idx&#93;)
        feat_cache&#91;idx&#93; = cache_x
        feat_idx&#91;0&#93; += 1
    else:
        x = self.conv1(x)

    for layer in self.middle:
        if isinstance(layer, Cosmos25ResidualBlock) and feat_cache is not None:
            x = layer(x, feat_cache, feat_idx)
        else:
            x = layer(x)  # type: ignore&#91;misc&#93;

    for layer in self.upsamples:
        x = layer(x, feat_cache, feat_idx) if feat_cache is not None else layer(x)  # type: ignore&#91;misc&#93;

    for layer in self.head:
        if isinstance(layer, Cosmos25CausalConv3d) and feat_cache is not None:
            idx = feat_idx&#91;0&#93;
            cache_x = x&#91;:, :, -CACHE_T:, :, :&#93;.clone()
            if cache_x.shape&#91;2&#93; &lt; 2 and feat_cache&#91;idx&#93; is not None:
                cache_x = torch.cat(&#91;feat_cache&#91;idx&#93;&#91;:, :, -1, :, :&#93;.unsqueeze(2).to(cache_x.device), cache_x&#93;,
                                    dim=2)
            x = layer(x, feat_cache&#91;idx&#93;)
            feat_cache&#91;idx&#93; = cache_x
            feat_idx&#91;0&#93; += 1
        else:
            x = layer(x)  # type: ignore&#91;misc&#93;
    return x</code></pre></details>
<h2 id="fastvideo.models.vaes.cosmos25wanvae.Cosmos25WanVAE"><code>fastvideo.models.vaes.cosmos25wanvae.Cosmos25WanVAE</code></h2>
<pre class="doc-signature"><code>Cosmos25WanVAE(*, device: torch.device | str = &#x27;cpu&#x27;, dtype: torch.dtype = torch.float32, temporal_window: int = 4, latents_mean: Optional&#91;torch.Tensor&#93; = None, latents_std: Optional&#91;torch.Tensor&#93; = None)</code></pre>
<p>A FastVideo-native copy of the <em>official-like</em> Wan2.1 VAE core.</p>
<p>Key properties:
- Module naming matches official tokenizer (<code>encoder</code>, <code>decoder</code>, <code>conv1</code>, <code>conv2</code>)
  so it can consume <code>tokenizer.pth</code> keys directly.
- <code>encode()</code> returns <strong>normalized</strong> latents and <code>decode()</code> expects <strong>normalized</strong>
  latents (Cosmos2.5 contract), matching <code>Wan2pt1VAEInterface</code>.</p>

<details><summary>Source</summary><pre><code>class Cosmos25WanVAE(nn.Module):
    &quot;&quot;&quot;
    A FastVideo-native copy of the *official-like* Wan2.1 VAE core.

    Key properties:
    - Module naming matches official tokenizer (`encoder`, `decoder`, `conv1`, `conv2`)
      so it can consume `tokenizer.pth` keys directly.
    - `encode()` returns **normalized** latents and `decode()` expects **normalized**
      latents (Cosmos2.5 contract), matching `Wan2pt1VAEInterface`.
    &quot;&quot;&quot;

    handles_latent_norm: bool = True
    handles_latent_denorm: bool = True

    def __init__(
        self,
        *,
        device: torch.device | str = &quot;cpu&quot;,
        dtype: torch.dtype = torch.float32,
        temporal_window: int = 4,
        latents_mean: Optional&#91;torch.Tensor&#93; = None,
        latents_std: Optional&#91;torch.Tensor&#93; = None,
    ) -&gt; None:
        super().__init__()

        # Official hyperparams for Cosmos2.5 tokenizer (Wan2.1 VAE).
        cfg = dict(
            dim=96,
            z_dim=16,
            dim_mult=&#91;1, 2, 4, 4&#93;,
            num_res_blocks=2,
            attn_scales=&#91;&#93;,
            temperal_downsample=&#91;False, True, True&#93;,
            dropout=0.0,
            temporal_window=temporal_window,
        )
        self.z_dim = 16
        self.temporal_window = temporal_window

        self.encoder = Cosmos25Encoder3d(
            dim=cfg&#91;&quot;dim&quot;&#93;,
            z_dim=cfg&#91;&quot;z_dim&quot;&#93; * 2,
            dim_mult=cfg&#91;&quot;dim_mult&quot;&#93;,
            num_res_blocks=cfg&#91;&quot;num_res_blocks&quot;&#93;,
            attn_scales=cfg&#91;&quot;attn_scales&quot;&#93;,
            temperal_downsample=cfg&#91;&quot;temperal_downsample&quot;&#93;,
            dropout=cfg&#91;&quot;dropout&quot;&#93;,
        )
        self.conv1 = Cosmos25CausalConv3d(self.z_dim * 2, self.z_dim * 2, 1)
        self.conv2 = Cosmos25CausalConv3d(self.z_dim, self.z_dim, 1)
        self.decoder = Cosmos25Decoder3d(
            dim=cfg&#91;&quot;dim&quot;&#93;,
            z_dim=cfg&#91;&quot;z_dim&quot;&#93;,
            dim_mult=cfg&#91;&quot;dim_mult&quot;&#93;,
            num_res_blocks=cfg&#91;&quot;num_res_blocks&quot;&#93;,
            attn_scales=cfg&#91;&quot;attn_scales&quot;&#93;,
            temperal_upsample=list(cfg&#91;&quot;temperal_downsample&quot;&#93;)&#91;::-1&#93;,
            dropout=cfg&#91;&quot;dropout&quot;&#93;,
        )

        # Default Cosmos2.5 latent stats (shared with configs).
        if latents_mean is None:
            latents_mean = torch.tensor(
                &#91;
                    -0.7571,
                    -0.7089,
                    -0.9113,
                    0.1075,
                    -0.1745,
                    0.9653,
                    -0.1517,
                    1.5508,
                    0.4134,
                    -0.0715,
                    0.5517,
                    -0.3632,
                    -0.1922,
                    -0.9497,
                    0.2503,
                    -0.2921,
                &#93;,
                dtype=torch.float32,
            ).view(1, 16, 1, 1, 1)
        if latents_std is None:
            latents_std = torch.tensor(
                &#91;
                    2.8184,
                    1.4541,
                    2.3275,
                    2.6558,
                    1.2196,
                    1.7708,
                    2.6052,
                    2.0743,
                    3.2687,
                    2.1526,
                    2.8652,
                    1.5579,
                    1.6382,
                    1.1253,
                    2.8251,
                    1.9160,
                &#93;,
                dtype=torch.float32,
            ).view(1, 16, 1, 1, 1)

        self.register_buffer(&quot;_latents_mean&quot;, latents_mean, persistent=False)
        self.register_buffer(&quot;_latents_std&quot;, latents_std, persistent=False)

        self.to(device=device, dtype=dtype)
        self.clear_cache()

    def clear_cache(self) -&gt; None:
        # Decoder cache
        self._conv_num = _count_cosmos25_conv3d(self.decoder)
        self._conv_idx = &#91;0&#93;
        self._feat_map: list&#91;Any&#93; = &#91;None&#93; * self._conv_num
        # Encoder cache
        self._enc_conv_num = _count_cosmos25_conv3d(self.encoder)
        self._enc_conv_idx = &#91;0&#93;
        self._enc_feat_map: list&#91;Any&#93; = &#91;None&#93; * self._enc_conv_num

    def _scale(self, like: torch.Tensor) -&gt; tuple&#91;torch.Tensor, torch.Tensor&#93;:
        mean = self._latents_mean.to(device=like.device, dtype=like.dtype)
        std = self._latents_std.to(device=like.device, dtype=like.dtype)
        return mean, 1.0 / std

    def _i0_encode(self, x: torch.Tensor) -&gt; torch.Tensor:
        return self.encoder(x&#91;:, :, :1, :, :&#93;, feat_cache=self._enc_feat_map, feat_idx=self._enc_conv_idx)

    def _i0_decode(self, x: torch.Tensor) -&gt; torch.Tensor:
        return self.decoder(x&#91;:, :, 0:1, :, :&#93;, feat_cache=self._feat_map, feat_idx=self._conv_idx)

    def encode(self, x: torch.Tensor) -&gt; _TensorLatentDist:
        &quot;&quot;&quot;
        Encode to *normalized* latents (Cosmos contract).
        &quot;&quot;&quot;
        self.clear_cache()
        t = x.shape&#91;2&#93;
        iters = 1 + (t - 1) // self.temporal_window

        for i in range(iters):
            self._enc_conv_idx = &#91;0&#93;
            if i == 0:
                out = self._i0_encode(x)
            else:
                out_ = self.encoder(
                    x&#91;:, :, 1 + self.temporal_window * (i - 1):1 + self.temporal_window * i, :, :&#93;,
                    feat_cache=self._enc_feat_map,
                    feat_idx=self._enc_conv_idx,
                )
                out = torch.cat(&#91;out, out_&#93;, 2)

        if (t - 1) % self.temporal_window:
            self._enc_conv_idx = &#91;0&#93;
            out_ = self.encoder(
                x&#91;:, :, 1 + self.temporal_window * (iters - 1):, :, :&#93;,
                feat_cache=self._enc_feat_map,
                feat_idx=self._enc_conv_idx,
            )
            out = torch.cat(&#91;out, out_&#93;, 2)

        mu, _log_var = self.conv1(out).chunk(2, dim=1)
        mean, inv_std = self._scale(mu)
        z_norm = (mu - mean) * inv_std
        self.clear_cache()
        return _TensorLatentDist(z_norm)

    def decode(self, latent: torch.Tensor) -&gt; torch.Tensor:
        &quot;&quot;&quot;
        Decode from *normalized* latents (Cosmos contract).
        &quot;&quot;&quot;
        self.clear_cache()
        mean, inv_std = self._scale(latent)
        z = latent / inv_std + mean  # z = z_norm * std + mean

        iter_ = z.shape&#91;2&#93;
        x = self.conv2(z)
        for i in range(iter_):
            self._conv_idx = &#91;0&#93;
            if i == 0:
                out = self._i0_decode(x)
            else:
                out_ = self.decoder(x&#91;:, :, i:i + 1, :, :&#93;, feat_cache=self._feat_map, feat_idx=self._conv_idx)
                out = torch.cat(&#91;out, out_&#93;, 2)

        self.clear_cache()
        return out

    # --- Interface helpers (match official Wan2pt1VAEInterface) ---
    def get_latent_num_frames(self, num_pixel_frames: int) -&gt; int:
        return 1 + (int(num_pixel_frames) - 1) // 4

    def get_pixel_num_frames(self, num_latent_frames: int) -&gt; int:
        return (int(num_latent_frames) - 1) * 4 + 1

    @property
    def spatial_compression_factor(self) -&gt; int:
        return 8

    @property
    def temporal_compression_factor(self) -&gt; int:
        return 4

    @property
    def latent_ch(self) -&gt; int:
        return 16</code></pre></details>
<h3 id="fastvideo.models.vaes.cosmos25wanvae.Cosmos25WanVAE.clear_cache"><code>fastvideo.models.vaes.cosmos25wanvae.Cosmos25WanVAE.clear_cache</code></h3>
<pre class="doc-signature"><code>clear_cache() -&gt; None</code></pre>

<details><summary>Source</summary><pre><code>def clear_cache(self) -&gt; None:
    # Decoder cache
    self._conv_num = _count_cosmos25_conv3d(self.decoder)
    self._conv_idx = &#91;0&#93;
    self._feat_map: list&#91;Any&#93; = &#91;None&#93; * self._conv_num
    # Encoder cache
    self._enc_conv_num = _count_cosmos25_conv3d(self.encoder)
    self._enc_conv_idx = &#91;0&#93;
    self._enc_feat_map: list&#91;Any&#93; = &#91;None&#93; * self._enc_conv_num</code></pre></details>
<h3 id="fastvideo.models.vaes.cosmos25wanvae.Cosmos25WanVAE.encode"><code>fastvideo.models.vaes.cosmos25wanvae.Cosmos25WanVAE.encode</code></h3>
<pre class="doc-signature"><code>encode(x: torch.Tensor) -&gt; _TensorLatentDist</code></pre>
<p>Encode to <em>normalized</em> latents (Cosmos contract).</p>
<details><summary>Source</summary><pre><code>def encode(self, x: torch.Tensor) -&gt; _TensorLatentDist:
    &quot;&quot;&quot;
    Encode to *normalized* latents (Cosmos contract).
    &quot;&quot;&quot;
    self.clear_cache()
    t = x.shape&#91;2&#93;
    iters = 1 + (t - 1) // self.temporal_window

    for i in range(iters):
        self._enc_conv_idx = &#91;0&#93;
        if i == 0:
            out = self._i0_encode(x)
        else:
            out_ = self.encoder(
                x&#91;:, :, 1 + self.temporal_window * (i - 1):1 + self.temporal_window * i, :, :&#93;,
                feat_cache=self._enc_feat_map,
                feat_idx=self._enc_conv_idx,
            )
            out = torch.cat(&#91;out, out_&#93;, 2)

    if (t - 1) % self.temporal_window:
        self._enc_conv_idx = &#91;0&#93;
        out_ = self.encoder(
            x&#91;:, :, 1 + self.temporal_window * (iters - 1):, :, :&#93;,
            feat_cache=self._enc_feat_map,
            feat_idx=self._enc_conv_idx,
        )
        out = torch.cat(&#91;out, out_&#93;, 2)

    mu, _log_var = self.conv1(out).chunk(2, dim=1)
    mean, inv_std = self._scale(mu)
    z_norm = (mu - mean) * inv_std
    self.clear_cache()
    return _TensorLatentDist(z_norm)</code></pre></details>
<h3 id="fastvideo.models.vaes.cosmos25wanvae.Cosmos25WanVAE.decode"><code>fastvideo.models.vaes.cosmos25wanvae.Cosmos25WanVAE.decode</code></h3>
<pre class="doc-signature"><code>decode(latent: torch.Tensor) -&gt; torch.Tensor</code></pre>
<p>Decode from <em>normalized</em> latents (Cosmos contract).</p>
<details><summary>Source</summary><pre><code>def decode(self, latent: torch.Tensor) -&gt; torch.Tensor:
    &quot;&quot;&quot;
    Decode from *normalized* latents (Cosmos contract).
    &quot;&quot;&quot;
    self.clear_cache()
    mean, inv_std = self._scale(latent)
    z = latent / inv_std + mean  # z = z_norm * std + mean

    iter_ = z.shape&#91;2&#93;
    x = self.conv2(z)
    for i in range(iter_):
        self._conv_idx = &#91;0&#93;
        if i == 0:
            out = self._i0_decode(x)
        else:
            out_ = self.decoder(x&#91;:, :, i:i + 1, :, :&#93;, feat_cache=self._feat_map, feat_idx=self._conv_idx)
            out = torch.cat(&#91;out, out_&#93;, 2)

    self.clear_cache()
    return out</code></pre></details>
<h3 id="fastvideo.models.vaes.cosmos25wanvae.Cosmos25WanVAE.get_latent_num_frames"><code>fastvideo.models.vaes.cosmos25wanvae.Cosmos25WanVAE.get_latent_num_frames</code></h3>
<pre class="doc-signature"><code>get_latent_num_frames(num_pixel_frames: int) -&gt; int</code></pre>

<details><summary>Source</summary><pre><code>def get_latent_num_frames(self, num_pixel_frames: int) -&gt; int:
    return 1 + (int(num_pixel_frames) - 1) // 4</code></pre></details>
<h3 id="fastvideo.models.vaes.cosmos25wanvae.Cosmos25WanVAE.get_pixel_num_frames"><code>fastvideo.models.vaes.cosmos25wanvae.Cosmos25WanVAE.get_pixel_num_frames</code></h3>
<pre class="doc-signature"><code>get_pixel_num_frames(num_latent_frames: int) -&gt; int</code></pre>

<details><summary>Source</summary><pre><code>def get_pixel_num_frames(self, num_latent_frames: int) -&gt; int:
    return (int(num_latent_frames) - 1) * 4 + 1</code></pre></details>
</div>
