---
title: gen3c_tokenizer_vae
---

<div class="python-reference">
<a id="fastvideo.models.vaes.gen3c_tokenizer_vae-classes"></a>
<a id="fastvideo.models.vaes.gen3c_tokenizer_vae-functions"></a>
<a id="fastvideo.models.vaes.gen3c_tokenizer_vae"></a>
<p>GEN3C tokenizer-backed VAE adapter.</p>
<p>This wrapper loads the available tokenizer checkpoint (<code>tokenizer.pth</code>) and
adapts it to GEN3C's latent-time contract (T=16 for 121 output frames).</p>
<p>Why this exists:
- The converted GEN3C bundle includes tokenizer-style VAE weights, not a
  standard diffusers Wan VAE contract.
- GEN3C diffusion expects 8x temporal compression (121 -&gt; 16), while the
  available tokenizer checkpoint follows a 4x temporal path.</p>
<p>To bridge this at inference time, we:
- keep the inner tokenizer model as-is,
- downsample encoded latent time from inner-T to target-T for DiT input,
- upsample generated latent time back to inner-T before decoding.</p>
<h2 id="fastvideo.models.vaes.gen3c_tokenizer_vae.init_logger"><code>fastvideo.models.vaes.gen3c_tokenizer_vae.init_logger</code></h2>
<pre class="doc-signature"><code>init_logger(name: str) -&gt; _FastvideoLogger</code></pre>
<p>The main purpose of this function is to ensure that loggers are
retrieved in such a way that we can be sure the root fastvideo logger has
already been configured.</p>
<h2 id="fastvideo.models.vaes.gen3c_tokenizer_vae.AutoencoderKLGen3CTokenizer"><code>fastvideo.models.vaes.gen3c_tokenizer_vae.AutoencoderKLGen3CTokenizer</code></h2>
<pre class="doc-signature"><code>AutoencoderKLGen3CTokenizer(inner: nn.Module, *, target_temporal_compression: int = 8, inner_temporal_compression: int = 4, spatial_compression_factor: int = 8, pixel_chunk_duration: int = 121)</code></pre>
<p>GEN3C VAE wrapper with temporal contract adaptation.</p>
<p>Interface contract:
- <code>encode(x)</code> returns normalized latents in the <em>target</em> temporal layout.
- <code>decode(z)</code> expects normalized latents in the <em>target</em> temporal layout.</p>

<details><summary>Source</summary><pre><code>class AutoencoderKLGen3CTokenizer(nn.Module):
    &quot;&quot;&quot;
    GEN3C VAE wrapper with temporal contract adaptation.

    Interface contract:
    - `encode(x)` returns normalized latents in the *target* temporal layout.
    - `decode(z)` expects normalized latents in the *target* temporal layout.
    &quot;&quot;&quot;

    handles_latent_norm: bool = True
    handles_latent_denorm: bool = True

    def __init__(
        self,
        inner: nn.Module,
        *,
        target_temporal_compression: int = 8,
        inner_temporal_compression: int = 4,
        spatial_compression_factor: int = 8,
        pixel_chunk_duration: int = 121,
    ) -&gt; None:
        super().__init__()
        self.inner = inner
        self.config = getattr(inner, &quot;config&quot;, None)
        self._target_temporal_compression = int(target_temporal_compression)
        self._inner_temporal_compression = int(inner_temporal_compression)
        self._spatial_compression_factor = int(spatial_compression_factor)
        self._pixel_chunk_duration = int(pixel_chunk_duration)

    @staticmethod
    def _extract_latents(encoder_output: Any) -&gt; torch.Tensor:
        if hasattr(encoder_output, &quot;latent_dist&quot;):
            dist = encoder_output.latent_dist
            if hasattr(dist, &quot;mode&quot;):
                return dist.mode()
            if hasattr(dist, &quot;mean&quot;):
                return dist.mean
            return dist.sample()
        if hasattr(encoder_output, &quot;mode&quot;):
            return encoder_output.mode()
        if hasattr(encoder_output, &quot;latents&quot;):
            return encoder_output.latents
        if hasattr(encoder_output, &quot;sample&quot;):
            return encoder_output.sample()
        if isinstance(encoder_output, torch.Tensor):
            return encoder_output
        raise TypeError(f&quot;Unsupported encoder output type: &#123;type(encoder_output)&#125;&quot;)

    def _inner_to_target_time(self, z_inner: torch.Tensor) -&gt; torch.Tensor:
        if z_inner.shape&#91;2&#93; &lt;= 1:
            return z_inner

        # Common GEN3C case: inner=4x, target=8x =&gt; keep every other latent frame.
        if self._target_temporal_compression == 2 * self._inner_temporal_compression:
            return z_inner&#91;:, :, 0::2, :, :&#93;.contiguous()

        # Generic fallback: keep boundary latents and sample uniformly.
        t_inner = z_inner.shape&#91;2&#93;
        t_target = 1 + (t_inner - 1) * self._inner_temporal_compression // self._target_temporal_compression
        idx = torch.linspace(0, t_inner - 1, t_target, device=z_inner.device)
        idx = idx.round().long()
        return z_inner.index_select(2, idx).contiguous()

    def _target_to_inner_time(self, z_target: torch.Tensor) -&gt; torch.Tensor:
        if z_target.shape&#91;2&#93; &lt;= 1:
            return z_target

        # Common GEN3C case: inner=4x, target=8x =&gt; insert midpoint frames.
        if self._target_temporal_compression == 2 * self._inner_temporal_compression:
            b, c, t, h, w = z_target.shape
            t_inner = 2 * t - 1
            out = torch.empty(b, c, t_inner, h, w, device=z_target.device, dtype=z_target.dtype)
            out&#91;:, :, 0::2, :, :&#93; = z_target
            out&#91;:, :, 1::2, :, :&#93; = 0.5 * (z_target&#91;:, :, :-1, :, :&#93; + z_target&#91;:, :, 1:, :, :&#93;)
            return out.contiguous()

        # Generic fallback: linear index interpolation in time.
        t_target = z_target.shape&#91;2&#93;
        t_inner = 1 + (t_target - 1) * self._target_temporal_compression // self._inner_temporal_compression
        idx = torch.linspace(0, t_target - 1, t_inner, device=z_target.device)
        idx0 = idx.floor().long()
        idx1 = idx.ceil().long().clamp_max(t_target - 1)
        frac = (idx - idx0).view(1, 1, -1, 1, 1)
        z0 = z_target.index_select(2, idx0)
        z1 = z_target.index_select(2, idx1)
        return (z0 * (1.0 - frac) + z1 * frac).contiguous()

    def encode(self, x: torch.Tensor) -&gt; _TensorLatentDist:
        z_inner = self._extract_latents(self.inner.encode(x))
        z_target = self._inner_to_target_time(z_inner)
        return _TensorLatentDist(z_target)

    def decode(self, z: torch.Tensor) -&gt; torch.Tensor:
        z_inner = self._target_to_inner_time(z)
        out = self.inner.decode(z_inner)
        return out.sample if hasattr(out, &quot;sample&quot;) else out

    def enable_tiling(self) -&gt; None:
        if hasattr(self.inner, &quot;enable_tiling&quot;):
            self.inner.enable_tiling()

    def disable_tiling(self) -&gt; None:
        if hasattr(self.inner, &quot;disable_tiling&quot;):
            self.inner.disable_tiling()

    def get_latent_num_frames(self, num_pixel_frames: int) -&gt; int:
        num_pixel_frames = int(num_pixel_frames)
        if num_pixel_frames &lt;= 1:
            return 1
        return 1 + (num_pixel_frames - 1) // self._target_temporal_compression

    def get_pixel_num_frames(self, num_latent_frames: int) -&gt; int:
        num_latent_frames = int(num_latent_frames)
        if num_latent_frames &lt;= 1:
            return 1
        return (num_latent_frames - 1) * self._target_temporal_compression + 1

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

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

    @property
    def temporal_compression_ratio(self) -&gt; int:
        return self._target_temporal_compression

    @property
    def pixel_chunk_duration(self) -&gt; int:
        return self._pixel_chunk_duration

    @property
    def latent_chunk_duration(self) -&gt; int:
        return self.get_latent_num_frames(self._pixel_chunk_duration)

    @classmethod
    def from_tokenizer_checkpoint(
        cls,
        checkpoint_path: str,
        *,
        device: torch.device,
        dtype: torch.dtype,
        target_temporal_compression: int = 8,
        pixel_chunk_duration: int = 121,
    ) -&gt; &quot;AutoencoderKLGen3CTokenizer&quot;:
        from fastvideo.models.vaes.cosmos25wanvae import Cosmos25WanVAE

        inner = Cosmos25WanVAE(device=device, dtype=dtype)
        loaded = torch.load(checkpoint_path, map_location=&quot;cpu&quot;)
        if isinstance(loaded, dict):
            for key in (&quot;state_dict&quot;, &quot;model&quot;, &quot;ema&quot;, &quot;model_state_dict&quot;):
                if key in loaded and isinstance(loaded&#91;key&#93;, dict):
                    loaded = loaded&#91;key&#93;
                    break
        missing, unexpected = inner.load_state_dict(loaded, strict=False)
        if missing:
            logger.warning(
                &quot;GEN3C tokenizer VAE missing keys (%d). Example: %s&quot;,
                len(missing),
                missing&#91;:5&#93;,
            )
        if unexpected:
            logger.warning(
                &quot;GEN3C tokenizer VAE unexpected keys (%d). Example: %s&quot;,
                len(unexpected),
                unexpected&#91;:5&#93;,
            )
        return cls(
            inner,
            target_temporal_compression=target_temporal_compression,
            inner_temporal_compression=4,
            spatial_compression_factor=8,
            pixel_chunk_duration=pixel_chunk_duration,
        )

    @classmethod
    def from_jit_tokenizer(
        cls,
        tokenizer_dir: str,
        *,
        device: torch.device,
        dtype: torch.dtype,
        target_temporal_compression: int = 8,
        pixel_chunk_duration: int = 121,
    ) -&gt; &quot;AutoencoderKLGen3CTokenizer&quot;:
        encoder_path = f&quot;&#123;tokenizer_dir&#125;/encoder.jit&quot;
        decoder_path = f&quot;&#123;tokenizer_dir&#125;/decoder.jit&quot;
        mean_std_path = f&quot;&#123;tokenizer_dir&#125;/mean_std.pt&quot;
        inner = _JITGen3CTokenizerInner(
            encoder_path=encoder_path,
            decoder_path=decoder_path,
            mean_std_path=mean_std_path,
            dtype=dtype,
            device=device,
            latent_channels=16,
            latent_chunk_duration=1 + (pixel_chunk_duration - 1) // target_temporal_compression,
        )
        return cls(
            inner,
            target_temporal_compression=target_temporal_compression,
            inner_temporal_compression=target_temporal_compression,
            spatial_compression_factor=8,
            pixel_chunk_duration=pixel_chunk_duration,
        )</code></pre></details>
<h3 id="fastvideo.models.vaes.gen3c_tokenizer_vae.AutoencoderKLGen3CTokenizer.encode"><code>fastvideo.models.vaes.gen3c_tokenizer_vae.AutoencoderKLGen3CTokenizer.encode</code></h3>
<pre class="doc-signature"><code>encode(x: torch.Tensor) -&gt; _TensorLatentDist</code></pre>

<details><summary>Source</summary><pre><code>def encode(self, x: torch.Tensor) -&gt; _TensorLatentDist:
    z_inner = self._extract_latents(self.inner.encode(x))
    z_target = self._inner_to_target_time(z_inner)
    return _TensorLatentDist(z_target)</code></pre></details>
<h3 id="fastvideo.models.vaes.gen3c_tokenizer_vae.AutoencoderKLGen3CTokenizer.decode"><code>fastvideo.models.vaes.gen3c_tokenizer_vae.AutoencoderKLGen3CTokenizer.decode</code></h3>
<pre class="doc-signature"><code>decode(z: torch.Tensor) -&gt; torch.Tensor</code></pre>

<details><summary>Source</summary><pre><code>def decode(self, z: torch.Tensor) -&gt; torch.Tensor:
    z_inner = self._target_to_inner_time(z)
    out = self.inner.decode(z_inner)
    return out.sample if hasattr(out, &quot;sample&quot;) else out</code></pre></details>
<h3 id="fastvideo.models.vaes.gen3c_tokenizer_vae.AutoencoderKLGen3CTokenizer.enable_tiling"><code>fastvideo.models.vaes.gen3c_tokenizer_vae.AutoencoderKLGen3CTokenizer.enable_tiling</code></h3>
<pre class="doc-signature"><code>enable_tiling() -&gt; None</code></pre>

<details><summary>Source</summary><pre><code>def enable_tiling(self) -&gt; None:
    if hasattr(self.inner, &quot;enable_tiling&quot;):
        self.inner.enable_tiling()</code></pre></details>
<h3 id="fastvideo.models.vaes.gen3c_tokenizer_vae.AutoencoderKLGen3CTokenizer.disable_tiling"><code>fastvideo.models.vaes.gen3c_tokenizer_vae.AutoencoderKLGen3CTokenizer.disable_tiling</code></h3>
<pre class="doc-signature"><code>disable_tiling() -&gt; None</code></pre>

<details><summary>Source</summary><pre><code>def disable_tiling(self) -&gt; None:
    if hasattr(self.inner, &quot;disable_tiling&quot;):
        self.inner.disable_tiling()</code></pre></details>
<h3 id="fastvideo.models.vaes.gen3c_tokenizer_vae.AutoencoderKLGen3CTokenizer.get_latent_num_frames"><code>fastvideo.models.vaes.gen3c_tokenizer_vae.AutoencoderKLGen3CTokenizer.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:
    num_pixel_frames = int(num_pixel_frames)
    if num_pixel_frames &lt;= 1:
        return 1
    return 1 + (num_pixel_frames - 1) // self._target_temporal_compression</code></pre></details>
<h3 id="fastvideo.models.vaes.gen3c_tokenizer_vae.AutoencoderKLGen3CTokenizer.get_pixel_num_frames"><code>fastvideo.models.vaes.gen3c_tokenizer_vae.AutoencoderKLGen3CTokenizer.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:
    num_latent_frames = int(num_latent_frames)
    if num_latent_frames &lt;= 1:
        return 1
    return (num_latent_frames - 1) * self._target_temporal_compression + 1</code></pre></details>
<h3 id="fastvideo.models.vaes.gen3c_tokenizer_vae.AutoencoderKLGen3CTokenizer.from_tokenizer_checkpoint"><code>fastvideo.models.vaes.gen3c_tokenizer_vae.AutoencoderKLGen3CTokenizer.from_tokenizer_checkpoint</code></h3>
<pre class="doc-signature"><code>from_tokenizer_checkpoint(checkpoint_path: str, *, device: torch.device, dtype: torch.dtype, target_temporal_compression: int = 8, pixel_chunk_duration: int = 121) -&gt; &#x27;AutoencoderKLGen3CTokenizer&#x27;</code></pre>

<details><summary>Source</summary><pre><code>@classmethod
def from_tokenizer_checkpoint(
    cls,
    checkpoint_path: str,
    *,
    device: torch.device,
    dtype: torch.dtype,
    target_temporal_compression: int = 8,
    pixel_chunk_duration: int = 121,
) -&gt; &quot;AutoencoderKLGen3CTokenizer&quot;:
    from fastvideo.models.vaes.cosmos25wanvae import Cosmos25WanVAE

    inner = Cosmos25WanVAE(device=device, dtype=dtype)
    loaded = torch.load(checkpoint_path, map_location=&quot;cpu&quot;)
    if isinstance(loaded, dict):
        for key in (&quot;state_dict&quot;, &quot;model&quot;, &quot;ema&quot;, &quot;model_state_dict&quot;):
            if key in loaded and isinstance(loaded&#91;key&#93;, dict):
                loaded = loaded&#91;key&#93;
                break
    missing, unexpected = inner.load_state_dict(loaded, strict=False)
    if missing:
        logger.warning(
            &quot;GEN3C tokenizer VAE missing keys (%d). Example: %s&quot;,
            len(missing),
            missing&#91;:5&#93;,
        )
    if unexpected:
        logger.warning(
            &quot;GEN3C tokenizer VAE unexpected keys (%d). Example: %s&quot;,
            len(unexpected),
            unexpected&#91;:5&#93;,
        )
    return cls(
        inner,
        target_temporal_compression=target_temporal_compression,
        inner_temporal_compression=4,
        spatial_compression_factor=8,
        pixel_chunk_duration=pixel_chunk_duration,
    )</code></pre></details>
<h3 id="fastvideo.models.vaes.gen3c_tokenizer_vae.AutoencoderKLGen3CTokenizer.from_jit_tokenizer"><code>fastvideo.models.vaes.gen3c_tokenizer_vae.AutoencoderKLGen3CTokenizer.from_jit_tokenizer</code></h3>
<pre class="doc-signature"><code>from_jit_tokenizer(tokenizer_dir: str, *, device: torch.device, dtype: torch.dtype, target_temporal_compression: int = 8, pixel_chunk_duration: int = 121) -&gt; &#x27;AutoencoderKLGen3CTokenizer&#x27;</code></pre>

<details><summary>Source</summary><pre><code>@classmethod
def from_jit_tokenizer(
    cls,
    tokenizer_dir: str,
    *,
    device: torch.device,
    dtype: torch.dtype,
    target_temporal_compression: int = 8,
    pixel_chunk_duration: int = 121,
) -&gt; &quot;AutoencoderKLGen3CTokenizer&quot;:
    encoder_path = f&quot;&#123;tokenizer_dir&#125;/encoder.jit&quot;
    decoder_path = f&quot;&#123;tokenizer_dir&#125;/decoder.jit&quot;
    mean_std_path = f&quot;&#123;tokenizer_dir&#125;/mean_std.pt&quot;
    inner = _JITGen3CTokenizerInner(
        encoder_path=encoder_path,
        decoder_path=decoder_path,
        mean_std_path=mean_std_path,
        dtype=dtype,
        device=device,
        latent_channels=16,
        latent_chunk_duration=1 + (pixel_chunk_duration - 1) // target_temporal_compression,
    )
    return cls(
        inner,
        target_temporal_compression=target_temporal_compression,
        inner_temporal_compression=target_temporal_compression,
        spatial_compression_factor=8,
        pixel_chunk_duration=pixel_chunk_duration,
    )</code></pre></details>
</div>
