Skip to content

minimax_h3_parallel

Sequence-parallel chunk scheduling for the MiniMax-H3 video VAE.

The H3 video VAE decodes a video as a series of temporal-chunk decoder forwards whose outputs are joined by a short deterministic frame blend (AutoencoderKLMiniMaxH3._decode_chunks), and encodes videos as fully independent clip_length-frame encoder forwards. Neither the chunk decode nor the clip encode has any cross-chunk data dependency — only the joining of decoded chunks (overlap blending, frame trimming) is sequential. This module round-robins the chunk/clip forwards across the ranks of a sequence-parallel group and replays the serial joining logic on the assembling rank, reproducing the serial result bit for bit.

Bit-exactness contract: - every rank holds an identical copy of the inputs (the H3 DiT all-gathers its outputs, and reference pixels are prepared identically on all ranks); - a chunk decoded on any rank is bitwise the tensor the serial loop would produce (identical weights, inputs, and deterministic kernels on identical GPUs), and NCCL transports it bitwise; - every serialization point of the serial algorithm (overlap blending, frame trimming, pixel denormalization, output-buffer copies, moment concatenation and token-drop trimming) runs on the assembling rank in serial order via the same VAE methods the serial path uses.

Collective safety: all group ranks must call these functions together with identically shaped inputs. Work proceeds in rounds of one collective each; ranks without a chunk in the final round contribute a placeholder tensor, so participation is uniform by construction and no rank-dependent branch guards a collective.

Caveat — compiled decoders (enable_torch_compile_vae): inductor autotunes kernel configs per process at first call, so a compiled decoder is only deterministic WITHIN a process, not across processes. Chunks decoded on other ranks then differ from the serial rank's decode of the same chunk exactly as two serial runs in different processes would. Direct decoder tensors measured on GB200 at 124f had max absolute error 0.00268358 (0.684/255), mean absolute error 4.213e-05 (0.0107/255), and 24.59% nonzero values; the first chunk was bit-identical. A separate decoded-MP4 comparison reached 63/255 on <0.5% of pixels, but that includes lossy MP4 encoding and is not the decoder-tensor error envelope. With the eager decoder — the pipeline default — parallel output is bitwise equal to serial decode_to_pixels.

Classes

Functions:

fastvideo.models.vaes.minimax_h3_parallel.decode_to_pixels_parallel

decode_to_pixels_parallel(vae: AutoencoderKLMiniMaxH3, z: Tensor, output: Tensor | None, group: 'GroupCoordinator', strategy: str = DEFAULT_DECODE_GATHER_STRATEGY) -> Tensor | None

Chunk-parallel decode_to_pixels across a sequence-parallel group.

All group ranks call this together with identical z. Temporal chunks are decoded round-robin across the group and their segments move to the group's first rank, which assembles bitwise the serial decode_to_pixels result into output. Only the first rank passes output (validated exactly like the serial API); other ranks pass None and receive None.

Source code in fastvideo/models/vaes/minimax_h3_parallel.py
def decode_to_pixels_parallel(
    vae: AutoencoderKLMiniMaxH3,
    z: torch.Tensor,
    output: torch.Tensor | None,
    group: "GroupCoordinator",
    strategy: str = DEFAULT_DECODE_GATHER_STRATEGY,
) -> torch.Tensor | None:
    """Chunk-parallel ``decode_to_pixels`` across a sequence-parallel group.

    All group ranks call this together with identical ``z``. Temporal chunks
    are decoded round-robin across the group and their segments move to the
    group's first rank, which assembles bitwise the serial
    ``decode_to_pixels`` result into ``output``. Only the first rank passes
    ``output`` (validated exactly like the serial API); other ranks pass
    ``None`` and receive ``None``.
    """
    if strategy not in DECODE_GATHER_STRATEGIES:
        raise ValueError(f"Unknown parallel-decode strategy {strategy!r}; expected one of {DECODE_GATHER_STRATEGIES}.")
    is_leader = group.rank_in_group == 0
    if is_leader:
        if output is None:
            raise ValueError("The first sequence-parallel rank must provide the CPU output buffer.")
        expected_shape = vae.decoded_pixel_shape(z.shape)
        if output.device.type != "cpu" or output.dtype != torch.float32 or tuple(output.shape) != expected_shape:
            raise ValueError(
                "`output` must be a CPU float32 tensor with shape "
                f"{expected_shape}, got device={output.device}, dtype={output.dtype}, shape={tuple(output.shape)}.")
    elif output is not None:
        raise ValueError("Only the first sequence-parallel rank may provide an output buffer.")
    if group.world_size == 1:
        return vae.decode_to_pixels(z, output)

    try:
        if vae.use_slicing and z.shape[0] > 1:
            for batch_index, z_slice in enumerate(z.split(1)):
                slice_output = output[batch_index:batch_index + 1] if output is not None else None
                _decode_single_parallel(vae, z_slice, slice_output, group, strategy)
        else:
            _decode_single_parallel(vae, z, output, group, strategy)
    finally:
        # Drain the leader's async chunk copies before the caller (or an
        # exception handler) can read or release the pinned buffer.
        if output is not None and vae._streams_chunk_copies(z, output):
            torch.cuda.current_stream(z.device).synchronize()
    return output

fastvideo.models.vaes.minimax_h3_parallel.encode_pixels_parallel

encode_pixels_parallel(vae: AutoencoderKLMiniMaxH3, pixels: Tensor, group: 'GroupCoordinator') -> AutoencoderKLOutput

Clip-parallel encode_pixels across a sequence-parallel group.

Encoder clips have no cross-clip dependency (no overlap, no blending), so ranks encode disjoint clips and all-gather the per-clip moment tensors. Every rank returns the identical full posterior — preserving the serial contract that all ranks hold the same encoded latents — bitwise equal to vae.encode_pixels(pixels). Moments are latent-sized (a few MB per clip), so the all-gather is negligible next to the clip forwards.

Source code in fastvideo/models/vaes/minimax_h3_parallel.py
def encode_pixels_parallel(
    vae: AutoencoderKLMiniMaxH3,
    pixels: torch.Tensor,
    group: "GroupCoordinator",
) -> AutoencoderKLOutput:
    """Clip-parallel ``encode_pixels`` across a sequence-parallel group.

    Encoder clips have no cross-clip dependency (no overlap, no blending), so
    ranks encode disjoint clips and all-gather the per-clip moment tensors.
    Every rank returns the identical full posterior — preserving the serial
    contract that all ranks hold the same encoded latents — bitwise equal to
    ``vae.encode_pixels(pixels)``. Moments are latent-sized (a few MB per
    clip), so the all-gather is negligible next to the clip forwards.
    """
    if pixels.ndim != 5 or pixels.shape[1] != vae.config.in_channels or pixels.shape[2] <= 0:
        raise ValueError(
            f"`pixels` must have shape [B, {vae.config.in_channels}, T, H, W] with T > 0, "
            f"got {tuple(pixels.shape)}.")
    if pixels.device.type != "cpu":
        raise ValueError(f"`pixels` must remain on CPU, got device={pixels.device}.")
    if pixels.dtype != torch.uint8 and not pixels.is_floating_point():
        raise TypeError(f"`pixels` must use uint8 or a floating-point dtype, got {pixels.dtype}.")
    if group.world_size == 1:
        return vae.encode_pixels(pixels)
    if vae.use_slicing and pixels.shape[0] > 1:
        moments = torch.cat([_encode_single_parallel(vae, pixel_slice, group) for pixel_slice in pixels.split(1)])
    else:
        moments = _encode_single_parallel(vae, pixels, group)
    return AutoencoderKLOutput(latent_dist=DiagonalGaussianDistribution(moments))

fastvideo.models.vaes.minimax_h3_parallel.parallel_chunk_indices

parallel_chunk_indices(num_chunks: int, world_size: int, rank_in_group: int) -> list[int]

Round-robin chunk ownership: chunk i belongs to rank i % world_size.

Source code in fastvideo/models/vaes/minimax_h3_parallel.py
def parallel_chunk_indices(num_chunks: int, world_size: int, rank_in_group: int) -> list[int]:
    """Round-robin chunk ownership: chunk ``i`` belongs to rank ``i % world_size``."""
    if num_chunks < 0:
        raise ValueError(f"num_chunks must be non-negative, got {num_chunks}.")
    if world_size < 1:
        raise ValueError(f"world_size must be positive, got {world_size}.")
    if not 0 <= rank_in_group < world_size:
        raise ValueError(f"rank_in_group {rank_in_group} out of range for world_size {world_size}.")
    return list(range(rank_in_group, num_chunks, world_size))