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

minimax_h3_video_vae

MiniMax-H3 video VAE for the Apple Silicon MLX runtime.

Faithful MLX port of fastvideo/models/vaes/minimax_h3_video.py (itself parity-validated against the official diffusers implementation):

  • Encoder: causal 3D CNN — causal temporal padding, reflect spatial padding, per-frame GroupNorm, residual blocks, spatial/temporal downsampling, then a 1x1x1 quant_conv producing mean/logvar channels.
  • Decoder: 1x1x1 post_quant_conv, then a 36-layer ViT — per-head Q/K RMSNorm, three-axis rotary embedding (theta 100, rotary width 48 of 64 head dims), register tokens plus a final zero class token, SwiGLU feed-forward, residual scale vectors, FP32 norm accumulation, final LayerNorm, output projection, and channel-major unpatchification (temporal patch 4, spatial patch 16x16) — with the exact clip chunking (clip_length=17, token_drop=3), frame pre-padding, overlap blending, tail trimming, and optional spatial tiling of the released model.

Released weights are FP32; the loader streams shards so peak memory stays bounded, and can optionally store bf16/fp16 after callers have measured the dtype drift against the FP32 acceptance gate.

Production code here never imports PyTorch. Torch parity references live in the tests under tests/local_tests/minimax_h3/.

fastvideo.mlx_runtime.minimax_h3_video_vae.init_logger

init_logger(name: str) -> _FastvideoLogger

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.

fastvideo.mlx_runtime.minimax_h3_video_vae.QuantizedWeight

QuantizedWeight(weight: Any, scales: Any, biases: Any, mode: str, group_size: int, bits: int, output_scale: float = 1.0)

A packed linear weight; output_scale undoes the pre-quantization tensor scale.

fastvideo.mlx_runtime.minimax_h3_video_vae.QuantizedWeight.matmul

matmul(x)

fastvideo.mlx_runtime.minimax_h3_video_vae.QuantizedWeight.dequantize

dequantize(dtype = mx.float32)

fastvideo.mlx_runtime.minimax_h3_video_vae.is_block_linear_weight

is_block_linear_weight(key: str) -> bool

True for the weight of one of the 216 decoder transformer-block linears.

fastvideo.mlx_runtime.minimax_h3_video_vae.packed_nvfp4_weight

packed_nvfp4_weight(packed_weight, inv_scale, global_sf) -> QuantizedWeight

One linear of a fastvideo_h3_decoder_nvfp4_packed_v1 file as an MLX NVFP4 weight.

fastvideo.mlx_runtime.minimax_h3_video_vae.quantize_weight

quantize_weight(weight, mode: str) -> QuantizedWeight

Quantize a dense (out, in) matrix; mode is a key of QUANT_MODES.

fastvideo.mlx_runtime.minimax_h3_video_vae.MiniMaxH3VideoVAEConfigView

MiniMaxH3VideoVAEConfigView(in_channels: int = 3, out_channels: int = 3, latent_channels: int = 24, block_out_channels: tuple[int, ...] = (128, 256, 256, 512, 512, 1024), layers_per_block: int = 2, spatial_downsample_factors: tuple[int, ...] = (2, 2, 2, 2, 1, 1), temporal_downsample_factors: tuple[int, ...] = (1, 2, 2, 1, 1, 1), norm_num_groups: int = 32, norm_eps: float = 1e-06, decoder_num_layers: int = 36, decoder_num_attention_heads: int = 32, decoder_attention_head_dim: int = 64, decoder_num_register_tokens: int = 4, decoder_ffn_mult: int = 4, decoder_rope_theta: float = 100.0, decoder_rope_dim_ratio: float = 0.75, decoder_norm_eps: float = 1e-05, clip_length: int = 17, token_drop: int = 3, latents_mean: tuple[float, ...] | None = None, latents_std: tuple[float, ...] | None = None)

Architecture constants (defaults mirror the released vae/config.json).

Source
@dataclass(frozen=True)
class MiniMaxH3VideoVAEConfigView:
    """Architecture constants (defaults mirror the released vae/config.json)."""
in_channels: int = 3
out_channels: int = 3
latent_channels: int = 24
block_out_channels: tuple[int, ...] = (128, 256, 256, 512, 512, 1024)
layers_per_block: int = 2
spatial_downsample_factors: tuple[int, ...] = (2, 2, 2, 2, 1, 1)
temporal_downsample_factors: tuple[int, ...] = (1, 2, 2, 1, 1, 1)
norm_num_groups: int = 32
norm_eps: float = 1e-6
decoder_num_layers: int = 36
decoder_num_attention_heads: int = 32
decoder_attention_head_dim: int = 64
decoder_num_register_tokens: int = 4
decoder_ffn_mult: int = 4
decoder_rope_theta: float = 100.0
decoder_rope_dim_ratio: float = 0.75
decoder_norm_eps: float = 1e-5
clip_length: int = 17
token_drop: int = 3
latents_mean: tuple[float, ...] | None = None
latents_std: tuple[float, ...] | None = None

@classmethod
def from_vae_dir(cls, vae_dir: str | Path) -> MiniMaxH3VideoVAEConfigView:
    return cls.from_dict(json.loads((Path(vae_dir) / "config.json").read_text()))

@classmethod
def from_dict(cls, config: dict[str, Any]) -> MiniMaxH3VideoVAEConfigView:
    """Build from a ``vae/config.json`` dict (also LightVAE's embedded ``teacher_config``)."""
    latents_mean = tuple(float(v) for v in config.get("latents_mean", ()))
    latents_std = tuple(float(v) for v in config.get("latents_std", ()))
    return cls(
        in_channels=int(config["in_channels"]),
        out_channels=int(config["out_channels"]),
        latent_channels=int(config["latent_channels"]),
        block_out_channels=tuple(int(v) for v in config["block_out_channels"]),
        layers_per_block=int(config["layers_per_block"]),
        spatial_downsample_factors=tuple(int(v) for v in config["spatial_downsample_factors"]),
        temporal_downsample_factors=tuple(int(v) for v in config["temporal_downsample_factors"]),
        norm_num_groups=int(config["norm_num_groups"]),
        norm_eps=float(config["norm_eps"]),
        decoder_num_layers=int(config["decoder_num_layers"]),
        decoder_num_attention_heads=int(config["decoder_num_attention_heads"]),
        decoder_attention_head_dim=int(config["decoder_attention_head_dim"]),
        decoder_num_register_tokens=int(config["decoder_num_register_tokens"]),
        decoder_ffn_mult=int(config.get("decoder_ffn_mult", 4)),
        decoder_rope_theta=float(config["decoder_rope_theta"]),
        decoder_rope_dim_ratio=float(config["decoder_rope_dim_ratio"]),
        decoder_norm_eps=float(config["decoder_norm_eps"]),
        clip_length=int(config["clip_length"]),
        token_drop=int(config["token_drop"]),
        latents_mean=latents_mean or None,
        latents_std=latents_std or None,
    )

@property
def spatial_compression_ratio(self) -> int:
    return math.prod(self.spatial_downsample_factors)

@property
def temporal_compression_ratio(self) -> int:
    return math.prod(self.temporal_downsample_factors)

@property
def tokens_chunk_size(self) -> int:
    """Latent frames decoded per clip chunk (ceil(clip_length / ratio))."""
    return math.ceil(self.clip_length / self.temporal_compression_ratio)

@property
def token_overlap(self) -> int:
    return (-self.token_drop) % self.tokens_chunk_size

@property
def frame_pre_padding(self) -> int:
    return (-self.clip_length) % self.temporal_compression_ratio

@property
def frame_overlap(self) -> int:
    return max(self.token_overlap * self.temporal_compression_ratio - self.frame_pre_padding, 0)</code></pre></details>

fastvideo.mlx_runtime.minimax_h3_video_vae.MiniMaxH3VideoVAEConfigView.from_vae_dir

from_vae_dir(vae_dir: str | Path) -> MiniMaxH3VideoVAEConfigView
Source
@classmethod
def from_vae_dir(cls, vae_dir: str | Path) -> MiniMaxH3VideoVAEConfigView:
    return cls.from_dict(json.loads((Path(vae_dir) / "config.json").read_text()))

fastvideo.mlx_runtime.minimax_h3_video_vae.MiniMaxH3VideoVAEConfigView.from_dict

from_dict(config: dict[str, Any]) -> MiniMaxH3VideoVAEConfigView

Build from a vae/config.json dict (also LightVAE's embedded teacher_config).

Source
@classmethod
def from_dict(cls, config: dict[str, Any]) -> MiniMaxH3VideoVAEConfigView:
    """Build from a ``vae/config.json`` dict (also LightVAE's embedded ``teacher_config``)."""
    latents_mean = tuple(float(v) for v in config.get("latents_mean", ()))
    latents_std = tuple(float(v) for v in config.get("latents_std", ()))
    return cls(
        in_channels=int(config["in_channels"]),
        out_channels=int(config["out_channels"]),
        latent_channels=int(config["latent_channels"]),
        block_out_channels=tuple(int(v) for v in config["block_out_channels"]),
        layers_per_block=int(config["layers_per_block"]),
        spatial_downsample_factors=tuple(int(v) for v in config["spatial_downsample_factors"]),
        temporal_downsample_factors=tuple(int(v) for v in config["temporal_downsample_factors"]),
        norm_num_groups=int(config["norm_num_groups"]),
        norm_eps=float(config["norm_eps"]),
        decoder_num_layers=int(config["decoder_num_layers"]),
        decoder_num_attention_heads=int(config["decoder_num_attention_heads"]),
        decoder_attention_head_dim=int(config["decoder_attention_head_dim"]),
        decoder_num_register_tokens=int(config["decoder_num_register_tokens"]),
        decoder_ffn_mult=int(config.get("decoder_ffn_mult", 4)),
        decoder_rope_theta=float(config["decoder_rope_theta"]),
        decoder_rope_dim_ratio=float(config["decoder_rope_dim_ratio"]),
        decoder_norm_eps=float(config["decoder_norm_eps"]),
        clip_length=int(config["clip_length"]),
        token_drop=int(config["token_drop"]),
        latents_mean=latents_mean or None,
        latents_std=latents_std or None,
    )

fastvideo.mlx_runtime.minimax_h3_video_vae.MiniMaxH3VideoVAEConfigView.tokens_chunk_size

tokens_chunk_size: int

Latent frames decoded per clip chunk (ceil(clip_length / ratio)).

Source
def tokens_chunk_size(self) -> int:
    """Latent frames decoded per clip chunk (ceil(clip_length / ratio))."""
    return math.ceil(self.clip_length / self.temporal_compression_ratio)

fastvideo.mlx_runtime.minimax_h3_video_vae.MLXMiniMaxH3VideoVAE

MLXMiniMaxH3VideoVAE(weights: dict[str, Any], config: MiniMaxH3VideoVAEConfigView, *, has_encoder: bool = True, has_decoder: bool = True)

Encoder + decoder for the released MiniMax-H3 video VAE.

Source
class MLXMiniMaxH3VideoVAE:
    """Encoder + decoder for the released MiniMax-H3 video VAE."""
def __init__(self,
             weights: dict&#91;str, Any&#93;,
             config: MiniMaxH3VideoVAEConfigView,
             *,
             has_encoder: bool = True,
             has_decoder: bool = True):
    self.weights = weights
    self.config = config
    self.has_encoder = has_encoder
    self.has_decoder = has_decoder
    self.latent_channels = config.latent_channels
    self.spatial_compression_ratio = config.spatial_compression_ratio
    self.temporal_compression_ratio = config.temporal_compression_ratio
    self.num_heads = config.decoder_num_attention_heads
    self.head_dim = config.decoder_attention_head_dim
    self.dim = self.num_heads * self.head_dim
    self.rotary_dim = int(self.head_dim * config.decoder_rope_dim_ratio)
    self._blocks: list&#91;dict&#91;str, Any&#93;&#93; | None = None
    arch = config
    mean = arch.latents_mean if arch.latents_mean is not None else &#91;0.0&#93; * arch.latent_channels
    std = arch.latents_std if arch.latents_std is not None else &#91;1.0&#93; * arch.latent_channels
    self._latents_mean = np.asarray(mean, dtype=np.float32).reshape(1, -1, 1, 1, 1)
    self._latents_std = np.asarray(std, dtype=np.float32).reshape(1, -1, 1, 1, 1)
    self._pixel_mean = np.asarray(PIXEL_MEAN, dtype=np.float32).reshape(1, -1, 1, 1, 1)
    self._pixel_std = np.asarray(PIXEL_STD, dtype=np.float32).reshape(1, -1, 1, 1, 1)

# -- normalization -------------------------------------------------

def normalize_latents(self, latents):
    return (latents - mx.array(self._latents_mean)) / mx.array(self._latents_std)

def denormalize_latents(self, latents):
    return latents * mx.array(self._latents_std) + mx.array(self._latents_mean)

def normalize_pixels(self, pixels):
    return (pixels - mx.array(self._pixel_mean)) / mx.array(self._pixel_std)

def denormalize_pixels(self, sample):
    return sample * mx.array(self._pixel_std) + mx.array(self._pixel_mean)

# -- encoder ---------------------------------------------------------

def _encode_clip(self, x):
    &quot;&quot;&quot;(N, 3, T, H, W) pixels -&gt; (N, 2C, T&#x27;, H&#x27;, W&#x27;) moments.&quot;&quot;&quot;
    w = self.weights
    cfg = self.config
    x = x.astype(w&#91;&quot;encoder.conv_in.weight&quot;&#93;.dtype)  # compute in the storage dtype
    x = _causal_conv3d(x,
                       w&#91;&quot;encoder.conv_in.weight&quot;&#93;,
                       w&#91;&quot;encoder.conv_in.bias&quot;&#93;,
                       spatial_padding=1,
                       temporal_padding=2)
    for index in range(len(cfg.block_out_channels)):
        td = cfg.temporal_downsample_factors&#91;index&#93;
        sd = cfg.spatial_downsample_factors&#91;index&#93;
        for layer in range(cfg.layers_per_block):
            x = _resnet_block(w,
                              f&quot;encoder.down_blocks.&#123;index&#125;.resnets.&#123;layer&#125;&quot;,
                              x,
                              num_groups=cfg.norm_num_groups,
                              eps=cfg.norm_eps)
            mx.eval(x)  # bound the lazy graph: full-resolution activations are the encoder&#x27;s peak
        if td * sd &gt; 1:
            if sd == 2:
                x = _reflect_pad_axis(x, 3, 0, 1)
                x = _reflect_pad_axis(x, 4, 0, 1)
            x = _causal_conv3d(
                x,
                w&#91;f&quot;encoder.down_blocks.&#123;index&#125;.downsamplers.0.conv.weight&quot;&#93;,
                w&#91;f&quot;encoder.down_blocks.&#123;index&#125;.downsamplers.0.conv.bias&quot;&#93;,
                stride=(td, sd, sd),
                temporal_padding=2,
            )
            mx.eval(x)
    x = _group_norm_per_frame(x,
                              w&#91;&quot;encoder.norm_out.weight&quot;&#93;,
                              w&#91;&quot;encoder.norm_out.bias&quot;&#93;,
                              num_groups=cfg.norm_num_groups,
                              eps=cfg.norm_eps,
                              silu=True)
    x = _causal_conv3d(x,
                       w&#91;&quot;encoder.conv_out.weight&quot;&#93;,
                       w&#91;&quot;encoder.conv_out.bias&quot;&#93;,
                       spatial_padding=1,
                       temporal_padding=2)
    x = _causal_conv3d(x, w&#91;&quot;quant_conv.weight&quot;&#93;, w&#91;&quot;quant_conv.bias&quot;&#93;)
    return x.astype(mx.float32)

def encode(self, pixels):
    &quot;&quot;&quot;Normalized pixels (1, 3, T, H, W) -&gt; (mean, logvar) each (1, C, T&#x27;, H&#x27;, W&#x27;).

    Pads the clip to ``clip_length`` and drops ``token_drop`` trailing
    moment frames exactly like the reference ``_encode``.
    &quot;&quot;&quot;
    if not self.has_encoder:
        raise RuntimeError(&quot;This MLX H3 video VAE was loaded without encoder weights.&quot;)
    cfg = self.config
    num_frames = pixels.shape&#91;2&#93;
    if num_frames % cfg.clip_length != 0:
        pad_frames = (-num_frames) % cfg.clip_length
        tail = mx.repeat(pixels&#91;:, :, -1:&#93;, pad_frames, axis=2)
        pixels = mx.concatenate(&#91;pixels, tail&#93;, axis=2)
    moment_chunks: list&#91;Any&#93; = &#91;&#93;
    for index in range(pixels.shape&#91;2&#93; // cfg.clip_length):
        clip = pixels&#91;:, :, index * cfg.clip_length:(index + 1) * cfg.clip_length&#93;
        moment_chunks.append(self._encode_clip(clip))
    moments = mx.concatenate(moment_chunks, axis=2)
    if cfg.token_drop &gt; 0:
        moments = moments&#91;:, :, :-cfg.token_drop&#93;
    mean, logvar = mx.split(moments, 2, axis=1)
    logvar = mx.clip(logvar, -30.0, 20.0)
    return mean, logvar

def encode_keyframe(self, pixels):
    &quot;&quot;&quot;Single-frame conditioning encode without chunk padding.&quot;&quot;&quot;
    if pixels.shape&#91;2&#93; != 1:
        raise ValueError(f&quot;encode_keyframe expects exactly one frame, got &#123;pixels.shape&#125;.&quot;)
    moments = self._encode_clip(pixels)
    mean, logvar = mx.split(moments, 2, axis=1)
    logvar = mx.clip(logvar, -30.0, 20.0)
    return mean, logvar

@staticmethod
def sample_posterior(mean, logvar, noise):
    &quot;&quot;&quot;Reparameterization with an explicit noise array (deterministic parity).&quot;&quot;&quot;
    return mean + mx.exp(0.5 * logvar) * noise

# -- decoder ---------------------------------------------------------

def _decoder_blocks(self) -&gt; list&#91;dict&#91;str, Any&#93;&#93;:
    &quot;&quot;&quot;Split the flat weight dict into per-block dicts once.&quot;&quot;&quot;
    if self._blocks is None:
        blocks: list&#91;dict&#91;str, Any&#93;&#93; = &#91;&#123;&#125; for _ in range(self.config.decoder_num_layers)&#93;
        prefix = &quot;decoder.transformer_blocks.&quot;
        for key, value in self.weights.items():
            if key.startswith(prefix):
                index, sub = key&#91;len(prefix):&#93;.split(&quot;.&quot;, 1)
                blocks&#91;int(index)&#93;&#91;sub&#93; = value
        self._blocks = blocks
    return self._blocks

def _decode_clip(self, z):
    &quot;&quot;&quot;(1, C, T_tokens, H&#x27;, W&#x27;) latents -&gt; (1, 3, T_tokens*4, H, W) pixels.&quot;&quot;&quot;
    w = self.weights
    cfg = self.config
    z = z.astype(w&#91;&quot;post_quant_conv.weight&quot;&#93;.dtype)  # compute in the storage dtype
    z = _causal_conv3d(z, w&#91;&quot;post_quant_conv.weight&quot;&#93;, w&#91;&quot;post_quant_conv.bias&quot;&#93;)
    batch, channels, num_frames, height, width = z.shape
    # (B, C, T, H, W) -&gt; (B*T*H*W, C): sequence-major tokens.
    tokens = _ct(z, 0, 2, 3, 4, 1).reshape(-1, channels)
    tokens = _linear(tokens, w&#91;&quot;decoder.proj_in.weight&quot;&#93;, w&#91;&quot;decoder.proj_in.bias&quot;&#93;)
    num_patches = tokens.shape&#91;0&#93;

    num_reg = self.config.decoder_num_register_tokens
    register = mx.broadcast_to(w&#91;&quot;decoder.register_tokens&quot;&#93;, (batch, num_reg, self.dim)).reshape(-1, self.dim)
    cls_token = mx.zeros((batch, self.dim), dtype=tokens.dtype)
    hidden = mx.concatenate(&#91;tokens, register, cls_token&#93;, axis=0)&#91;:, None, :&#93;  # (S, B, D)

    grids = &#91;2.0 * (mx.arange(0.5, size, dtype=mx.float32) / size) - 1.0 for size in (num_frames, height, width)&#93;
    mesh_t, mesh_h, mesh_w = mx.meshgrid(grids&#91;0&#93;, grids&#91;1&#93;, grids&#91;2&#93;, indexing=&quot;ij&quot;)
    position_ids = mx.stack(&#91;mesh_t.reshape(-1), mesh_h.reshape(-1), mesh_w.reshape(-1)&#93;, axis=-1)
    suffix = mx.zeros((batch * (cfg.decoder_num_register_tokens + 1), 3), dtype=position_ids.dtype)
    position_ids = mx.concatenate(&#91;position_ids, suffix&#93;, axis=0)  # (S, 3)
    cos, sin = _rotary_cos_sin(position_ids, rotary_dim=self.rotary_dim, theta=cfg.decoder_rope_theta)

    for block in self._decoder_blocks():
        hidden = _transformer_block(
            block,
            hidden,
            block&#91;&quot;scale1&quot;&#93;,
            block&#91;&quot;scale2&quot;&#93;,
            cos,
            sin,
            num_heads=self.num_heads,
            head_dim=self.head_dim,
            norm_eps=cfg.decoder_norm_eps,
            qk_norm_eps=cfg.decoder_norm_eps,
        )
        mx.eval(hidden)  # per-block sync (36 blocks build a huge lazy graph)
    hidden = _layer_norm(hidden, w&#91;&quot;decoder.norm_out.weight&quot;&#93;, w&#91;&quot;decoder.norm_out.bias&quot;&#93;, eps=cfg.decoder_norm_eps)
    output = _linear(hidden, w&#91;&quot;decoder.proj_out.weight&quot;&#93;, w&#91;&quot;decoder.proj_out.bias&quot;&#93;)&#91;:num_patches&#93;

    patch_t = cfg.temporal_compression_ratio
    patch_h = patch_w = cfg.spatial_compression_ratio
    output = output.reshape(num_patches, cfg.out_channels, patch_t, patch_h, patch_w)
    output = output.reshape(batch, num_frames, height, width, cfg.out_channels, patch_t, patch_h, patch_w)
    output = _ct(output, 0, 4, 1, 5, 2, 6, 3, 7)
    return output.reshape(batch, cfg.out_channels, num_frames * patch_t, height * patch_h, width * patch_w)

def _split_tiles(self, length: int, tile_size: int, min_overlap: int):
    if tile_size &gt;= length:
        return &#91;0&#93;, &#91;length&#93;, &#91;&#93;
    num_tiles = math.ceil(length / tile_size)
    while tile_size * num_tiles - min_overlap * (num_tiles - 1) - length &lt; 0:
        num_tiles += 1
    overlaps = &#91;min_overlap&#93; * (num_tiles - 1)
    remaining = tile_size * num_tiles - sum(overlaps) - length
    for index in range(remaining // self.spatial_compression_ratio):
        overlaps&#91;index % (num_tiles - 1)&#93; += self.spatial_compression_ratio
    starts = &#91;0&#93;
    for index in range(num_tiles - 1):
        starts.append(starts&#91;-1&#93; + tile_size - overlaps&#91;index&#93;)
    return starts, &#91;tile_size&#93; * num_tiles, overlaps

@staticmethod
def _blend(a, b, blend_extent: int, dim: int):
    blend_extent = min(a.shape&#91;dim&#93;, b.shape&#91;dim&#93;, blend_extent)
    positions = mx.arange(blend_extent, dtype=b.dtype)
    shape = &#91;1&#93; * a.ndim
    shape&#91;dim&#93; = blend_extent
    weight_a = (1 - positions / blend_extent).reshape(shape)
    weight_b = (positions / blend_extent).reshape(shape)
    slice_a = &#91;slice(None)&#93; * a.ndim
    slice_a&#91;dim&#93; = slice(-blend_extent, None)
    slice_b = &#91;slice(None)&#93; * b.ndim
    slice_b&#91;dim&#93; = slice(0, blend_extent)
    blended = a&#91;tuple(slice_a)&#93; * weight_a + b&#91;tuple(slice_b)&#93; * weight_b
    if blend_extent == b.shape&#91;dim&#93;:
        return blended
    rest = &#91;slice(None)&#93; * b.ndim
    rest&#91;dim&#93; = slice(blend_extent, None)
    return mx.concatenate(&#91;blended, b&#91;tuple(rest)&#93;&#93;, axis=dim)

def _stitch_tiles(self, tiles, height_overlaps, width_overlaps):
    result_rows = &#91;&#93;
    for row_index, row in enumerate(tiles):
        result_row = &#91;&#93;
        for column_index, tile in enumerate(row):
            if row_index &gt; 0:
                tile = self._blend(tiles&#91;row_index - 1&#93;&#91;column_index&#93;, tile, height_overlaps&#91;row_index - 1&#93;, -2)
            if column_index &gt; 0:
                tile = self._blend(row&#91;column_index - 1&#93;, tile, width_overlaps&#91;column_index - 1&#93;, -1)
            if row_index &lt; len(tiles) - 1:
                tile = tile&#91;..., :-height_overlaps&#91;row_index&#93;, :&#93;
            if column_index &lt; len(row) - 1:
                tile = tile&#91;..., :, :-width_overlaps&#91;column_index&#93;&#93;
            result_row.append(tile)
        result_rows.append(mx.concatenate(result_row, axis=-1))
    return mx.concatenate(result_rows, axis=-2)

def decode_clip_tiled(self,
                      z,
                      tile_sample_min_height: int,
                      tile_sample_min_width: int,
                      tile_sample_min_overlap_height: int = 64,
                      tile_sample_min_overlap_width: int = 64):
    &quot;&quot;&quot;One clip through the decoder with spatial tiling (memory bounded).&quot;&quot;&quot;
    height = z.shape&#91;-2&#93; * self.spatial_compression_ratio
    width = z.shape&#91;-1&#93; * self.spatial_compression_ratio
    y_starts, y_lengths, y_overlaps = self._split_tiles(height, tile_sample_min_height,
                                                        tile_sample_min_overlap_height)
    x_starts, x_lengths, x_overlaps = self._split_tiles(width, tile_sample_min_width, tile_sample_min_overlap_width)
    ratio = self.spatial_compression_ratio
    rows = &#91;&#93;
    for y_start, y_length in zip(y_starts, y_lengths, strict=False):
        row = &#91;&#93;
        for x_start, x_length in zip(x_starts, x_lengths, strict=False):
            tile = z&#91;..., y_start // ratio:y_start // ratio + y_length // ratio,
                     x_start // ratio:x_start // ratio + x_length // ratio&#93;
            row.append(self._decode_clip(tile))
        rows.append(row)
    return self._stitch_tiles(rows, y_overlaps, x_overlaps)

def decode(self,
           z,
           *,
           tiled: bool = True,
           tile_sample_min_height: int = 256,
           tile_sample_min_width: int = 256,
           tile_sample_min_overlap_height: int = 64,
           tile_sample_min_overlap_width: int = 64):
    &quot;&quot;&quot;Chunked decode of normalized latents (1, C, T_lat, H&#x27;, W&#x27;) -&gt; (1, 3, T, H, W).&quot;&quot;&quot;
    if not self.has_decoder:
        raise RuntimeError(&quot;This MLX H3 video VAE was loaded without decoder weights.&quot;)
    cfg = self.config
    tokens_chunk_size = cfg.tokens_chunk_size
    token_drop = cfg.token_drop
    temporal_ratio = self.temporal_compression_ratio
    chunk_num_frames = tokens_chunk_size * temporal_ratio
    num_tokens = z.shape&#91;2&#93; + token_drop
    pad_tokens = (-num_tokens) % tokens_chunk_size
    num_chunks = (num_tokens + pad_tokens) // tokens_chunk_size - int(token_drop &gt; 0)
    if pad_tokens &gt; 0:
        tail = mx.repeat(z&#91;:, :, -1:&#93;, pad_tokens, axis=2)
        z = mx.concatenate(&#91;z, tail&#93;, axis=2)

    def decode_one(chunk):
        if tiled:
            return self.decode_clip_tiled(chunk, tile_sample_min_height, tile_sample_min_width,
                                          tile_sample_min_overlap_height, tile_sample_min_overlap_width)
        return self._decode_clip(chunk)

    decoded_chunks = &#91;&#93;
    overlap = None
    for index in range(num_chunks):
        start = index * tokens_chunk_size
        clip = decode_one(z&#91;:, :, start:start + tokens_chunk_size + cfg.token_overlap&#93;)
        for overlap_index in range(int(token_drop &gt; 0) + 1):
            frame_start = overlap_index * chunk_num_frames
            chunk = clip&#91;:, :, frame_start:frame_start + chunk_num_frames&#93;
            chunk = chunk&#91;:, :, cfg.frame_pre_padding:&#93;
            if overlap_index == 0:
                if overlap is not None:
                    chunk = self._blend(overlap, chunk, cfg.frame_overlap, dim=-3)
                mx.eval(chunk)  # materialize per clip; keeps the lazy graph bounded
                decoded_chunks.append(chunk)
            else:
                overlap = chunk
                mx.eval(overlap)
    if overlap is not None:
        decoded_chunks.append(overlap)
    decoded = mx.concatenate(decoded_chunks, axis=2)

    if pad_tokens &gt; 0:
        intra_tail = cfg.clip_length % temporal_ratio
        num_tokens_before_pad = z.shape&#91;2&#93; - pad_tokens
        pad_frames = sum(intra_tail if intra_tail and (num_tokens_before_pad + offset) %
                         tokens_chunk_size == 0 else temporal_ratio for offset in range(pad_tokens))
        decoded = decoded&#91;:, :, :-pad_frames&#93;
    return decoded</code></pre></details>

fastvideo.mlx_runtime.minimax_h3_video_vae.MLXMiniMaxH3VideoVAE.normalize_latents

normalize_latents(latents)
Source
def normalize_latents(self, latents):
    return (latents - mx.array(self._latents_mean)) / mx.array(self._latents_std)

fastvideo.mlx_runtime.minimax_h3_video_vae.MLXMiniMaxH3VideoVAE.denormalize_latents

denormalize_latents(latents)
Source
def denormalize_latents(self, latents):
    return latents * mx.array(self._latents_std) + mx.array(self._latents_mean)

fastvideo.mlx_runtime.minimax_h3_video_vae.MLXMiniMaxH3VideoVAE.normalize_pixels

normalize_pixels(pixels)
Source
def normalize_pixels(self, pixels):
    return (pixels - mx.array(self._pixel_mean)) / mx.array(self._pixel_std)

fastvideo.mlx_runtime.minimax_h3_video_vae.MLXMiniMaxH3VideoVAE.denormalize_pixels

denormalize_pixels(sample)
Source
def denormalize_pixels(self, sample):
    return sample * mx.array(self._pixel_std) + mx.array(self._pixel_mean)

fastvideo.mlx_runtime.minimax_h3_video_vae.MLXMiniMaxH3VideoVAE.encode

encode(pixels)

Normalized pixels (1, 3, T, H, W) -> (mean, logvar) each (1, C, T', H', W').

Pads the clip to clip_length and drops token_drop trailing moment frames exactly like the reference _encode.

Source
def encode(self, pixels):
    """Normalized pixels (1, 3, T, H, W) -> (mean, logvar) each (1, C, T', H', W').
Pads the clip to ``clip_length`` and drops ``token_drop`` trailing
moment frames exactly like the reference ``_encode``.
&quot;&quot;&quot;
if not self.has_encoder:
    raise RuntimeError(&quot;This MLX H3 video VAE was loaded without encoder weights.&quot;)
cfg = self.config
num_frames = pixels.shape&#91;2&#93;
if num_frames % cfg.clip_length != 0:
    pad_frames = (-num_frames) % cfg.clip_length
    tail = mx.repeat(pixels&#91;:, :, -1:&#93;, pad_frames, axis=2)
    pixels = mx.concatenate(&#91;pixels, tail&#93;, axis=2)
moment_chunks: list&#91;Any&#93; = &#91;&#93;
for index in range(pixels.shape&#91;2&#93; // cfg.clip_length):
    clip = pixels&#91;:, :, index * cfg.clip_length:(index + 1) * cfg.clip_length&#93;
    moment_chunks.append(self._encode_clip(clip))
moments = mx.concatenate(moment_chunks, axis=2)
if cfg.token_drop &gt; 0:
    moments = moments&#91;:, :, :-cfg.token_drop&#93;
mean, logvar = mx.split(moments, 2, axis=1)
logvar = mx.clip(logvar, -30.0, 20.0)
return mean, logvar</code></pre></details>

fastvideo.mlx_runtime.minimax_h3_video_vae.MLXMiniMaxH3VideoVAE.encode_keyframe

encode_keyframe(pixels)

Single-frame conditioning encode without chunk padding.

Source
def encode_keyframe(self, pixels):
    """Single-frame conditioning encode without chunk padding."""
    if pixels.shape[2] != 1:
        raise ValueError(f"encode_keyframe expects exactly one frame, got {pixels.shape}.")
    moments = self._encode_clip(pixels)
    mean, logvar = mx.split(moments, 2, axis=1)
    logvar = mx.clip(logvar, -30.0, 20.0)
    return mean, logvar

fastvideo.mlx_runtime.minimax_h3_video_vae.MLXMiniMaxH3VideoVAE.sample_posterior

sample_posterior(mean, logvar, noise)

Reparameterization with an explicit noise array (deterministic parity).

Source
@staticmethod
def sample_posterior(mean, logvar, noise):
    """Reparameterization with an explicit noise array (deterministic parity)."""
    return mean + mx.exp(0.5 * logvar) * noise

fastvideo.mlx_runtime.minimax_h3_video_vae.MLXMiniMaxH3VideoVAE.decode_clip_tiled

decode_clip_tiled(z, tile_sample_min_height: int, tile_sample_min_width: int, tile_sample_min_overlap_height: int = 64, tile_sample_min_overlap_width: int = 64)

One clip through the decoder with spatial tiling (memory bounded).

Source
def decode_clip_tiled(self,
                      z,
                      tile_sample_min_height: int,
                      tile_sample_min_width: int,
                      tile_sample_min_overlap_height: int = 64,
                      tile_sample_min_overlap_width: int = 64):
    """One clip through the decoder with spatial tiling (memory bounded)."""
    height = z.shape[-2] * self.spatial_compression_ratio
    width = z.shape[-1] * self.spatial_compression_ratio
    y_starts, y_lengths, y_overlaps = self._split_tiles(height, tile_sample_min_height,
                                                        tile_sample_min_overlap_height)
    x_starts, x_lengths, x_overlaps = self._split_tiles(width, tile_sample_min_width, tile_sample_min_overlap_width)
    ratio = self.spatial_compression_ratio
    rows = []
    for y_start, y_length in zip(y_starts, y_lengths, strict=False):
        row = []
        for x_start, x_length in zip(x_starts, x_lengths, strict=False):
            tile = z[..., y_start // ratio:y_start // ratio + y_length // ratio,
                     x_start // ratio:x_start // ratio + x_length // ratio]
            row.append(self._decode_clip(tile))
        rows.append(row)
    return self._stitch_tiles(rows, y_overlaps, x_overlaps)

fastvideo.mlx_runtime.minimax_h3_video_vae.MLXMiniMaxH3VideoVAE.decode

decode(z, *, tiled: bool = True, tile_sample_min_height: int = 256, tile_sample_min_width: int = 256, tile_sample_min_overlap_height: int = 64, tile_sample_min_overlap_width: int = 64)

Chunked decode of normalized latents (1, C, T_lat, H', W') -> (1, 3, T, H, W).

Source
def decode(self,
           z,
           *,
           tiled: bool = True,
           tile_sample_min_height: int = 256,
           tile_sample_min_width: int = 256,
           tile_sample_min_overlap_height: int = 64,
           tile_sample_min_overlap_width: int = 64):
    """Chunked decode of normalized latents (1, C, T_lat, H', W') -> (1, 3, T, H, W)."""
    if not self.has_decoder:
        raise RuntimeError("This MLX H3 video VAE was loaded without decoder weights.")
    cfg = self.config
    tokens_chunk_size = cfg.tokens_chunk_size
    token_drop = cfg.token_drop
    temporal_ratio = self.temporal_compression_ratio
    chunk_num_frames = tokens_chunk_size * temporal_ratio
    num_tokens = z.shape[2] + token_drop
    pad_tokens = (-num_tokens) % tokens_chunk_size
    num_chunks = (num_tokens + pad_tokens) // tokens_chunk_size - int(token_drop > 0)
    if pad_tokens > 0:
        tail = mx.repeat(z[:, :, -1:], pad_tokens, axis=2)
        z = mx.concatenate([z, tail], axis=2)
def decode_one(chunk):
    if tiled:
        return self.decode_clip_tiled(chunk, tile_sample_min_height, tile_sample_min_width,
                                      tile_sample_min_overlap_height, tile_sample_min_overlap_width)
    return self._decode_clip(chunk)

decoded_chunks = &#91;&#93;
overlap = None
for index in range(num_chunks):
    start = index * tokens_chunk_size
    clip = decode_one(z&#91;:, :, start:start + tokens_chunk_size + cfg.token_overlap&#93;)
    for overlap_index in range(int(token_drop &gt; 0) + 1):
        frame_start = overlap_index * chunk_num_frames
        chunk = clip&#91;:, :, frame_start:frame_start + chunk_num_frames&#93;
        chunk = chunk&#91;:, :, cfg.frame_pre_padding:&#93;
        if overlap_index == 0:
            if overlap is not None:
                chunk = self._blend(overlap, chunk, cfg.frame_overlap, dim=-3)
            mx.eval(chunk)  # materialize per clip; keeps the lazy graph bounded
            decoded_chunks.append(chunk)
        else:
            overlap = chunk
            mx.eval(overlap)
if overlap is not None:
    decoded_chunks.append(overlap)
decoded = mx.concatenate(decoded_chunks, axis=2)

if pad_tokens &gt; 0:
    intra_tail = cfg.clip_length % temporal_ratio
    num_tokens_before_pad = z.shape&#91;2&#93; - pad_tokens
    pad_frames = sum(intra_tail if intra_tail and (num_tokens_before_pad + offset) %
                     tokens_chunk_size == 0 else temporal_ratio for offset in range(pad_tokens))
    decoded = decoded&#91;:, :, :-pad_frames&#93;
return decoded</code></pre></details>

fastvideo.mlx_runtime.minimax_h3_video_vae.mlx_h3_video_vae_from_files

mlx_h3_video_vae_from_files(*, vae_dir: str | Path | None = None, decoder_path: str | Path | None = None, encoder_path: str | Path | None = None, include_encoder: bool = True, include_decoder: bool = True, storage_dtype: str = 'fp32', config: MiniMaxH3VideoVAEConfigView | None = None, decoder_quantization: str | None = None) -> MLXMiniMaxH3VideoVAE

Load an H3 video VAE from any mix of the released VAE directory and standalone component files.

decoder_path / encoder_path override the matching half of vae_dir (or stand alone when no vae_dir is given, so the base VAE weights are never read). Standalone files may be a LightVAE block-pruned component (config embedded in its metadata; decoder_num_layers follows the kept blocks) or a FastH3 NVFP4 decoder (decoder.safetensors bf16 masters, or decoder-nvfp4.safetensors pre-packed). A FastH3 decoder carries no latent statistics: pass a LightVAE encoder, a vae_dir holding config.json, or config.

decoder_quantization ("nvfp4", "mxfp8", "int8", "int4") stores the 216 decoder block linears quantized as they are read, so the dense decoder never needs to be resident.

Source
def mlx_h3_video_vae_from_files(*,
                                vae_dir: str | Path | None = None,
                                decoder_path: str | Path | None = None,
                                encoder_path: str | Path | None = None,
                                include_encoder: bool = True,
                                include_decoder: bool = True,
                                storage_dtype: str = "fp32",
                                config: MiniMaxH3VideoVAEConfigView | None = None,
                                decoder_quantization: str | None = None) -> MLXMiniMaxH3VideoVAE:
    """Load an H3 video VAE from any mix of the released VAE directory and standalone component files.
``decoder_path`` / ``encoder_path`` override the matching half of ``vae_dir`` (or stand alone when no
``vae_dir`` is given, so the base VAE weights are never read). Standalone files may be a LightVAE
block-pruned component (config embedded in its metadata; ``decoder_num_layers`` follows the kept
blocks) or a FastH3 NVFP4 decoder (``decoder.safetensors`` bf16 masters, or ``decoder-nvfp4.safetensors``
pre-packed). A FastH3 decoder carries no latent statistics: pass a LightVAE encoder, a ``vae_dir``
holding ``config.json``, or ``config``.

``decoder_quantization`` (&quot;nvfp4&quot;, &quot;mxfp8&quot;, &quot;int8&quot;, &quot;int4&quot;) stores the 216 decoder block linears
quantized as they are read, so the dense decoder never needs to be resident.
&quot;&quot;&quot;
cast_dtype = _DTYPE_MAP&#91;storage_dtype&#93;

def component_paths(explicit: str | Path | None, wanted: bool, what: str) -&gt; list&#91;Path&#93;:
    if not wanted:
        return &#91;&#93;
    if explicit:
        return &#91;Path(explicit)&#93;
    if vae_dir is None:
        raise ValueError(f&quot;A &#123;what&#125; needs vae_dir or an explicit &#123;what&#125;_path.&quot;)
    return _shards(Path(vae_dir))

decoder_paths = component_paths(decoder_path, include_decoder, &quot;decoder&quot;)
encoder_paths = component_paths(encoder_path, include_encoder, &quot;encoder&quot;)
if config is None and vae_dir is not None and (Path(vae_dir) / &quot;config.json&quot;).exists():
    config = MiniMaxH3VideoVAEConfigView.from_vae_dir(vae_dir)
if config is None:
    config = _embedded_config(&#91;Path(p) for p in (encoder_path, decoder_path) if p&#93; + encoder_paths + decoder_paths)
if config is None:
    raise ValueError(&quot;No VAE config: pass vae_dir with config.json, a LightVAE component file, or config=.&quot;)

weights: dict&#91;str, Any&#93; = &#123;&#125;
if include_encoder:
    weights.update(_read_weights(encoder_paths, _ENCODER_PREFIXES, cast_dtype, None))
    _check_required(
        weights,
        &#91;&quot;quant_conv.weight&quot;, &quot;encoder.conv_in.weight&quot;, &quot;encoder.conv_out.weight&quot;, &quot;encoder.norm_out.weight&quot;&#93;,
        &quot;H3 video VAE encoder&quot;)
if include_decoder:
    weights.update(_read_weights(decoder_paths, _DECODER_PREFIXES, cast_dtype, decoder_quantization))
    _check_required(weights, &#91;
        &quot;post_quant_conv.weight&quot;, &quot;post_quant_conv.bias&quot;, &quot;decoder.proj_in.weight&quot;, &quot;decoder.norm_out.weight&quot;,
        &quot;decoder.proj_out.weight&quot;, &quot;decoder.register_tokens&quot;
    &#93;, &quot;H3 video VAE decoder&quot;)
    found = &#123;int(key.split(&quot;.&quot;)&#91;2&#93;) for key in weights if key.startswith(&quot;decoder.transformer_blocks.&quot;)&#125;
    if found != set(range(len(found))) or not found:
        raise KeyError(f&quot;H3 video VAE decoder blocks are not contiguous from 0: &#123;sorted(found)&#125;.&quot;)
    if vae_dir is not None and decoder_path is None and len(found) != config.decoder_num_layers:
        raise KeyError(f&quot;H3 video VAE decoder blocks incomplete: found &#123;sorted(found)&#125; &quot;
                       f&quot;of &#123;config.decoder_num_layers&#125;.&quot;)
    if len(found) != config.decoder_num_layers:  # pruned / depth-cut decoder
        config = replace(config, decoder_num_layers=len(found))
return MLXMiniMaxH3VideoVAE(weights, config, has_encoder=include_encoder, has_decoder=include_decoder)</code></pre></details>

fastvideo.mlx_runtime.minimax_h3_video_vae.mlx_h3_video_vae_from_dir

mlx_h3_video_vae_from_dir(vae_dir: str | Path, *, include_encoder: bool = True, include_decoder: bool = True, storage_dtype: str = 'fp32', config: MiniMaxH3VideoVAEConfigView | None = None) -> MLXMiniMaxH3VideoVAE

Load the released H3 video VAE, streaming one shard at a time.

storage_dtype="fp32" keeps the released numerics. bf16/fp16 halve residency; measure drift against the FP32 acceptance gate before shipping a reduced-dtype configuration.

Source
def mlx_h3_video_vae_from_dir(vae_dir: str | Path,
                              *,
                              include_encoder: bool = True,
                              include_decoder: bool = True,
                              storage_dtype: str = "fp32",
                              config: MiniMaxH3VideoVAEConfigView | None = None) -> MLXMiniMaxH3VideoVAE:
    """Load the released H3 video VAE, streaming one shard at a time.
``storage_dtype=&quot;fp32&quot;`` keeps the released numerics. bf16/fp16 halve
residency; measure drift against the FP32 acceptance gate before shipping
a reduced-dtype configuration.
&quot;&quot;&quot;
return mlx_h3_video_vae_from_files(vae_dir=vae_dir,
                                   include_encoder=include_encoder,
                                   include_decoder=include_decoder,
                                   storage_dtype=storage_dtype,
                                   config=config)</code></pre></details>

Was this page helpful?