Skip to content

Fix Ulysses sequence-parallel crash in self-forcing causal Wan DiT#1627

Open
pkisfaludi-nv wants to merge 1 commit into
hao-ai-lab:mainfrom
pkisfaludi-nv:fix/causal-wan-ulysses-sp
Open

Fix Ulysses sequence-parallel crash in self-forcing causal Wan DiT#1627
pkisfaludi-nv wants to merge 1 commit into
hao-ai-lab:mainfrom
pkisfaludi-nv:fix/causal-wan-ulysses-sp

Conversation

@pkisfaludi-nv

Copy link
Copy Markdown

Problem

Running a self-forcing causal Wan model (e.g. SFWan2.1-T2V-A14B, SFWan2.1-T2V-1.3B) with Ulysses sequence parallelism (num_gpus>=2) crashes:

RuntimeError: The size of tensor a (4680) must match the size of tensor b (9360)
at non-singleton dimension 1   # in _apply_rotary_emb

Root cause

causal_wanvideo.py builds RoPE with rope_num_frames = post_patch_num_frames * get_sp_world_size() (sp× too long), but the causal pipeline replicates the full token sequence and a full-size KV cache on every rank (it does not shard the sequence). The attention then applies that over-long RoPE to the full local query → shape mismatch (9360 = 2 × 4680 at sp=2).

Fix

  • Remove * get_sp_world_size() from the RoPE length in all branches (relativistic / standard / train) so RoPE matches the full local sequence (no-op at sp=1).
  • Parallelize the causal attention across heads (Ulysses over heads): each rank runs attention on its head slice [rank*H/sp : (rank+1)*H/sp] over the full sequence, then sequence_model_parallel_all_gather(dim=2) reconstructs all heads. Applied uniformly to all three attention paths (flex_attention, KV-cache self.attn, relativistic window). A sequence all-to-all (à la wanvideo.py) is incompatible with the causal KV-cache windowing (expressed in global token coords), so head-parallel is the exact-matching approach; heads are independent → numerically identical to single-GPU.

Verification

wlsaidhi/SFWan2.1-T2V-1.3B-Diffusers, 480×832, 21 frames (7 autoregressive KV-cache blocks), seed 1234:

mean std
num_gpus=1 116.8295684 90.8565301
num_gpus=2 (fixed) 116.8295684 90.8565301

torch.equal=True, max_abs_diff=0.0byte-identical; 2-GPU no longer crashes.

Note: verified on the standard KV-cache path (this model uses rope_cache_policy="absolute"); the relativistic path gets the identical head-slice+all-gather transform for consistency.

Under Ulysses SP (num_gpus>=2), self-forcing causal Wan models
(causal_wanvideo.py) crash in `_apply_rotary_emb`:

    RuntimeError: The size of tensor a (4680) must match tensor b (9360)
    at non-singleton dimension 1

Cause: the DiT builds RoPE for `post_patch_num_frames * get_sp_world_size()`
(sp x too long) but the causal pipeline *replicates* the full token sequence and
a full-size KV cache on every rank (it does not shard the sequence), so the
attention applies that over-long RoPE to the full local query.

Fix:
- Remove `* get_sp_world_size()` from the RoPE length (relativistic, standard,
  and train branches) so RoPE matches the full local sequence. No-op at sp=1.
- Parallelize the causal attention across HEADS (Ulysses over heads): each rank
  computes attention for its head slice `[rank*H/sp:(rank+1)*H/sp]` over the full
  sequence, then `sequence_model_parallel_all_gather(dim=2)` reconstructs all
  heads. Applied uniformly to all three attention paths (flex_attention,
  KV-cache `self.attn`, and the relativistic window). A sequence all-to-all a la
  wanvideo.py is incompatible with the causal KV-cache windowing (global token
  coords), so head-parallel is the exact-matching approach; heads are
  independent so it is numerically identical to single-GPU.

Verified: `wlsaidhi/SFWan2.1-T2V-1.3B-Diffusers`, 480x832, 21 frames (7
autoregressive KV-cache blocks), seed 1234, on 2x GPU vs 1x GPU:
torch.equal=True, max_abs_diff=0.0 (byte-identical). 2-GPU previously crashed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mergify mergify Bot added the scope: model Model architecture (DiTs, encoders, VAEs) label Jul 21, 2026
@mergify

mergify Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

⚠️ PR title format required

Your PR title must start with a type tag in brackets. Examples:

  • [feat] Add new model support
  • [bugfix] Fix VAE tiling corruption
  • [refactor] Restructure training pipeline
  • [perf] Optimize attention kernel
  • [ci] Update test infrastructure
  • [infra] Add activation trace hooks
  • [docs] Add inference guide
  • [misc] Clean up configs
  • [new-model] Port Flux2 to FastVideo
  • [skill] Add add-model agent skill

Valid tags: feat, feature, bugfix, fix, refactor, perf, ci, infra, doc, docs, misc, chore, kernel, new-model, skill, skills

Please update your PR title and the merge protection check will pass automatically.

@mergify

mergify Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Merge Protections

🔴 1 of 1 protections blocking · waiting on 👀 reviews, 🤖 CI and 🙋 you

Protection Waiting on
🔴 PR merge requirements 👀 reviews, 🤖 CI and 🙋 you

🔴 PR merge requirements

Waiting for

  • #approved-reviews-by>=1
  • check-success=full-suite-passed
  • title~=(?i)^\[(feat|feature|bugfix|fix|refactor|perf|ci|doc|docs|misc|chore|kernel|new.?model|skill|skills|infra)\]
This rule is failing.
  • #approved-reviews-by>=1
  • check-success=full-suite-passed
  • title~=(?i)^\[(feat|feature|bugfix|fix|refactor|perf|ci|doc|docs|misc|chore|kernel|new.?model|skill|skills|infra)\]
  • check-success=fastcheck-passed
  • check-success~=pre-commit

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request implements Ulysses sequence-parallel (SP) support for causal attention in causal_wanvideo.py. Instead of partitioning across the sequence dimension, the causal pipeline replicates the full sequence on every rank and parallelizes attention across the head dimension. Per-rank head outputs are then gathered back using sequence_model_parallel_all_gather. Additionally, the multiplication of sequence length by the sequence-parallel world size when computing rotary position embeddings (RoPE) has been removed to prevent crashes, as the sequence is unsharded. There are no review comments to address, and I have no further feedback to provide.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope: model Model architecture (DiTs, encoders, VAEs)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant