Skip to content

fix: RoFormer on DirectML — CPU state-dict load + complex-op CPU hops (#292) - #295

Merged
beveradb merged 2 commits into
mainfrom
feat/sess-20260719-2150-roformer-dml-loader
Jul 20, 2026
Merged

fix: RoFormer on DirectML — CPU state-dict load + complex-op CPU hops (#292)#295
beveradb merged 2 commits into
mainfrom
feat/sess-20260719-2150-roformer-dml-loader

Conversation

@beveradb

@beveradb beveradb commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Summary

The two code fixes from the RoFormer-on-Windows plan (#292; plan doc in #294), both strictly gated on DirectML (privateuseone) devices so CUDA/MPS/CPU behavior is byte-identical.

Fix 1 — loader TypeError (the "'>=' not supported between torch.device and int" error)

RoformerLoader._load_with_new_implementation called torch.load(map_location="privateuseone:0"); torch-directml's deserialization hook expects integer device ids and throws. Result: the new implementation failed on every DirectML load and silently fell back to legacy. Fix: DML-gated map_location="cpu" + existing model.to(device) — the same pattern the non-Roformer MDXC path already uses.

Fix 2 — Invalid or unsupported data type ComplexFloat

torch-directml has no complex tensor support. bs_roformer/mel_band_roformer now hop exactly the complex ops (stft → view_as_real, view_as_complex + mask multiply/scatter, istft) to CPU on DML, keeping the transformer stack — the heavy compute — on the GPU. Same pattern as the MDX arch's uvr_lib_v5/stft.py (which is why MDX already works on DirectML) and the existing MPS istft hop. All MPS branches untouched.

Testing

  • _is_dml_device truth tables; loader tests assert map_location is CPU for DML and unchanged for cpu/cuda/mps with model.to(device) still receiving the original device
  • Equivalence tests: _is_dml_device is module-level so tests force the DML branches on CPU tensors (where every hop is a no-op) and assert bit-for-bit identical output vs the normal path for both models — proving the hop plumbing doesn't alter results
  • tests/unit + tests/contract: 301 passed, 4 skipped
  • CodeRabbit reviewed commit 1 clean (0 findings); commit 2 hit the free-OSS rate limit — same-pattern change with equivalence tests
  • Real-DML verification lands with the windows-directml CI job once the Windows runner image exists (feat(runner-manager): Windows GPU ephemeral runner family (gpu-windows) karaoke-gen#881); that job will assert new-implementation load (no legacy fallback) and CPU-vs-DML output quality

Known remaining risk on real DML hardware: F.scaled_dot_product_attention (flash path in attend.py) may be unsupported — if the first real-DML run shows that, the einsum fallback gets wired up in a follow-up.

@coderabbitai ignore

🤖 Generated with Claude Code

beveradb and others added 2 commits July 19, 2026 21:51
torch-directml's torch.load deserialization hook expects integer device
ids and raises TypeError ("'>=' not supported between instances of
'torch.device' and 'int'") when map_location targets a privateuseone
device. This made the new Roformer implementation fail on every DirectML
load and silently fall back to the legacy path (whose CPU map_location
got further, then died on ComplexFloat at inference — separate fix).

Load the state dict on CPU and move the model to the device afterwards,
gated on DML so cuda/mps/cpu keep byte-identical behavior.

Part 3 of the RoFormer-on-Windows plan (see #294 for the plan doc).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
torch-directml has no complex tensor support — torch.stft with
return_complex, view_as_complex, complex multiplies and torch.istft all
fail with 'Invalid or unsupported data type ComplexFloat'. Hop exactly
those ops to CPU when the input lives on a privateuseone device, keeping
the transformer stack (the heavy compute) on the DML device. This is the
same pattern the MDX arch already uses in uvr_lib_v5/stft.py (which is
why MDX works on DirectML today) and mirrors the existing MPS istft hop.

All hops are gated on _is_dml_device(); cuda/mps/cpu behavior is
unchanged — the MPS branches are untouched. The helper is module-level
so tests can force the DML branches on CPU tensors: new equivalence
tests assert the hopped path produces identical output to the normal
path for both BSRoformer and MelBandRoformer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@beveradb beveradb changed the title fix: load Roformer state dicts on CPU for DirectML devices (#292 part 1) fix: RoFormer on DirectML — CPU state-dict load + complex-op CPU hops (#292) Jul 20, 2026
@beveradb
beveradb merged commit 675e5ff into main Jul 20, 2026
39 of 48 checks passed
@beveradb
beveradb deleted the feat/sess-20260719-2150-roformer-dml-loader branch July 20, 2026 18:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant