Skip to content

mlx_runtime

Experimental Apple MLX runtime helpers.

This package is intentionally small for now. It exists to grow the Apple-native FastWan path in measurable steps: shape planning, primitive benchmarks, then Wan block parity, then full DiT/runtime support.

Classes

fastvideo.mlx_runtime.AppliedMemoryLimits dataclass

AppliedMemoryLimits(mlx_memory_limit_gib: float | None = None, mlx_cache_limit_gib: float | None = None, mlx_disable_cache: bool = False, mlx_wired_limit_gib: float | None = None, torch_mps_high_watermark_ratio: float | None = None, torch_mps_low_watermark_ratio: float | None = None, applied_bytes: dict[str, int] = dict(), previous_bytes: dict[str, int] = dict(), errors: dict[str, str] = dict())

Memory limits applied for one Apple Silicon benchmark/generation process.

Methods:

fastvideo.mlx_runtime.AppliedMemoryLimits.as_metrics
as_metrics() -> dict[str, int | float | str | bool | None]

Flatten the configured memory limits, applied values, previous values, and errors into a metrics dictionary.

Returns:

Type Description
dict[str, int | float | str | bool | None]

dict[str, int | float | str | bool | None]: Metrics keyed by limit names and their corresponding values.

Source code in fastvideo/mlx_runtime/memory.py
def as_metrics(self) -> dict[str, int | float | str | bool | None]:
    """Flatten the configured memory limits, applied values, previous values, and errors into a metrics dictionary.

    Returns:
        dict[str, int | float | str | bool | None]: Metrics keyed by limit names and their corresponding values.
    """
    metrics: dict[str, int | float | str | bool | None] = {
        "mlx_memory_limit_gib": self.mlx_memory_limit_gib,
        "mlx_cache_limit_gib": self.mlx_cache_limit_gib,
        "mlx_disable_cache": self.mlx_disable_cache,
        "mlx_wired_limit_gib": self.mlx_wired_limit_gib,
        "torch_mps_high_watermark_ratio": self.torch_mps_high_watermark_ratio,
        "torch_mps_low_watermark_ratio": self.torch_mps_low_watermark_ratio,
    }
    for name, value in self.applied_bytes.items():
        metrics[f"{name}_bytes"] = value
    for name, value in self.previous_bytes.items():
        metrics[f"previous_{name}_bytes"] = value
    for name, error in self.errors.items():
        metrics[f"{name}_error"] = error
    return metrics

fastvideo.mlx_runtime.EnhanceResult dataclass

EnhanceResult(original: str, enhanced: str, backend: str, elapsed_s: float, model: str | None = None)

Outcome of a prompt enrichment call.

Attributes

fastvideo.mlx_runtime.EnhanceResult.changed property
changed: bool

Indicates whether the enhanced prompt differs from the original after trimming surrounding whitespace.

Returns:

Name Type Description
bool bool

True if the prompts differ, False otherwise.

fastvideo.mlx_runtime.FastSpatialPlan dataclass

FastSpatialPlan(plan: RefinePlan, upsample_mode: str, sharpen: float = DEFAULT_FAST_SPATIAL_SHARPEN)

Resolved geometry for a spatial-fast (upsample-only) run.

Attributes

fastvideo.mlx_runtime.FastSpatialPlan.enabled property
enabled: bool

Determine whether spatial scaling is enabled.

Returns:

Type Description
bool

true if the spatial scale is greater than one, false otherwise.

fastvideo.mlx_runtime.FastSpatialPlan.scale property
scale: int

Provides the configured spatial scaling factor.

Returns:

Name Type Description
int int

The spatial scaling factor.

fastvideo.mlx_runtime.FastSpatialPlan.stage1_height property
stage1_height: int

Provide the stage-one latent height used for reduced-resolution processing.

Returns:

Name Type Description
int int

The stage-one latent height.

fastvideo.mlx_runtime.FastSpatialPlan.stage1_width property
stage1_width: int

Get the stage-one latent width.

Returns:

Name Type Description
int int

The stage-one latent width.

fastvideo.mlx_runtime.FastSpatialPlan.target_height property
target_height: int

Return the target output height for the spatial plan.

Returns:

Name Type Description
int int

Target output height in pixels.

fastvideo.mlx_runtime.FastSpatialPlan.target_width property
target_width: int

Return the target image width in pixels.

Returns:

Name Type Description
int int

The target image width.

fastvideo.mlx_runtime.MLXQuantizationSpec dataclass

MLXQuantizationSpec(mode: str, bits: int | None = None, group_size: int | None = None)

MLX quantized-matmul configuration for DiT linear weights.

fastvideo.mlx_runtime.MLXWanDiT

MLXWanDiT(weights: dict[str, array], blocks: list[MLXWanTransformerBlock], config: dict, *, compile: bool = False)

Experimental FP16 Wan/FastWan DiT forward path in MLX.

Source code in fastvideo/mlx_runtime/fastwan.py
def __init__(
    self,
    weights: dict[str, mx.array],
    blocks: list[MLXWanTransformerBlock],
    config: dict,
    *,
    compile: bool = False,
) -> None:
    import os

    self.weights = weights
    self.blocks = blocks
    self.config = config
    self.num_heads = int(config["num_attention_heads"])
    self.head_dim = int(config["attention_head_dim"])
    self.hidden_size = self.num_heads * self.head_dim
    self.ffn_dim = int(config["ffn_dim"])
    self.in_channels = int(config["in_channels"])
    self.out_channels = int(config["out_channels"])
    self.patch_size = tuple(config["patch_size"])
    self.freq_dim = int(config["freq_dim"])
    # Opt-in graph fusion. With fixed weights and static shapes, the whole
    # denoise-step forward is a pure function of (latents, timestep) -- a
    # good mx.compile target. Off by default so the eager path stays the
    # baseline; enable via constructor or FASTVIDEO_MLX_COMPILE=1 and verify
    # with the benchmark's SSIM ~= 1.0 check.
    self._enable_compile = compile or os.environ.get("FASTVIDEO_MLX_COMPILE", "0") == "1"
    self._compiled_forward: Callable[..., Any] | None = None
    self._compiled_signature: tuple | None = None

fastvideo.mlx_runtime.MLXWanTransformerBlock

MLXWanTransformerBlock(weights: dict[str, array], *, dim: int, ffn_dim: int, num_heads: int, eps: float = 1e-06)

Dense T2V Wan transformer block for the experimental MLX runtime.

This mirrors the non-VSA PyTorch block for single-process dense attention. Rotary embeddings and sequence-parallel paths are intentionally left out of this first parity target.

Source code in fastvideo/mlx_runtime/fastwan.py
def __init__(self, weights: dict[str, mx.array], *, dim: int, ffn_dim: int, num_heads: int, eps: float = 1e-6):
    self.weights = weights
    self.dim = dim
    self.ffn_dim = ffn_dim
    self.num_heads = num_heads
    self.head_dim = dim // num_heads
    self.eps = eps
    self.attn2 = MLXWanT2VCrossAttention(weights, dim=dim, num_heads=num_heads, eps=eps)

fastvideo.mlx_runtime.RefinePlan dataclass

RefinePlan(target_height: int, target_width: int, stage1_height: int, stage1_width: int, spatial_scale: int, vae_spatial_compression: int, vae_temporal_compression: int, num_frames: int)

Resolved stage-1 / stage-2 geometry for a two-pass refine run.

Attributes

fastvideo.mlx_runtime.RefinePlan.latent_frames property
latent_frames: int

Calculate the number of latent frames after VAE temporal compression.

Returns:

Name Type Description
int int

The compressed latent frame count.

fastvideo.mlx_runtime.RefinePlan.stage1_latent_height property
stage1_latent_height: int

Return the stage-1 latent height after VAE spatial compression.

fastvideo.mlx_runtime.RefinePlan.stage1_latent_width property
stage1_latent_width: int

Return the stage-one latent width after VAE spatial compression.

fastvideo.mlx_runtime.RefinePlan.stage2_latent_height property
stage2_latent_height: int

Calculate the target-resolution latent height.

Returns:

Name Type Description
int int

The target height divided by the VAE spatial compression factor.

fastvideo.mlx_runtime.RefinePlan.stage2_latent_width property
stage2_latent_width: int

Return the target image width in latent-space units.

fastvideo.mlx_runtime.TwoPassResult dataclass

TwoPassResult(latents: Any, stage1_latents: Any, plan: RefinePlan, refine_sigma: float)

Outputs of :func:run_two_pass_dmd.

fastvideo.mlx_runtime.UnsupportedMLXQuantizationError

Bases: ValueError

A quantization mode the installed MLX build cannot execute.

Raised by :func:ensure_quantization_supported before any model weights are loaded, so callers (CLI flags, benchmark sweeps) can fail fast with an actionable message -- or skip the mode -- instead of crashing deep inside mx.quantize mid-load.

Functions:

fastvideo.mlx_runtime.add_memory_limit_args

add_memory_limit_args(parser: ArgumentParser, *, mlx_memory_limit_gib: float | None = None, mlx_cache_limit_gib: float | None = None, mlx_disable_cache: bool = False, mlx_wired_limit_gib: float | None = None, torch_mps_high_watermark_ratio: float | None = None, torch_mps_low_watermark_ratio: float | None = None) -> None

Add configurable Apple Silicon memory-limit options to an argument parser.

Parameters:

Name Type Description Default
parser ArgumentParser

Parser to which the options are added.

required
mlx_memory_limit_gib float | None

Default MLX memory limit in GiB.

None
mlx_cache_limit_gib float | None

Default MLX cache limit in GiB.

None
mlx_disable_cache bool

Whether the cache limit defaults to zero.

False
mlx_wired_limit_gib float | None

Default MLX wired-memory limit in GiB.

None
torch_mps_high_watermark_ratio float | None

Default PyTorch MPS high-watermark ratio.

None
torch_mps_low_watermark_ratio float | None

Default PyTorch MPS low-watermark ratio.

None
Source code in fastvideo/mlx_runtime/memory.py
def add_memory_limit_args(
    parser: argparse.ArgumentParser,
    *,
    mlx_memory_limit_gib: float | None = None,
    mlx_cache_limit_gib: float | None = None,
    mlx_disable_cache: bool = False,
    mlx_wired_limit_gib: float | None = None,
    torch_mps_high_watermark_ratio: float | None = None,
    torch_mps_low_watermark_ratio: float | None = None,
) -> None:
    """
    Add configurable Apple Silicon memory-limit options to an argument parser.

    Parameters:
        parser (argparse.ArgumentParser): Parser to which the options are added.
        mlx_memory_limit_gib (float | None): Default MLX memory limit in GiB.
        mlx_cache_limit_gib (float | None): Default MLX cache limit in GiB.
        mlx_disable_cache (bool): Whether the cache limit defaults to zero.
        mlx_wired_limit_gib (float | None): Default MLX wired-memory limit in GiB.
        torch_mps_high_watermark_ratio (float | None): Default PyTorch MPS high-watermark ratio.
        torch_mps_low_watermark_ratio (float | None): Default PyTorch MPS low-watermark ratio.
    """
    parser.add_argument("--mlx-memory-limit-gib",
                        type=float,
                        default=mlx_memory_limit_gib,
                        help="Set MLX memory limit in GiB for memory-tier testing (DiT path).")
    parser.add_argument("--mlx-cache-limit-gib",
                        type=float,
                        default=mlx_cache_limit_gib,
                        help="Set MLX cache limit in GiB. Use --mlx-disable-cache to force 0.")
    parser.add_argument("--mlx-disable-cache",
                        action="store_true",
                        default=mlx_disable_cache,
                        help="Set MLX cache limit to 0 for stricter memory-tier tests.")
    parser.add_argument("--mlx-wired-limit-gib",
                        type=float,
                        default=mlx_wired_limit_gib,
                        help="Set MLX wired-memory limit in GiB where supported by macOS/MLX.")
    parser.add_argument("--torch-mps-high-watermark-ratio",
                        type=float,
                        default=torch_mps_high_watermark_ratio,
                        help="Set PYTORCH_MPS_HIGH_WATERMARK_RATIO before importing torch.")
    parser.add_argument("--torch-mps-low-watermark-ratio",
                        type=float,
                        default=torch_mps_low_watermark_ratio,
                        help="Set PYTORCH_MPS_LOW_WATERMARK_RATIO before importing torch.")

fastvideo.mlx_runtime.apply_fast_spatial_upsample

apply_fast_spatial_upsample(frames: Iterable[ndarray], spatial: FastSpatialPlan) -> list[ndarray]

Resample decoded stage-1 frames up to the target resolution.

This runs on decoded RGB frames, not on latents: see the module docstring for why the latent-space version produced a blurred veil.

Parameters:

Name Type Description Default
frames Iterable[ndarray]

Decoded HxWx3 uint8 RGB frames, produced by decoding at the stage-one resolution.

required
spatial FastSpatialPlan

Plan defining the target size, interpolation kernel, and unsharp strength.

required

Returns:

Type Description
list[ndarray]

list[np.ndarray]: Frames at the target resolution. When spatial scaling is disabled the frames are returned unchanged, as a list.

Source code in fastvideo/mlx_runtime/fast_spatial.py
def apply_fast_spatial_upsample(
    frames: Iterable[np.ndarray],
    spatial: FastSpatialPlan,
) -> list[np.ndarray]:
    """Resample decoded stage-1 frames up to the target resolution.

    This runs on decoded RGB frames, *not* on latents: see the module
    docstring for why the latent-space version produced a blurred veil.

    Parameters:
        frames (Iterable[np.ndarray]): Decoded HxWx3 uint8 RGB frames, produced
            by decoding at the stage-one resolution.
        spatial (FastSpatialPlan): Plan defining the target size, interpolation
            kernel, and unsharp strength.

    Returns:
        list[np.ndarray]: Frames at the target resolution. When spatial scaling
            is disabled the frames are returned unchanged, as a list.
    """
    if not spatial.enabled:
        return list(frames)
    return upsample_frames(
        frames,
        width=spatial.target_width,
        height=spatial.target_height,
        mode=spatial.upsample_mode,
        sharpen=spatial.sharpen,
    )

fastvideo.mlx_runtime.apply_memory_limits

apply_memory_limits(*, mlx_memory_limit_gib: float | None = None, mlx_cache_limit_gib: float | None = None, mlx_disable_cache: bool = False, mlx_wired_limit_gib: float | None = None, torch_mps_high_watermark_ratio: float | None = None, torch_mps_low_watermark_ratio: float | None = None, mx_module: Any | None = None) -> AppliedMemoryLimits

Apply optional MLX allocator limits and PyTorch MPS watermarks.

PyTorch reads MPS watermark variables when the MPS backend initializes, so call this before importing PyTorch. Specifying only a high watermark sets the low watermark to 0.0. MLX limit-setting failures are recorded in the result and do not prevent other limits from being applied.

Parameters:

Name Type Description Default
mlx_memory_limit_gib float | None

Maximum MLX memory in GiB.

None
mlx_cache_limit_gib float | None

Maximum MLX cache size in GiB.

None
mlx_disable_cache bool

Whether to disable the MLX cache.

False
mlx_wired_limit_gib float | None

Maximum MLX wired memory in GiB.

None
torch_mps_high_watermark_ratio float | None

PyTorch MPS high watermark ratio.

None
torch_mps_low_watermark_ratio float | None

PyTorch MPS low watermark ratio.

None

Returns:

Name Type Description
AppliedMemoryLimits AppliedMemoryLimits

Configured values, applied and previous MLX byte limits, MPS watermark values, and per-limit errors.

Source code in fastvideo/mlx_runtime/memory.py
def apply_memory_limits(
    *,
    mlx_memory_limit_gib: float | None = None,
    mlx_cache_limit_gib: float | None = None,
    mlx_disable_cache: bool = False,
    mlx_wired_limit_gib: float | None = None,
    torch_mps_high_watermark_ratio: float | None = None,
    torch_mps_low_watermark_ratio: float | None = None,
    mx_module: Any | None = None,
) -> AppliedMemoryLimits:
    """Apply optional MLX allocator limits and PyTorch MPS watermarks.

    PyTorch reads MPS watermark variables when the MPS backend initializes, so
    call this before importing PyTorch. Specifying only a high watermark sets the
    low watermark to ``0.0``. MLX limit-setting failures are recorded in the
    result and do not prevent other limits from being applied.

    Parameters:
        mlx_memory_limit_gib (float | None): Maximum MLX memory in GiB.
        mlx_cache_limit_gib (float | None): Maximum MLX cache size in GiB.
        mlx_disable_cache (bool): Whether to disable the MLX cache.
        mlx_wired_limit_gib (float | None): Maximum MLX wired memory in GiB.
        torch_mps_high_watermark_ratio (float | None): PyTorch MPS high watermark
            ratio.
        torch_mps_low_watermark_ratio (float | None): PyTorch MPS low watermark
            ratio.

    Returns:
        AppliedMemoryLimits: Configured values, applied and previous MLX byte
            limits, MPS watermark values, and per-limit errors.
    """
    if torch_mps_high_watermark_ratio is not None and torch_mps_low_watermark_ratio is None:
        torch_mps_low_watermark_ratio = 0.0

    high = _set_mps_env("PYTORCH_MPS_HIGH_WATERMARK_RATIO", torch_mps_high_watermark_ratio)
    low = _set_mps_env("PYTORCH_MPS_LOW_WATERMARK_RATIO", torch_mps_low_watermark_ratio)

    memory_bytes = gib_to_bytes(mlx_memory_limit_gib)
    cache_bytes = 0 if mlx_disable_cache else gib_to_bytes(mlx_cache_limit_gib)
    wired_bytes = gib_to_bytes(mlx_wired_limit_gib)

    applied: dict[str, int] = {}
    previous: dict[str, int] = {}
    errors: dict[str, str] = {}
    if memory_bytes is not None or cache_bytes is not None or wired_bytes is not None:
        if mx_module is None:
            import mlx.core as mx

            mx_module = mx

        # Apply each limit independently; record failures without stopping.
        limits = [
            ("mlx_memory_limit", memory_bytes, mx_module.set_memory_limit),
            ("mlx_cache_limit", cache_bytes, mx_module.set_cache_limit),
            ("mlx_wired_limit", wired_bytes, mx_module.set_wired_limit),
        ]
        for name, value, setter in limits:
            if value is not None:
                try:
                    previous[name] = int(setter(value))
                    applied[name] = value
                except Exception as exc:  # noqa: BLE001 - macOS/system-limit dependent.
                    errors[name] = f"{type(exc).__name__}: {exc}"

    return AppliedMemoryLimits(
        mlx_memory_limit_gib=mlx_memory_limit_gib,
        mlx_cache_limit_gib=mlx_cache_limit_gib,
        mlx_disable_cache=mlx_disable_cache,
        mlx_wired_limit_gib=mlx_wired_limit_gib,
        torch_mps_high_watermark_ratio=high,
        torch_mps_low_watermark_ratio=low,
        applied_bytes=applied,
        previous_bytes=previous,
        errors=errors,
    )

fastvideo.mlx_runtime.default_refine_timesteps

default_refine_timesteps(schedule: MLXDMDSchedule, timesteps: Sequence[float | int]) -> list[float]

Derive stage-2 timesteps from the stage-1 DMD grid.

The stage-2 pass must start below full noise, otherwise the hand-off (1 - sigma) * upsampled + sigma * noise weights stage 1 at zero and the refine pass silently becomes a plain full-resolution generation at twice the cost. FastWan's stage-1 grid opens at t=1000 (sigma exactly 1.0), so reusing it verbatim — which is what happens when --refine-dmd-denoising-steps is left unset — discards stage 1.

Dropping the leading full-noise entries keeps the pass on timesteps the distilled student was actually trained on (no off-grid t the DiT has never seen) while letting the stage-1 structure through.

Parameters:

Name Type Description Default
schedule MLXDMDSchedule

Schedule used to map timesteps to noise levels.

required
timesteps Sequence[float | int]

The stage-1 DMD timestep grid.

required

Returns:

Type Description
list[float]

list[float]: The stage-1 grid with leading full-noise timesteps removed.

Raises:

Type Description
ValueError

If every timestep in the grid is at full noise, leaving no usable refine step.

Source code in fastvideo/mlx_runtime/refine.py
def default_refine_timesteps(
    schedule: MLXDMDSchedule,
    timesteps: Sequence[float | int],
) -> list[float]:
    """Derive stage-2 timesteps from the stage-1 DMD grid.

    The stage-2 pass must start *below* full noise, otherwise the hand-off
    ``(1 - sigma) * upsampled + sigma * noise`` weights stage 1 at zero and
    the refine pass silently becomes a plain full-resolution generation at
    twice the cost. FastWan's stage-1 grid opens at ``t=1000`` (``sigma``
    exactly 1.0), so reusing it verbatim — which is what happens when
    ``--refine-dmd-denoising-steps`` is left unset — discards stage 1.

    Dropping the leading full-noise entries keeps the pass on timesteps the
    distilled student was actually trained on (no off-grid ``t`` the DiT has
    never seen) while letting the stage-1 structure through.

    Parameters:
        schedule (MLXDMDSchedule): Schedule used to map timesteps to noise levels.
        timesteps (Sequence[float | int]): The stage-1 DMD timestep grid.

    Returns:
        list[float]: The stage-1 grid with leading full-noise timesteps removed.

    Raises:
        ValueError: If every timestep in the grid is at full noise, leaving no
            usable refine step.
    """
    steps = [float(step) for step in timesteps]
    first = 0
    while first < len(steps) and schedule.sigma_for(steps[first]) >= 1.0:
        first += 1
    if first == len(steps):
        raise ValueError(f"No usable refine timesteps in {steps}: every entry is at sigma >= 1 "
                         "(full noise), which would discard the stage-1 result. Pass "
                         "explicit stage-2 timesteps below the full-noise step.")
    return steps[first:]

fastvideo.mlx_runtime.enhance_prompt

enhance_prompt(prompt: str, *, backend: str = 'auto', model: str | None = None, system_prompt: str = DEFAULT_ENHANCE_SYSTEM_PROMPT, max_tokens: int = 128) -> EnhanceResult

Enhance a prompt using the selected backend, falling back to a deterministic template when configured for automatic selection.

Parameters:

Name Type Description Default
prompt str

The prompt to enhance.

required
backend str

The enhancement backend: "auto", "mlx-lm", or "template".

'auto'
model str | None

The MLX language model to use.

None
system_prompt str

Instructions provided to the MLX language model.

DEFAULT_ENHANCE_SYSTEM_PROMPT
max_tokens int

Maximum number of tokens generated by the MLX language model.

128

Returns:

Name Type Description
EnhanceResult EnhanceResult

The original and enhanced prompts, selected backend, timing information, and model metadata.

Raises:

Type Description
ValueError

If the prompt is empty or the backend is unsupported.

Exception

If the explicitly selected "mlx-lm" backend fails.

Source code in fastvideo/mlx_runtime/prompt_enhance.py
def enhance_prompt(
    prompt: str,
    *,
    backend: str = "auto",
    model: str | None = None,
    system_prompt: str = DEFAULT_ENHANCE_SYSTEM_PROMPT,
    max_tokens: int = 128,
) -> EnhanceResult:
    """Enhance a prompt using the selected backend, falling back to a deterministic template when configured for automatic selection.

    Parameters:
        prompt (str): The prompt to enhance.
        backend (str): The enhancement backend: ``"auto"``, ``"mlx-lm"``, or ``"template"``.
        model (str | None): The MLX language model to use.
        system_prompt (str): Instructions provided to the MLX language model.
        max_tokens (int): Maximum number of tokens generated by the MLX language model.

    Returns:
        EnhanceResult: The original and enhanced prompts, selected backend, timing information, and model metadata.

    Raises:
        ValueError: If the prompt is empty or the backend is unsupported.
        Exception: If the explicitly selected ``"mlx-lm"`` backend fails.
    """
    text = _normalize_user_prompt(prompt)
    backend_norm = (backend or "auto").lower()
    if backend_norm not in {"auto", "mlx-lm", "template"}:
        raise ValueError(f"Unknown enhance backend: {backend}")

    start = time.perf_counter()
    used_model: str | None = None

    if backend_norm in {"auto", "mlx-lm"}:
        try:
            used_model = model or DEFAULT_MLX_LM_MODEL
            enhanced = enhance_prompt_mlx_lm(
                text,
                model=used_model,
                system_prompt=system_prompt,
                max_tokens=max_tokens,
            )
            return EnhanceResult(
                original=text,
                enhanced=enhanced,
                backend="mlx-lm",
                elapsed_s=time.perf_counter() - start,
                model=used_model,
            )
        except Exception as exc:
            if backend_norm == "mlx-lm":
                raise
            logger.info(
                "[MLX enhance] mlx-lm unavailable (%s); using template backend",
                exc,
            )

    enhanced = enhance_prompt_template(text)
    return EnhanceResult(
        original=text,
        enhanced=enhanced,
        backend="template",
        elapsed_s=time.perf_counter() - start,
        model=None,
    )

fastvideo.mlx_runtime.enhance_prompt_template

enhance_prompt_template(prompt: str) -> str

Expand a prompt with cinematic camera, lighting, motion, and visual-quality details.

Rich prompts are preserved, while thinner prompts receive deterministic enhancements without changing their subject.

Returns:

Name Type Description
str str

The original or expanded prompt with normalized whitespace and punctuation.

Source code in fastvideo/mlx_runtime/prompt_enhance.py
def enhance_prompt_template(prompt: str) -> str:
    """
    Expand a prompt with cinematic camera, lighting, motion, and visual-quality details.

    Rich prompts are preserved, while thinner prompts receive deterministic enhancements
    without changing their subject.

    Returns:
        str: The original or expanded prompt with normalized whitespace and punctuation.
    """
    text = _normalize_user_prompt(prompt)
    if _already_rich(text):
        return text

    lower = text.lower()
    parts = [text.rstrip(".")]

    if not any(c in lower for c in _CAMERA_CUES):
        parts.append("shot on a 35mm anamorphic lens, gentle handheld micro-movement, "
                     "shallow depth of field")
    if not any(c in lower for c in _LIGHT_CUES):
        parts.append("natural cinematic lighting with soft volumetric haze and subtle "
                     "rim light separating subject from background")
    if not any(c in lower for c in _MOTION_CUES):
        parts.append("smooth continuous motion with grounded physics")

    parts.append("highly detailed, coherent temporal continuity, film grain, "
                 "color graded like a contemporary drama")
    enhanced = ", ".join(parts)
    # Single trailing period; collapse duplicate whitespace.
    enhanced = re.sub(r"\s+", " ", enhanced).strip()
    if not enhanced.endswith("."):
        enhanced += "."
    return enhanced

fastvideo.mlx_runtime.enhance_result_as_metrics

enhance_result_as_metrics(result: EnhanceResult | None) -> dict[str, Any]

Convert prompt enhancement results into metrics fields.

Parameters:

Name Type Description Default
result EnhanceResult | None

The enhancement result, or None when no enhancement was performed.

required

Returns:

Type Description
dict[str, Any]

dict[str, Any]: A metrics mapping containing enhancement status, backend metadata, timing, and original and enhanced prompts.

Source code in fastvideo/mlx_runtime/prompt_enhance.py
def enhance_result_as_metrics(result: EnhanceResult | None) -> dict[str, Any]:
    """
    Convert prompt enhancement results into metrics fields.

    Parameters:
        result (EnhanceResult | None): The enhancement result, or `None` when no enhancement was performed.

    Returns:
        dict[str, Any]: A metrics mapping containing enhancement status, backend metadata, timing, and original and enhanced prompts.
    """
    if result is None:
        return {
            "enhance_prompt": False,
            "enhance_backend": None,
            "enhance_model": None,
            "enhance_elapsed_s": None,
            "prompt_original": None,
            "prompt_enhanced": None,
        }
    return {
        "enhance_prompt": True,
        "enhance_backend": result.backend,
        "enhance_model": result.model,
        "enhance_elapsed_s": result.elapsed_s,
        "prompt_original": result.original,
        "prompt_enhanced": result.enhanced,
    }

fastvideo.mlx_runtime.ensure_quantization_supported

ensure_quantization_supported(spec: MLXQuantizationSpec | None) -> None

Raise :class:UnsupportedMLXQuantizationError if spec cannot run here.

Source code in fastvideo/mlx_runtime/fastwan.py
def ensure_quantization_supported(spec: MLXQuantizationSpec | None) -> None:
    """Raise :class:`UnsupportedMLXQuantizationError` if ``spec`` cannot run here."""
    if spec is None:
        return
    error = quantization_support_error(spec)
    if error is None:
        return
    import mlx.core as mx

    mlx_version = getattr(mx, "__version__", "unknown")
    raise UnsupportedMLXQuantizationError(f"MLX quantization mode '{spec.label}' is not supported by the installed mlx "
                                          f"({mlx_version}): {error}. Upgrade mlx or pick a supported mode "
                                          f"(int8 is currently the most reliable quality/memory target).")

fastvideo.mlx_runtime.fastwan_shape

fastwan_shape(*, height: int, width: int, num_frames: int, vae_temporal_compression: int = 4, vae_spatial_compression: int = 8, patch_size: tuple[int, int, int] = (1, 2, 2), num_heads: int = 12, head_dim: int = 128) -> FastWanShape

Return the approximate DiT token shape for Wan/FastWan T2V inference.

Source code in fastvideo/mlx_runtime/fastwan.py
def fastwan_shape(
        *,
        height: int,
        width: int,
        num_frames: int,
        vae_temporal_compression: int = 4,
        vae_spatial_compression: int = 8,
        patch_size: tuple[int, int, int] = (1, 2, 2),
        num_heads: int = 12,
        head_dim: int = 128,
) -> FastWanShape:
    """Return the approximate DiT token shape for Wan/FastWan T2V inference."""
    latent_frames = (num_frames - 1) // vae_temporal_compression + 1
    latent_height = height // vae_spatial_compression
    latent_width = width // vae_spatial_compression
    patch_frames = latent_frames // patch_size[0]
    patch_height = latent_height // patch_size[1]
    patch_width = latent_width // patch_size[2]
    tokens = patch_frames * patch_height * patch_width
    return FastWanShape(
        height=height,
        width=width,
        num_frames=num_frames,
        latent_frames=latent_frames,
        latent_height=latent_height,
        latent_width=latent_width,
        patch_frames=patch_frames,
        patch_height=patch_height,
        patch_width=patch_width,
        tokens=tokens,
        hidden_size=num_heads * head_dim,
        num_heads=num_heads,
        head_dim=head_dim,
    )

fastvideo.mlx_runtime.gib_to_bytes

gib_to_bytes(value: float | None) -> int | None

Convert a positive memory limit from GiB to bytes.

Parameters:

Name Type Description Default
value float | None

Memory limit in GiB, or None when unset.

required

Returns:

Type Description
int | None

int | None: The memory limit in bytes, or None when no limit is provided.

Raises:

Type Description
ValueError

If value is zero or negative.

Source code in fastvideo/mlx_runtime/memory.py
def gib_to_bytes(value: float | None) -> int | None:
    """
    Convert a positive memory limit from GiB to bytes.

    Parameters:
        value (float | None): Memory limit in GiB, or `None` when unset.

    Returns:
        int | None: The memory limit in bytes, or `None` when no limit is provided.

    Raises:
        ValueError: If `value` is zero or negative.
    """
    if value is None:
        return None
    if value <= 0:
        raise ValueError(f"Memory limit must be positive GiB, got {value}")
    return int(value * GIB)

fastvideo.mlx_runtime.load_mlx_dit_checkpoint

load_mlx_dit_checkpoint(checkpoint_dir: str | Path, *, compile: bool = False) -> MLXWanDiT

Reconstruct an MLXWanDiT model from a versioned checkpoint.

Parameters:

Name Type Description Default
checkpoint_dir str | Path

Directory containing the checkpoint manifest and weights.

required
compile bool

Whether to configure the reconstructed model for compilation.

False

Returns:

Name Type Description
MLXWanDiT MLXWanDiT

The reconstructed model.

Raises:

Type Description
FileNotFoundError

If the checkpoint manifest or weights file is missing.

ValueError

If the checkpoint format is unsupported or block weights are incomplete.

Source code in fastvideo/mlx_runtime/checkpoint.py
def load_mlx_dit_checkpoint(checkpoint_dir: str | Path, *, compile: bool = False) -> MLXWanDiT:
    """
    Reconstruct an MLXWanDiT model from a versioned checkpoint.

    Parameters:
        checkpoint_dir (str | Path): Directory containing the checkpoint manifest and weights.
        compile (bool): Whether to configure the reconstructed model for compilation.

    Returns:
        MLXWanDiT: The reconstructed model.

    Raises:
        FileNotFoundError: If the checkpoint manifest or weights file is missing.
        ValueError: If the checkpoint format is unsupported or block weights are incomplete.
    """
    import mlx.core as mx

    checkpoint_dir = Path(checkpoint_dir)
    manifest_path = checkpoint_dir / MANIFEST_FILENAME
    weights_path = checkpoint_dir / WEIGHTS_FILENAME
    if not manifest_path.exists() or not weights_path.exists():
        raise FileNotFoundError(f"Not an MLX DiT checkpoint directory: {checkpoint_dir} "
                                f"(expected {MANIFEST_FILENAME} and {WEIGHTS_FILENAME}).")

    manifest = json.loads(manifest_path.read_text())
    version = manifest.get("format_version")
    if version != FORMAT_VERSION:
        raise ValueError(f"MLX DiT checkpoint {checkpoint_dir} has format_version={version}; "
                         f"this FastVideo build reads version {FORMAT_VERSION}. Re-export the checkpoint.")

    spec = None
    if manifest["quantization"] is not None:
        spec = MLXQuantizationSpec(**manifest["quantization"])
        # The packed layout of mx.quantize output is mode-specific, so a build
        # that cannot run the mode cannot use these arrays at all.
        ensure_quantization_supported(spec)

    arrays = mx.load(str(weights_path))
    quantized_keys: dict[str, dict[str, Any]] = manifest["quantized_keys"]

    def rebuild(key: str):
        """
        Reconstructs a weight array or quantized matrix from checkpoint data.

        Parameters:
            key (str): The weight key to rebuild.

        Returns:
            The stored array for an unquantized weight or a reconstructed quantized matrix.
        """
        if key not in quantized_keys:
            return arrays[key]
        info = quantized_keys[key]
        assert spec is not None, f"Quantized key '{key}' in a checkpoint without a quantization spec"
        return QuantizedMatrix(
            weight=arrays[key],
            scales=arrays[f"{key}.scales"],
            biases=arrays[f"{key}.biases"] if info["has_biases"] else None,
            spec=spec,
            dequantized_dtype=_name_to_dtype(info["dequantized_dtype"]),
        )

    config = manifest["config"]
    block_keys: dict[int, list[str]] = {}
    top_level_keys: list[str] = []
    for key in arrays:
        if key.endswith(".scales") or key.endswith(".biases"):
            continue
        if key.startswith(f"{_BLOCK_PREFIX}."):
            index_str, _, _ = key[len(_BLOCK_PREFIX) + 1:].partition(".")
            block_keys.setdefault(int(index_str), []).append(key)
        else:
            top_level_keys.append(key)

    weights = {key: rebuild(key) for key in top_level_keys}

    num_blocks = int(manifest["num_blocks"])
    if sorted(block_keys) != list(range(num_blocks)):
        raise ValueError(f"MLX DiT checkpoint {checkpoint_dir} is missing block weights: "
                         f"manifest says {num_blocks} blocks, found indices {sorted(block_keys)}.")

    inner_dim = int(config["num_attention_heads"]) * int(config["attention_head_dim"])
    blocks = []
    for index in range(num_blocks):
        prefix = f"{_BLOCK_PREFIX}.{index}."
        block_weights = {key[len(prefix):]: rebuild(key) for key in block_keys[index]}
        blocks.append(
            MLXWanTransformerBlock(
                block_weights,
                dim=inner_dim,
                ffn_dim=int(config["ffn_dim"]),
                num_heads=int(config["num_attention_heads"]),
                eps=float(config["eps"]),
            ))
    return MLXWanDiT(weights, blocks, config, compile=compile)

fastvideo.mlx_runtime.load_or_enhance_prompt

load_or_enhance_prompt(prompt: str, *, backend: str = 'auto', model: str | None = None, system_prompt: str = DEFAULT_ENHANCE_SYSTEM_PROMPT, max_tokens: int = 128, cache: bool = True, cache_dir: Path | None = None) -> EnhanceResult

Enhance a prompt, reusing a cached result when available.

Parameters:

Name Type Description Default
prompt str

The prompt to enhance.

required
backend str

Enhancement backend to use.

'auto'
model str | None

Optional model identifier.

None
system_prompt str

System prompt for model-based enhancement.

DEFAULT_ENHANCE_SYSTEM_PROMPT
max_tokens int

Maximum number of tokens generated by the model.

128
cache bool

Whether to read and write the on-disk cache.

True
cache_dir Path | None

Optional directory for cached results.

None

Returns:

Name Type Description
EnhanceResult EnhanceResult

The enhanced prompt and backend metadata. Cached results are marked with the "cache" backend.

Source code in fastvideo/mlx_runtime/prompt_enhance.py
def load_or_enhance_prompt(
    prompt: str,
    *,
    backend: str = "auto",
    model: str | None = None,
    system_prompt: str = DEFAULT_ENHANCE_SYSTEM_PROMPT,
    max_tokens: int = 128,
    cache: bool = True,
    cache_dir: Path | None = None,
) -> EnhanceResult:
    """
    Enhance a prompt, reusing a cached result when available.

    Parameters:
        prompt (str): The prompt to enhance.
        backend (str): Enhancement backend to use.
        model (str | None): Optional model identifier.
        system_prompt (str): System prompt for model-based enhancement.
        max_tokens (int): Maximum number of tokens generated by the model.
        cache (bool): Whether to read and write the on-disk cache.
        cache_dir (Path | None): Optional directory for cached results.

    Returns:
        EnhanceResult: The enhanced prompt and backend metadata. Cached results are marked with the ``"cache"`` backend.
    """
    text = _normalize_user_prompt(prompt)
    path = enhance_cache_path(text, backend=backend, model=model, cache_dir=cache_dir)
    if cache and path.is_file():
        try:
            payload = json.loads(path.read_text())
            return EnhanceResult(
                original=str(payload.get("original", text)),
                enhanced=str(payload["enhanced"]),
                # Mark cache hits explicitly so metrics/logs can distinguish
                # a free replay from a fresh template/mlx-lm call.
                backend="cache",
                elapsed_s=0.0,
                model=payload.get("model"),
            )
        except (OSError, KeyError, json.JSONDecodeError):
            pass

    result = enhance_prompt(
        text,
        backend=backend,
        model=model,
        system_prompt=system_prompt,
        max_tokens=max_tokens,
    )
    if cache:
        try:
            path.parent.mkdir(parents=True, exist_ok=True)
            path.write_text(
                json.dumps(
                    {
                        "original": result.original,
                        "enhanced": result.enhanced,
                        "backend": result.backend,
                        "model": result.model,
                    },
                    indent=2,
                ))
        except OSError as exc:  # pragma: no cover - cache is best-effort
            logger.info("[MLX enhance] cache write skipped: %s", exc)
    return result

fastvideo.mlx_runtime.mlx_block_weights_from_diffusers_safetensors

mlx_block_weights_from_diffusers_safetensors(checkpoint_path: str | Path, *, block_index: int = 0, quantization: str | MLXQuantizationSpec | None = None, dtype=None) -> dict[str, array]

Load one Diffusers-format Wan block into the MLX dense-block key layout.

Source code in fastvideo/mlx_runtime/fastwan.py
def mlx_block_weights_from_diffusers_safetensors(
    checkpoint_path: str | Path,
    *,
    block_index: int = 0,
    quantization: str | MLXQuantizationSpec | None = None,
    dtype=None,
) -> dict[str, mx.array]:
    """Load one Diffusers-format Wan block into the MLX dense-block key layout."""
    from safetensors import safe_open

    prefix = f"blocks.{block_index}."
    key_map = _WAN_BLOCK_KEY_MAP

    spec = MLXQuantizationSpec.from_name(quantization) if (quantization is None
                                                           or isinstance(quantization, str)) else quantization
    ensure_quantization_supported(spec)
    matrix_targets = {target for target in key_map.values() if target.endswith(".weight") and "norm" not in target}
    weights = {}
    with safe_open(str(checkpoint_path), framework="pt", device="cpu") as handle:
        available = set(handle.keys())
        for source_name, target_name in key_map.items():
            full = prefix + source_name
            if full not in available:
                # Biases are optional: e.g. Wan2.1-14B has bias-free attention/FFN.
                # The block forward already fetches biases via ``.get(...)``.
                if source_name.endswith(".bias"):
                    continue
                raise KeyError(f"missing required block weight: {full}")
            array = _load_mx_array_from_safetensor(handle, full, dtype)
            loaded = quantize_matrix(array, spec) if target_name in matrix_targets else array
            _eval_loaded_weight(loaded)
            weights[target_name] = loaded
            del array
    return weights

fastvideo.mlx_runtime.plan_fast_spatial

plan_fast_spatial(*, height: int, width: int, num_frames: int, spatial_scale: int = 2, vae_spatial_compression: int = 8, vae_temporal_compression: int = 4, patch_size: tuple[int, int, int] = (1, 2, 2), upsample_mode: str = DEFAULT_PIXEL_UPSAMPLE_MODE, sharpen: float = DEFAULT_FAST_SPATIAL_SHARPEN, enabled: bool = True) -> FastSpatialPlan

Build a plan for reduced-resolution denoising followed by pixel-space upsampling.

Parameters:

Name Type Description Default
upsample_mode str

Pixel interpolation kernel, one of :data:~fastvideo.mlx_runtime.frame_upsample.PIXEL_UPSAMPLE_MODES.

DEFAULT_PIXEL_UPSAMPLE_MODE
sharpen float

Unsharp strength applied after the resize.

DEFAULT_FAST_SPATIAL_SHARPEN

Returns:

Name Type Description
FastSpatialPlan FastSpatialPlan

The validated spatial-fast processing plan.

Raises:

Type Description
ValueError

If the upsample mode is unsupported or sharpen is negative.

Source code in fastvideo/mlx_runtime/fast_spatial.py
def plan_fast_spatial(
    *,
    height: int,
    width: int,
    num_frames: int,
    spatial_scale: int = 2,
    vae_spatial_compression: int = 8,
    vae_temporal_compression: int = 4,
    patch_size: tuple[int, int, int] = (1, 2, 2),
    upsample_mode: str = DEFAULT_PIXEL_UPSAMPLE_MODE,
    sharpen: float = DEFAULT_FAST_SPATIAL_SHARPEN,
    enabled: bool = True,
) -> FastSpatialPlan:
    """
    Build a plan for reduced-resolution denoising followed by pixel-space upsampling.

    Parameters:
        upsample_mode (str): Pixel interpolation kernel, one of
            :data:`~fastvideo.mlx_runtime.frame_upsample.PIXEL_UPSAMPLE_MODES`.
        sharpen (float): Unsharp strength applied after the resize.

    Returns:
        FastSpatialPlan: The validated spatial-fast processing plan.

    Raises:
        ValueError: If the upsample mode is unsupported or ``sharpen`` is negative.
    """
    if upsample_mode not in PIXEL_UPSAMPLE_MODES:
        raise ValueError(f"Unsupported upsample mode: {upsample_mode!r} "
                         f"(expected one of {', '.join(PIXEL_UPSAMPLE_MODES)})")
    if sharpen < 0.0:
        raise ValueError(f"sharpen must be >= 0, got {sharpen}")
    plan = plan_refine_resolutions(
        height=height,
        width=width,
        num_frames=num_frames,
        spatial_scale=spatial_scale,
        vae_spatial_compression=vae_spatial_compression,
        vae_temporal_compression=vae_temporal_compression,
        patch_size=patch_size,
        enabled=enabled,
        mode_label="fast-spatial",
    )
    if plan.spatial_scale > 1:
        logger.info(
            "[MLX fast-spatial] denoise+decode %dx%d → upsample %dx to %dx%d (%s, sharpen=%.2f)",
            plan.stage1_width,
            plan.stage1_height,
            plan.spatial_scale,
            plan.target_width,
            plan.target_height,
            upsample_mode,
            sharpen,
        )
    return FastSpatialPlan(plan=plan, upsample_mode=upsample_mode, sharpen=sharpen)

fastvideo.mlx_runtime.plan_refine_resolutions

plan_refine_resolutions(*, height: int, width: int, num_frames: int, spatial_scale: int = 2, vae_spatial_compression: int = 8, vae_temporal_compression: int = 4, patch_size: tuple[int, int, int] = (1, 2, 2), enabled: bool = True, mode_label: str = 'Refine') -> RefinePlan

Validate the requested dimensions and create the stage-1 and target-resolution refinement plan.

Parameters:

Name Type Description Default
height int

Target image height in pixels.

required
width int

Target image width in pixels.

required
num_frames int

Number of frames in the input sequence.

required
spatial_scale int

Factor used to reduce spatial dimensions for stage 1.

2
vae_spatial_compression int

Spatial compression factor of the VAE.

8
vae_temporal_compression int

Temporal compression factor of the VAE.

4
patch_size tuple[int, int, int]

Temporal and spatial patch dimensions used to validate latent-grid alignment.

(1, 2, 2)
enabled bool

Whether to use two-pass refinement.

True
mode_label str

Name of the calling mode, used to prefix validation errors so --fast-spatial failures do not read as refine failures.

'Refine'

Returns:

Name Type Description
RefinePlan RefinePlan

The validated stage-1 and target-resolution plan.

Source code in fastvideo/mlx_runtime/refine.py
def plan_refine_resolutions(
        *,
        height: int,
        width: int,
        num_frames: int,
        spatial_scale: int = 2,
        vae_spatial_compression: int = 8,
        vae_temporal_compression: int = 4,
        patch_size: tuple[int, int, int] = (1, 2, 2),
        enabled: bool = True,
        mode_label: str = "Refine",
) -> RefinePlan:
    """
    Validate the requested dimensions and create the stage-1 and target-resolution refinement plan.

    Parameters:
        height (int): Target image height in pixels.
        width (int): Target image width in pixels.
        num_frames (int): Number of frames in the input sequence.
        spatial_scale (int): Factor used to reduce spatial dimensions for stage 1.
        vae_spatial_compression (int): Spatial compression factor of the VAE.
        vae_temporal_compression (int): Temporal compression factor of the VAE.
        patch_size (tuple[int, int, int]): Temporal and spatial patch dimensions used to validate latent-grid alignment.
        enabled (bool): Whether to use two-pass refinement.
        mode_label (str): Name of the calling mode, used to prefix validation
            errors so ``--fast-spatial`` failures do not read as refine failures.

    Returns:
        RefinePlan: The validated stage-1 and target-resolution plan.
    """
    if height <= 0 or width <= 0:
        raise ValueError(f"height/width must be positive, got {height}x{width}")
    if spatial_scale < 1:
        raise ValueError(f"spatial_scale must be >= 1, got {spatial_scale}")
    if num_frames <= 0:
        raise ValueError(f"num_frames must be positive, got {num_frames}")
    if vae_spatial_compression < 1 or vae_temporal_compression < 1:
        raise ValueError("VAE compression factors must be positive")
    if height % vae_spatial_compression != 0 or width % vae_spatial_compression != 0:
        raise ValueError(f"height/width must be divisible by vae_spatial_compression={vae_spatial_compression} "
                         f"(got {height}x{width}).")
    if (num_frames - 1) % vae_temporal_compression != 0:
        raise ValueError(f"num_frames must be 1 modulo vae_temporal_compression={vae_temporal_compression} "
                         f"(got {num_frames}).")

    if not enabled or spatial_scale == 1:
        plan = RefinePlan(
            target_height=height,
            target_width=width,
            stage1_height=height,
            stage1_width=width,
            spatial_scale=1,
            vae_spatial_compression=vae_spatial_compression,
            vae_temporal_compression=vae_temporal_compression,
            num_frames=num_frames,
        )
        _validate_plan(plan, patch_size=patch_size, mode_label=mode_label)
        return plan

    if height % spatial_scale != 0 or width % spatial_scale != 0:
        raise ValueError(f"{mode_label} requires height/width divisible by spatial_scale={spatial_scale} "
                         f"(got {height}x{width}).")

    stage1_height = height // spatial_scale
    stage1_width = width // spatial_scale
    # Stage-1 must land on a VAE-aligned grid so the first denoise produces
    # valid latents; the LTX-2 init stage enforces the same constraint.
    if (stage1_height % vae_spatial_compression != 0 or stage1_width % vae_spatial_compression != 0):
        raise ValueError(f"{mode_label} requires height/width divisible by "
                         f"{spatial_scale * vae_spatial_compression} "
                         f"(got {height}x{width}, vae_spatial={vae_spatial_compression}).")

    plan = RefinePlan(
        target_height=height,
        target_width=width,
        stage1_height=stage1_height,
        stage1_width=stage1_width,
        spatial_scale=spatial_scale,
        vae_spatial_compression=vae_spatial_compression,
        vae_temporal_compression=vae_temporal_compression,
        num_frames=num_frames,
    )
    _validate_plan(plan, patch_size=patch_size, mode_label=mode_label)
    logger.info(
        "[MLX refine] enabled: stage1=%dx%d stage2=%dx%d scale=%dx",
        stage1_width,
        stage1_height,
        width,
        height,
        spatial_scale,
    )
    return plan

fastvideo.mlx_runtime.prepare_refine_latents

prepare_refine_latents(clean_latents: Any, *, scale: int = 2, sigma: float = DEFAULT_REFINE_SIGMA, noise: Any | None = None, add_noise_flag: bool = True, upsample_mode: str = 'bilinear', seed: int | None = None) -> Any

Upsample clean latents spatially and optionally mix them with Gaussian noise.

Parameters:

Name Type Description Default
clean_latents Any

The stage-1 latent tensor.

required
sigma float

Noise mixing factor between 0 and 1.

DEFAULT_REFINE_SIGMA
noise Any | None

Optional noise tensor to mix with the upsampled latents.

None
add_noise_flag bool

Whether to apply noise mixing.

True
upsample_mode str

Spatial interpolation mode.

'bilinear'
seed int | None

Optional seed for generated noise.

None

Returns:

Type Description
Any

The upsampled latents, optionally mixed with noise.

Raises:

Type Description
ValueError

If sigma is outside the range from 0 to 1.

Source code in fastvideo/mlx_runtime/refine.py
def prepare_refine_latents(
    clean_latents: Any,
    *,
    scale: int = 2,
    sigma: float = DEFAULT_REFINE_SIGMA,
    noise: Any | None = None,
    add_noise_flag: bool = True,
    upsample_mode: str = "bilinear",
    seed: int | None = None,
) -> Any:
    """
    Upsample clean latents spatially and optionally mix them with Gaussian noise.

    Parameters:
        clean_latents: The stage-1 latent tensor.
        sigma: Noise mixing factor between 0 and 1.
        noise: Optional noise tensor to mix with the upsampled latents.
        add_noise_flag: Whether to apply noise mixing.
        upsample_mode: Spatial interpolation mode.
        seed: Optional seed for generated noise.

    Returns:
        The upsampled latents, optionally mixed with noise.

    Raises:
        ValueError: If sigma is outside the range from 0 to 1.
    """
    if sigma < 0.0 or sigma > 1.0:
        raise ValueError(f"sigma must be in [0, 1], got {sigma}")

    upsampled = upsample_latents_spatial(clean_latents, scale=scale, mode=upsample_mode)
    if not add_noise_flag or sigma == 0.0:
        return upsampled

    is_mlx = hasattr(upsampled, "dtype") and type(upsampled).__module__.startswith("mlx")
    if noise is None:
        noise = _draw_noise_like(upsampled, seed=seed, is_mlx=is_mlx)
    return add_noise(upsampled, noise, float(sigma))

fastvideo.mlx_runtime.quantization_support_error

quantization_support_error(spec: MLXQuantizationSpec) -> str | None

Probe whether the installed MLX build supports spec.

Runs a tiny mx.quantize + mx.quantized_matmul with exactly the arguments :func:quantize_matrix / :func:linear use, so the result reflects the real runtime path. The affine (int8/int4) modes are stable across MLX releases, but the mxfp8/mxfp4/nvfp4 mode strings require newer MLX builds and raise otherwise. Returns None when the mode works, else the underlying error message. Cached per spec.

Source code in fastvideo/mlx_runtime/fastwan.py
def quantization_support_error(spec: MLXQuantizationSpec) -> str | None:
    """Probe whether the installed MLX build supports ``spec``.

    Runs a tiny ``mx.quantize`` + ``mx.quantized_matmul`` with exactly the
    arguments :func:`quantize_matrix` / :func:`linear` use, so the result
    reflects the real runtime path. The affine (int8/int4) modes are stable
    across MLX releases, but the ``mxfp8``/``mxfp4``/``nvfp4`` mode strings
    require newer MLX builds and raise otherwise. Returns ``None`` when the
    mode works, else the underlying error message. Cached per spec.
    """
    key = (spec.mode, spec.bits, spec.group_size)
    if key not in _QUANT_SUPPORT_CACHE:
        import mlx.core as mx

        try:
            probe_dim = max(spec.group_size or 0, 64)
            weight = mx.zeros((probe_dim, probe_dim), dtype=mx.float16)
            quantized = quantize_matrix(weight, spec)
            y = linear(mx.zeros((1, probe_dim), dtype=mx.float16), quantized)
            mx.eval(y)
            _QUANT_SUPPORT_CACHE[key] = None
        except Exception as exc:  # noqa: BLE001 - MLX raises varied error types per backend/version.
            _QUANT_SUPPORT_CACHE[key] = f"{type(exc).__name__}: {exc}"
    return _QUANT_SUPPORT_CACHE[key]

fastvideo.mlx_runtime.refine_sigma_from_schedule

refine_sigma_from_schedule(schedule: MLXDMDSchedule, timesteps: Sequence[float | int]) -> float

Derive the refinement noise level from the first refinement timestep.

Parameters:

Name Type Description Default
schedule MLXDMDSchedule

Schedule used to map timesteps to noise levels.

required
timesteps Sequence[float | int]

Refinement timesteps, whose first value determines the sigma.

required

Returns:

Name Type Description
float float

Sigma corresponding to the first refinement timestep.

Raises:

Type Description
ValueError

If timesteps is empty.

Source code in fastvideo/mlx_runtime/refine.py
def refine_sigma_from_schedule(
    schedule: MLXDMDSchedule,
    timesteps: Sequence[float | int],
) -> float:
    """Derive the refinement noise level from the first refinement timestep.

    Parameters:
        schedule (MLXDMDSchedule): Schedule used to map timesteps to noise levels.
        timesteps (Sequence[float | int]): Refinement timesteps, whose first value determines the sigma.

    Returns:
        float: Sigma corresponding to the first refinement timestep.

    Raises:
        ValueError: If `timesteps` is empty.
    """
    if not timesteps:
        raise ValueError("timesteps must be non-empty to derive a refine sigma")
    return float(schedule.sigma_for(float(timesteps[0])))

fastvideo.mlx_runtime.resolve_spatial_mode

resolve_spatial_mode(*, refine: bool, fast_spatial: bool) -> str

Select the active spatial processing mode, with refinement taking precedence.

Returns:

Name Type Description
str str

"refine" when refinement is enabled, "fast_spatial" when spatial-fast processing is enabled, or "off" otherwise.

Source code in fastvideo/mlx_runtime/fast_spatial.py
def resolve_spatial_mode(
    *,
    refine: bool,
    fast_spatial: bool,
) -> str:
    """Select the active spatial processing mode, with refinement taking precedence.

    Returns:
        str: ``"refine"`` when refinement is enabled, ``"fast_spatial"`` when
            spatial-fast processing is enabled, or ``"off"`` otherwise.
    """
    if refine:
        return "refine"
    if fast_spatial:
        return "fast_spatial"
    return "off"

fastvideo.mlx_runtime.run_dmd_loop

run_dmd_loop(*, dit: Any, latents: Any, encoder_hidden_states: Any, freqs_cis: tuple[Any, Any], timesteps: Sequence[float | int], schedule: MLXDMDSchedule, mx_dtype: Any, seed: int | None = None, step_callback: Callable[[int, int], None] | None = None, label: str = 'denoise') -> Any

Denoise latents over the supplied timesteps using the DMD schedule.

Parameters:

Name Type Description Default
timesteps Sequence[float | int]

Denoising timesteps in execution order.

required
seed int | None

Seed for reproducible intermediate noise generation.

None
step_callback Callable[[int, int], None] | None

Callback receiving the completed step number and total step count.

None
label str

Label used for progress output when no callback is provided.

'denoise'

Returns:

Name Type Description
Any Any

The denoised latents.

Source code in fastvideo/mlx_runtime/refine.py
def run_dmd_loop(
    *,
    dit: Any,
    latents: Any,
    encoder_hidden_states: Any,
    freqs_cis: tuple[Any, Any],
    timesteps: Sequence[float | int],
    schedule: MLXDMDSchedule,
    mx_dtype: Any,
    seed: int | None = None,
    step_callback: Callable[[int, int], None] | None = None,
    label: str = "denoise",
) -> Any:
    """
    Denoise latents over the supplied timesteps using the DMD schedule.

    Parameters:
        timesteps (Sequence[float | int]): Denoising timesteps in execution order.
        seed (int | None): Seed for reproducible intermediate noise generation.
        step_callback (Callable[[int, int], None] | None): Callback receiving the
            completed step number and total step count.
        label (str): Label used for progress output when no callback is provided.

    Returns:
        Any: The denoised latents.
    """
    import mlx.core as mx

    renoise_rng = np.random.default_rng(seed) if seed is not None else None
    latents_out = latents
    n_steps = len(timesteps)
    for step_index, timestep in enumerate(timesteps):
        noise_input = latents_out
        ts_val = float(timestep)
        timestep_mx = mx.array([ts_val]).astype(mx.float32)
        noise_pred = dit(
            latents_out.astype(mx_dtype),
            encoder_hidden_states,
            timestep_mx,
            freqs_cis,
        )
        noise_input_f32 = noise_input.astype(mx.float32)
        pred_noise_f32 = noise_pred.astype(mx.float32)
        if step_index < n_steps - 1:
            next_ts: float | None = float(timesteps[step_index + 1])
            if renoise_rng is not None:
                renoise = mx.array(renoise_rng.standard_normal(tuple(noise_input_f32.shape)).astype(np.float32))
            else:
                renoise = mx.random.normal(noise_input_f32.shape).astype(mx.float32)
        else:
            next_ts, renoise = None, None
        latents_out = dmd_step(
            latents=noise_input_f32,
            noise_input_latent=noise_input_f32,
            pred_noise=pred_noise_f32,
            schedule=schedule,
            timestep=ts_val,
            next_timestep=next_ts,
            noise=renoise,
        ).astype(mx_dtype)
        mx.eval(latents_out)
        if step_callback is not None:
            step_callback(step_index + 1, n_steps)
        else:
            print(f"{label} step {step_index + 1}/{n_steps} complete")
    return latents_out

fastvideo.mlx_runtime.run_two_pass_dmd

run_two_pass_dmd(*, dit: Any, encoder_hidden_states: Any, noise_latents_stage1: Any, freqs_cis_stage1: tuple[Any, Any], freqs_cis_stage2: tuple[Any, Any] | None, plan: RefinePlan, schedule: MLXDMDSchedule, timesteps: Sequence[float | int], refine_timesteps: Sequence[float | int] | None = None, mx_dtype: Any, seed: int = 0, add_noise_flag: bool = True, upsample_mode: str = 'bilinear', refine_sigma: float | None = None, step_callback: Callable[[str, int, int], None] | None = None) -> TwoPassResult

Run base denoising and, when enabled, spatial refinement denoising.

Parameters:

Name Type Description Default
dit Any

DiT callable used for both denoising passes.

required
encoder_hidden_states Any

Prompt embeddings shared across both passes.

required
noise_latents_stage1 Any

Initial stage-1 noise latents.

required
freqs_cis_stage1 tuple[Any, Any]

RoPE tables for the stage-1 resolution.

required
freqs_cis_stage2 tuple[Any, Any] | None

RoPE tables for the stage-2 resolution, required when refinement is enabled.

required
plan RefinePlan

Refinement geometry and configuration.

required
schedule MLXDMDSchedule

Flow-matching schedule used by both passes.

required
timesteps Sequence[float | int]

Stage-1 denoising timesteps.

required
refine_timesteps Sequence[float | int] | None

Stage-2 denoising timesteps. Uses timesteps when omitted.

None
mx_dtype Any

MLX dtype used for DiT inputs and outputs.

required
seed int

Base seed for reproducible noise generation.

0
add_noise_flag bool

Whether to add noise to the upsampled stage-1 latents.

True
upsample_mode str

Spatial upsampling mode, either "bilinear" or "nearest".

'bilinear'
refine_sigma float | None

Stage-2 starting noise level. Derived from the first refinement timestep when omitted.

None
step_callback Callable[[str, int, int], None] | None

Optional callback receiving the phase name, step index, and total step count.

None

Returns:

Type Description
TwoPassResult

TwoPassResult containing the final latents, stage-1 latents, refinement plan, and applied refinement sigma.

Raises:

Type Description
ValueError

If refinement is enabled without stage-2 RoPE tables, without refinement timesteps, or if upsampled latents do not match the planned stage-2 dimensions.

Source code in fastvideo/mlx_runtime/refine.py
def run_two_pass_dmd(
    *,
    dit: Any,
    encoder_hidden_states: Any,
    noise_latents_stage1: Any,
    freqs_cis_stage1: tuple[Any, Any],
    freqs_cis_stage2: tuple[Any, Any] | None,
    plan: RefinePlan,
    schedule: MLXDMDSchedule,
    timesteps: Sequence[float | int],
    refine_timesteps: Sequence[float | int] | None = None,
    mx_dtype: Any,
    seed: int = 0,
    add_noise_flag: bool = True,
    upsample_mode: str = "bilinear",
    refine_sigma: float | None = None,
    step_callback: Callable[[str, int, int], None] | None = None,
) -> TwoPassResult:
    """
    Run base denoising and, when enabled, spatial refinement denoising.

    Parameters:
        dit: DiT callable used for both denoising passes.
        encoder_hidden_states: Prompt embeddings shared across both passes.
        noise_latents_stage1: Initial stage-1 noise latents.
        freqs_cis_stage1: RoPE tables for the stage-1 resolution.
        freqs_cis_stage2: RoPE tables for the stage-2 resolution, required when refinement is enabled.
        plan: Refinement geometry and configuration.
        schedule: Flow-matching schedule used by both passes.
        timesteps: Stage-1 denoising timesteps.
        refine_timesteps: Stage-2 denoising timesteps. Uses `timesteps` when omitted.
        mx_dtype: MLX dtype used for DiT inputs and outputs.
        seed: Base seed for reproducible noise generation.
        add_noise_flag: Whether to add noise to the upsampled stage-1 latents.
        upsample_mode: Spatial upsampling mode, either `"bilinear"` or `"nearest"`.
        refine_sigma: Stage-2 starting noise level. Derived from the first refinement timestep when omitted.
        step_callback: Optional callback receiving the phase name, step index, and total step count.

    Returns:
        TwoPassResult containing the final latents, stage-1 latents, refinement plan, and applied refinement sigma.

    Raises:
        ValueError: If refinement is enabled without stage-2 RoPE tables, without refinement timesteps, or if upsampled latents do not match the planned stage-2 dimensions.
    """
    stage1_cb = None
    stage2_cb = None
    if step_callback is not None:
        stage1_cb = lambda i, n: step_callback("stage1", i, n)  # noqa: E731
        stage2_cb = lambda i, n: step_callback("stage2", i, n)  # noqa: E731

    stage1_latents = run_dmd_loop(
        dit=dit,
        latents=noise_latents_stage1,
        encoder_hidden_states=encoder_hidden_states,
        freqs_cis=freqs_cis_stage1,
        timesteps=timesteps,
        schedule=schedule,
        mx_dtype=mx_dtype,
        seed=seed,
        step_callback=stage1_cb,
        label="stage1 denoise",
    )

    if plan.spatial_scale == 1:
        return TwoPassResult(
            latents=stage1_latents,
            stage1_latents=stage1_latents,
            plan=plan,
            refine_sigma=0.0,
        )

    if freqs_cis_stage2 is None:
        raise ValueError("freqs_cis_stage2 is required when refine spatial_scale > 1")

    if refine_timesteps is not None:
        stage2_timesteps = [float(step) for step in refine_timesteps]
        if not stage2_timesteps:
            raise ValueError("refine_timesteps must be non-empty when refine is enabled")
    else:
        # Not `list(timesteps)`: the stage-1 grid opens at full noise, which
        # would weight the stage-1 result at zero. See default_refine_timesteps.
        stage2_timesteps = default_refine_timesteps(schedule, timesteps)
    grid_sigma = refine_sigma_from_schedule(schedule, stage2_timesteps)
    sigma = float(refine_sigma) if refine_sigma is not None else grid_sigma
    if refine_sigma is not None and abs(sigma - grid_sigma) > 1e-6:
        # The loop tells the DiT `stage2_timesteps[0]`, which implies grid_sigma.
        # Overriding the hand-off noise level breaks that correspondence, so the
        # model is denoising from a level it was not told about. Useful for
        # exploring schedules that bottom out too high, but say so out loud.
        logger.warning(
            "[MLX refine] refine_sigma=%.4f overrides the schedule's %.4f for timestep %g; "
            "the DiT is told a timestep that no longer matches the noise it receives.",
            sigma,
            grid_sigma,
            stage2_timesteps[0],
        )

    # A hand-off at sigma >= 1 is `0 * upsampled + 1 * noise`: stage 1 is
    # thrown away and refine degrades to a plain full-res run at 2x the cost.
    # Fail loudly rather than silently burning the first pass.
    if add_noise_flag and sigma >= 1.0:
        raise ValueError(f"Refine hand-off sigma={sigma:.4f} (from stage-2 timestep "
                         f"{stage2_timesteps[0]:g}) discards the stage-1 result entirely: "
                         "the upsampled latents are weighted (1 - sigma) = 0. Start the "
                         "stage-2 grid below the full-noise timestep, or pass "
                         "add_noise_flag=False to hand off the clean upsample.")

    stage2_input = prepare_refine_latents(
        stage1_latents,
        scale=plan.spatial_scale,
        sigma=sigma,
        add_noise_flag=add_noise_flag,
        upsample_mode=upsample_mode,
        seed=seed + 1,
    )

    # Shape guard: upsampled latents must match the stage-2 RoPE grid.
    expected_h = plan.stage2_latent_height
    expected_w = plan.stage2_latent_width
    got_h, got_w = int(stage2_input.shape[-2]), int(stage2_input.shape[-1])
    if got_h != expected_h or got_w != expected_w:
        raise ValueError(f"Refine upsample produced {got_h}x{got_w} latents, expected "
                         f"{expected_h}x{expected_w} for target "
                         f"{plan.target_height}x{plan.target_width}.")

    logger.info(
        "[MLX refine] stage2 start: latent=%dx%d sigma=%.4f steps=%d",
        expected_w,
        expected_h,
        sigma,
        len(stage2_timesteps),
    )

    stage2_latents = run_dmd_loop(
        dit=dit,
        latents=stage2_input,
        encoder_hidden_states=encoder_hidden_states,
        freqs_cis=freqs_cis_stage2,
        timesteps=stage2_timesteps,
        schedule=schedule,
        mx_dtype=mx_dtype,
        seed=seed + 2,
        step_callback=stage2_cb,
        label="stage2 refine",
    )
    return TwoPassResult(
        latents=stage2_latents,
        stage1_latents=stage1_latents,
        plan=plan,
        refine_sigma=sigma,
    )

fastvideo.mlx_runtime.save_mlx_dit_checkpoint

save_mlx_dit_checkpoint(dit: MLXWanDiT, checkpoint_dir: str | Path) -> Path

Save a plain or quantized MLX Wan DiT checkpoint to a directory.

Parameters:

Name Type Description Default
dit MLXWanDiT

Model whose weights and configuration will be saved.

required
checkpoint_dir str | Path

Destination directory for the checkpoint.

required

Returns:

Name Type Description
Path Path

Path to the checkpoint directory.

Source code in fastvideo/mlx_runtime/checkpoint.py
def save_mlx_dit_checkpoint(dit: MLXWanDiT, checkpoint_dir: str | Path) -> Path:
    """Save a plain or quantized MLX Wan DiT checkpoint to a directory.

    Parameters:
        dit (MLXWanDiT): Model whose weights and configuration will be saved.
        checkpoint_dir (str | Path): Destination directory for the checkpoint.

    Returns:
        Path: Path to the checkpoint directory.
    """
    import mlx.core as mx

    checkpoint_dir = Path(checkpoint_dir)
    arrays: dict[str, Any] = {}
    quantized: dict[str, dict[str, Any]] = {}
    spec: MLXQuantizationSpec | None = None
    for key, value in _flatten_weights(dit).items():
        if isinstance(value, QuantizedMatrix):
            if spec is not None and value.spec != spec:
                raise ValueError(f"Mixed quantization specs in one checkpoint ({spec} vs {value.spec} at '{key}') "
                                 "are not supported.")
            spec = value.spec
            arrays[key] = value.weight
            arrays[f"{key}.scales"] = value.scales
            if value.biases is not None:
                arrays[f"{key}.biases"] = value.biases
            quantized[key] = {
                "dequantized_dtype": _dtype_name(value.dequantized_dtype),
                "has_biases": value.biases is not None,
            }
        else:
            arrays[key] = value

    manifest = {
        "format_version": FORMAT_VERSION,
        "config": dit.config,
        "num_blocks": len(dit.blocks),
        "quantization": None if spec is None else {
            "mode": spec.mode,
            "bits": spec.bits,
            "group_size": spec.group_size,
        },
        "quantized_keys": quantized,
    }

    manifest_json = json.dumps(manifest, indent=2)
    checkpoint_dir.parent.mkdir(parents=True, exist_ok=True)
    staging_dir = Path(tempfile.mkdtemp(dir=checkpoint_dir.parent, prefix=f".{checkpoint_dir.name}.staging-"))
    backup_root: Path | None = None
    try:
        staged_weights = staging_dir / WEIGHTS_FILENAME
        staged_manifest = staging_dir / MANIFEST_FILENAME
        mx.save_safetensors(str(staged_weights), arrays)
        staged_manifest.write_text(manifest_json)
        if checkpoint_dir.exists():
            backup_root = Path(tempfile.mkdtemp(dir=checkpoint_dir.parent, prefix=f".{checkpoint_dir.name}.backup-"))
            try:
                checkpoint_dir.replace(backup_root / checkpoint_dir.name)
            except Exception:
                shutil.rmtree(backup_root, ignore_errors=True)
                raise
        try:
            staging_dir.replace(checkpoint_dir)
        except Exception:
            if backup_root is not None:
                (backup_root / checkpoint_dir.name).replace(checkpoint_dir)
                shutil.rmtree(backup_root, ignore_errors=True)
            raise
        if backup_root is not None:
            shutil.rmtree(backup_root, ignore_errors=True)
    finally:
        shutil.rmtree(staging_dir, ignore_errors=True)
    logger.info("Saved MLX DiT checkpoint (%d arrays, quantization=%s) to %s", len(arrays),
                spec.label if spec else "none", checkpoint_dir)
    return checkpoint_dir

fastvideo.mlx_runtime.unsharp

unsharp(frame: ndarray, amount: float) -> ndarray

Light unsharp mask, used to counter resampling / optical-flow softening.

Parameters:

Name Type Description Default
frame ndarray

HxWx3 uint8 RGB frame.

required
amount float

Strength; 0 returns the frame unchanged.

required

Returns:

Type Description
ndarray

np.ndarray: A new frame; the input is never modified in place.

Source code in fastvideo/mlx_runtime/frame_upsample.py
def unsharp(frame: np.ndarray, amount: float) -> np.ndarray:
    """Light unsharp mask, used to counter resampling / optical-flow softening.

    Parameters:
        frame (np.ndarray): HxWx3 uint8 RGB frame.
        amount (float): Strength; ``0`` returns the frame unchanged.

    Returns:
        np.ndarray: A new frame; the input is never modified in place.
    """
    if amount <= 0.0:
        return frame
    import cv2

    blur = cv2.GaussianBlur(frame, (0, 0), 1.0)
    return cv2.addWeighted(frame, 1.0 + amount, blur, -amount, 0)

fastvideo.mlx_runtime.upsample_frame

upsample_frame(frame: ndarray, *, width: int, height: int, mode: str = DEFAULT_PIXEL_UPSAMPLE_MODE, sharpen: float = 0.0) -> ndarray

Resample one decoded RGB frame to the target pixel size.

Parameters:

Name Type Description Default
frame ndarray

HxWx3 uint8 RGB frame.

required
width int

Target width in pixels.

required
height int

Target height in pixels.

required
mode str

Interpolation kernel, one of :data:PIXEL_UPSAMPLE_MODES.

DEFAULT_PIXEL_UPSAMPLE_MODE
sharpen float

Unsharp strength applied after the resize.

0.0

Returns:

Type Description
ndarray

np.ndarray: A new frame at height x width; already-correct sizes are still passed through sharpen.

Raises:

Type Description
ValueError

If the frame is not HxWx3, or the target size is not positive.

Source code in fastvideo/mlx_runtime/frame_upsample.py
def upsample_frame(
    frame: np.ndarray,
    *,
    width: int,
    height: int,
    mode: str = DEFAULT_PIXEL_UPSAMPLE_MODE,
    sharpen: float = 0.0,
) -> np.ndarray:
    """
    Resample one decoded RGB frame to the target pixel size.

    Parameters:
        frame (np.ndarray): HxWx3 uint8 RGB frame.
        width (int): Target width in pixels.
        height (int): Target height in pixels.
        mode (str): Interpolation kernel, one of :data:`PIXEL_UPSAMPLE_MODES`.
        sharpen (float): Unsharp strength applied after the resize.

    Returns:
        np.ndarray: A new frame at ``height x width``; already-correct sizes
            are still passed through ``sharpen``.

    Raises:
        ValueError: If the frame is not HxWx3, or the target size is not positive.
    """
    import cv2

    array = np.asarray(frame)
    if array.ndim != 3 or array.shape[2] != 3:
        raise ValueError(f"frame must have shape HxWx3, got {array.shape}")
    if width <= 0 or height <= 0:
        raise ValueError(f"target size must be positive, got {width}x{height}")
    if array.dtype != np.uint8:
        array = np.clip(array, 0, 255).astype(np.uint8)

    if (array.shape[0], array.shape[1]) != (height, width):
        array = cv2.resize(array, (width, height), interpolation=_interpolation_flag(mode))
    return unsharp(array, sharpen)

fastvideo.mlx_runtime.upsample_frames

upsample_frames(frames: Iterable[ndarray], *, width: int, height: int, mode: str = DEFAULT_PIXEL_UPSAMPLE_MODE, sharpen: float = 0.0) -> list[ndarray]

Resample every decoded frame to the target pixel size.

Parameters:

Name Type Description Default
frames Iterable[ndarray]

Decoded HxWx3 uint8 RGB frames.

required
width int

Target width in pixels.

required
height int

Target height in pixels.

required
mode str

Interpolation kernel, one of :data:PIXEL_UPSAMPLE_MODES.

DEFAULT_PIXEL_UPSAMPLE_MODE
sharpen float

Unsharp strength applied after each resize.

0.0

Returns:

Type Description
list[ndarray]

list[np.ndarray]: New frames at the target size, in input order.

Source code in fastvideo/mlx_runtime/frame_upsample.py
def upsample_frames(
    frames: Iterable[np.ndarray],
    *,
    width: int,
    height: int,
    mode: str = DEFAULT_PIXEL_UPSAMPLE_MODE,
    sharpen: float = 0.0,
) -> list[np.ndarray]:
    """
    Resample every decoded frame to the target pixel size.

    Parameters:
        frames (Iterable[np.ndarray]): Decoded HxWx3 uint8 RGB frames.
        width (int): Target width in pixels.
        height (int): Target height in pixels.
        mode (str): Interpolation kernel, one of :data:`PIXEL_UPSAMPLE_MODES`.
        sharpen (float): Unsharp strength applied after each resize.

    Returns:
        list[np.ndarray]: New frames at the target size, in input order.
    """
    return [upsample_frame(frame, width=width, height=height, mode=mode, sharpen=sharpen) for frame in frames]

fastvideo.mlx_runtime.upsample_latents_spatial

upsample_latents_spatial(latents: Any, *, scale: int = 2, mode: str = 'bilinear') -> Any

Upsample the spatial dimensions of 5-D latent arrays while preserving the batch, channel, and temporal dimensions.

Parameters:

Name Type Description Default
latents Any

Latents with shape (B, C, T, H, W).

required
scale int

Integer factor for enlarging the spatial dimensions.

2
mode str

Interpolation mode, either "nearest" or "bilinear".

'bilinear'

Returns:

Name Type Description
Any Any

Latents with shape (B, C, T, H * scale, W * scale).

Source code in fastvideo/mlx_runtime/refine.py
def upsample_latents_spatial(
    latents: Any,
    *,
    scale: int = 2,
    mode: str = "bilinear",
) -> Any:
    """
    Upsample the spatial dimensions of 5-D latent arrays while preserving the batch, channel, and temporal dimensions.

    Parameters:
        latents (Any): Latents with shape ``(B, C, T, H, W)``.
        scale (int): Integer factor for enlarging the spatial dimensions.
        mode (str): Interpolation mode, either ``"nearest"`` or ``"bilinear"``.

    Returns:
        Any: Latents with shape ``(B, C, T, H * scale, W * scale)``.
    """
    if scale < 1:
        raise ValueError(f"scale must be >= 1, got {scale}")
    if scale == 1:
        return latents

    # Accept both mx.array and np.ndarray so unit tests can run without MLX.
    is_mlx = hasattr(latents, "dtype") and type(latents).__module__.startswith("mlx")
    if is_mlx:
        return _upsample_latents_mlx(latents, scale=scale, mode=mode)
    return _upsample_latents_numpy(np.asarray(latents), scale=scale, mode=mode)