[Cosmos3] Mixed W8A8/W8A16 denoising for ModelOpt FP8 checkpoints by ConstBob · Pull Request #14664 · huggingface/diffusers · GitHub
Skip to content

[Cosmos3] Mixed W8A8/W8A16 denoising for ModelOpt FP8 checkpoints - #14664

Merged
sayakpaul merged 11 commits into
huggingface:mainfrom
ConstBob:feat/cosmos3-mixed-w8a8-w8a16
Sep 14, 2026
Merged

sayakpaul merged 11 commits into
huggingface:mainfrom
ConstBob:feat/cosmos3-mixed-w8a8-w8a16

Conversation

@ConstBob

@ConstBob ConstBob commented Aug 31, 2026 •

Copy link
Copy Markdown
Contributor

What does this PR do?

Adds denoising-step-aware mixed W8A8/W8A16 for Cosmos3 ModelOpt FP8 checkpoints, so official Hub revision=fp8 is not W8A8 on every step.

This is the Diffusers counterpart of the FP8 schedule contract in vllm-omni#6560 (not a port of NVFP4, tensor parallel, caches, or vllm serve).

Behavior

  • Checkpoint weights stay serialized static W8A8 (weight_only: false). Mixing is runtime.
  • Video Nano / Super / Super-I2V fp8: first 3 + last 3 scheduler steps W8A16 (dequant + F.linear); middle steps native ModelOpt W8A8.
  • Precision is chosen once per scheduler step (CFG cond/uncond match). Reasoner defaults to A16 when the checkpoint says reasoner: a16.
  • Distilled 4-step and Super-T2I fp8 have runtime: null → mixed off, all steps native W8A8.
  • mixed_precision_format=None (default) reads quantization_config.runtime.diffusion_step_policy from the transformer (including on-disk transformer/config.json when the live ModelOpt config omits runtime). "none" disables only the schedule. "fp8" forces it.

Code

  • src/diffusers/pipelines/cosmos/mixed_precision.py — policy parse (fail-closed), linear discovery, W8A16 dispatch.
  • pipeline_cosmos3_omni.py and modular denoise.py — apply/reset once per step.
  • docs/source/en/api/pipelines/cosmos3.md — user-facing FP8 mixed section.
  • Out of scope: NVFP4, custom kernels, dense-weight cache, cookbook/Hub cards (follow-up after this lands).

Tests

Unit (CPU, in-tree): tests/pipelines/cosmos/test_cosmos3_mixed_precision.py — 17 passed (schedule boundaries, 1-step stays native, reasoner vs generation, overlay of live runtime: null + on-disk policy, FP32 W8A16, malformed policy / NVFP4 reject).

pytest tests/pipelines/cosmos/test_cosmos3_mixed_precision.py
# 17 passed, 5 subtests passed

GPU, not in CI (official Hub revision=fp8):

  • Cosmos3-Nano @fp8: auto-enable 3+3; 8-step T2I E2E with exact trace W8A16×3 / W8A8×2 / W8A16×3; 35-step latent W8A16×3 / W8A8×29 / W8A16×3.
  • Super-Text2Image-4Step @fp8 transformer: auto-off; GEMM stays native W8A8.

Not claimed in this PR: Super 64B generate, 189-frame I2V, vs-BF16 hashes, or the vLLM-Omni H100 throughput table.

Depends on NVIDIA ModelOpt restore for serialized FP8 (same as existing Cosmos3 FP8 loading).

Self-review (final)

Ran the self-review skill on huggingface/main...HEAD against .ai/references/review-rules.md (plus pipelines.md, modular.md, testing.md, code_style.md, pitfalls.md).

Diff files: mixed_precision.py, pipeline_cosmos3_omni.py, modular_pipelines/cosmos/denoise.py, test_cosmos3_mixed_precision.py, docs/source/en/api/pipelines/cosmos3.md.

Blocking issues: none.

Non-blocking — left for review (deliberate):

  1. Transformer forward wrappers stay installed after __call__ (mixed_precision.py). Inactive wrappers call the original ModelOpt forward. Per pipelines.md gotcha 7 we could uninstall on exit (PAG-style); we kept wrappers to avoid re-wrapping hundreds of linears each generate.
  2. W8A16 reads layer.weight then F.linear. Per testing.md, that bypasses group-offload leaf hooks. Same dense A16 reference as vLLM-Omni; group offload + serialized FP8 was not a target.
  3. Focused tests use fake ModelOpt linears + unittest.TestCase. Per testing.md, new pipeline tests prefer real tiny classes and pytest mixins. Existing Cosmos3 pipeline tests are still unittest; ModelOpt restore is impractical in CI.
  4. mixed_precision_overlap is parsed from the checkpoint / resolve(), not exposed on Cosmos3OmniPipeline.__call__.
  5. Docs revision="fp8" example does not repeat full ModelOpt from_pretrained kwargs; loading still follows the ModelOpt guide.

Dead code: FIRST_LAST_N_FP8_POLICY, from_kwargs, and quantization_config_from_module are used (tests + resolve). No unused runtime methods.

Verdict: READY.

Before submitting

  • Did you use an AI agent (Claude Code, Codex, Cursor, etc.) to help with this PR? If so:
    • Did you read the Coding with AI agents guide?
    • Did you run the self-review skill on the diff?
    • Did you share the final self-review notes in the PR description or a comment?
  • Did you read the contributor guideline?
  • Did you read our philosophy doc? (important for complex PRs)
  • Was this discussed/approved via a GitHub issue or the forum? Please add a link to it if that's the case.
  • Did you make sure to update the documentation with your changes? Here are the
    documentation guidelines, and
    here are tips on formatting docstrings.
  • Did you write any new necessary tests?
  • Are you the author (or part of the team) of the model/pipeline (only applicable for model/pipeline related PRs)?

Who can review?

@yiyixuxu @asomoza

ConstBob and others added 4 commits August 24, 2026 16:42
…oints.

Keep native ModelOpt GEMM on middle steps and dequant-linear W8A16 on the first/last steps so CFG cond/uncond share one precision per scheduler step.
Enable first/last W8A16 only when transformer/config.json declares diffusion_step_policy, so distilled FP8 stays native W8A8 instead of inheriting a hardcoded 3+3 window.
…schedules.

Read the checkpoint runtime policy from on-disk transformer/config.json when the live ModelOpt config omits it, fail closed on incomplete policies, and allow FP32 activations on the W8A16 path.
@github-actions github-actions Bot added documentation Improvements or additions to documentation tests modular-pipelines pipelines size/L PR with diff > 200 LOC labels Aug 31, 2026
@yiyixuxu
yiyixuxu requested a review from sayakpaul September 2, 2026 01:00

@sayakpaul sayakpaul left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR. I think it's a bit intrusive in terms of what it is doing. Could you provide runtime numbers (latency, throughput) and also how does it compare in terms of visual results (with and without comparisons would be great).

Comment thread docs/source/en/api/pipelines/cosmos3.md Outdated
Comment thread docs/source/en/api/pipelines/cosmos3.md Outdated
Comment thread tests/pipelines/cosmos/test_cosmos3_mixed_precision.py Outdated
Comment thread src/diffusers/pipelines/cosmos/pipeline_cosmos3_omni.py
ConstBob and others added 2 commits September 8, 2026 09:56
… drop unit tests.

W8A8/W8A16 is explained without listing call-site overrides; TorchAO and other
quantizers keep their native forwards; focused tests are removed until Hub fp8
usage is clearer.
@github-actions github-actions Bot removed the tests label Sep 8, 2026
@ConstBob

ConstBob commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

@ConstBob
ConstBob requested a review from sayakpaul September 8, 2026 20:13

@sayakpaul sayakpaul left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the iteration. Do you think it could make sense to try to think of an approach where we make the changes bit more general, i.e., we make it so that these are done at the Modelopt quantizer class?

class NVIDIAModelOptQuantizer(DiffusersQuantizer):

Or do you think the pipeline-level call arguments make it difficult to do so?

Comment thread docs/source/en/api/pipelines/cosmos3.md Outdated
Comment thread docs/source/en/api/pipelines/cosmos3.md Outdated
…e boilerplate.

Keep serialized restore in the ModelOpt guide and spell out that all-W8A8 is faster but can flicker on multi-step video.
@ConstBob

Copy link
Copy Markdown
Contributor Author

Thanks @sayakpaul for the review. I've updated the PR.

I looked at the other Cosmos3 FP8 runtimes that already landed this schedule. None of them put it on the generic ModelOpt loader/quantizer.

vLLM-Omni (vllm-project/vllm-omni#6560, merged) keeps policy parse + W8A16 dispatch under diffusion/models/cosmos3/mixed_precision/. The pipeline selects precision at each denoising-loop iteration; generate-time disable is --additional-config '{"cosmos3_mixed_precision":{"enabled":false}}'.

SGLang (sgl-project/sglang#36380, merged) does the same from the Cosmos3 denoise stage (StepMixedPrecisionController + dequant/F.linear). The generic FP8 linear method is unchanged; an env var disables only the schedule.

That matches this PR: NVIDIAModelOptQuantizer is load-time (restore FP8 weights), while the schedule is a function of scheduler step index, CFG, and reasoner vs generation. The __call__ kwargs are the Diffusers equivalent of vLLM's additional-config / SGLang's env — request-scoped, not from_pretrained.

To align with this PR's counterparts in other runtimes, I'd prefer to keep that split in this PR rather than moving the schedule onto NVIDIAModelOptQuantizer. Does that sound reasonable to you?

@ConstBob
ConstBob requested a review from sayakpaul September 11, 2026 18:22
@sayakpaul

Copy link
Copy Markdown
Member

@bot /style

@github-actions

github-actions Bot commented Sep 13, 2026 •

Copy link
Copy Markdown
Contributor

Style bot fixed some files and pushed the changes.

@HuggingFaceDocBuilderDev

Copy link
Copy Markdown

@ConstBob
ConstBob requested a review from sayakpaul September 14, 2026 16:23
@sayakpaul
sayakpaul merged commit 759164b into huggingface:main Sep 14, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation modular-pipelines pipelines size/L PR with diff > 200 LOC

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants