From 070af7d3125ef12bb202453a0041218babd68d12 Mon Sep 17 00:00:00 2001 From: yingzhou-bjtu Date: Mon, 14 Sep 2026 16:55:13 +0800 Subject: [PATCH 1/9] fix(musa): support float64 in-place log --- README.md | 1 + README_CN.md | 1 + src/torchada/_patch.py | 23 +++++++++++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/README.md b/README.md index 1a19a26..735c4e4 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,7 @@ That's it! Supported `torch.cuda.*` APIs are automatically redirected to `torch. | C++ nvJPEG porting | nvJPEG source and build settings → MTJPEG | | ctypes Libraries | `ctypes.CDLL` with CUDA function names → MUSA equivalents | | Unified Accelerator API | `torch.accelerator.empty_cache()`, `memory_stats()`, `Stream`, `Event`, ... | +| MUSA float64 in-place log | `Tensor.log_()` reuses the supported out-of-place operation while preserving the in-place contract | | Triton CUDA Extra | `tl.extra.cuda` → `tl.extra.musa` compatibility on MUSA | | Triton Fused MoE | Triton 3.2.0 MTT S5000 tuning configs for vLLM and SGLang | diff --git a/README_CN.md b/README_CN.md index 5ce05c9..579eb66 100644 --- a/README_CN.md +++ b/README_CN.md @@ -66,6 +66,7 @@ torch.cuda.synchronize() | C++ nvJPEG 移植 | nvJPEG 源码及构建配置 → MTJPEG | | ctypes 库加载 | `ctypes.CDLL` 使用 CUDA 函数名 → 自动转换为 MUSA | | 统一加速器 API | `torch.accelerator.empty_cache()`、`memory_stats()`、`Stream`、`Event` 等 | +| MUSA float64 原地对数 | `Tensor.log_()` 复用受支持的非原地操作,同时保持原地操作契约 | | Triton CUDA Extra | MUSA 上的 `tl.extra.cuda` → `tl.extra.musa` 兼容 | | Triton 融合 MoE | 面向 vLLM 和 SGLang 的 Triton 3.2.0 MTT S5000 调优配置 | diff --git a/src/torchada/_patch.py b/src/torchada/_patch.py index 732fa2b..13228c1 100644 --- a/src/torchada/_patch.py +++ b/src/torchada/_patch.py @@ -40,6 +40,7 @@ _patched = False _original_init_process_group = None +_original_tensor_log_ = None # Registry for patch functions _patch_registry: List[Callable[[], None]] = [] @@ -160,6 +161,28 @@ def _patch_inductor_template_heuristics(): heuristic_cache.clear() +@patch_function +@requires_import("torch_musa") +def _patch_tensor_log_(): + """Run MUSA float64 ``Tensor.log_`` through the supported out-of-place op.""" + global _original_tensor_log_ + + if not is_musa_platform() or _original_tensor_log_ is not None: + return + + _original_tensor_log_ = torch.Tensor.log_ + + @functools.wraps(_original_tensor_log_) + def patched_log_(self): + if self.device.type == "musa" and self.dtype == torch.float64: + # Preserve the in-place contract while avoiding unsupported MUSA + # float64 LOG in the validated torch_musa runtime. + return self.copy_(torch.log(self)) + return _original_tensor_log_(self) + + torch.Tensor.log_ = patched_log_ + + # Cache for translated device strings - avoids repeated string operations _device_str_cache = {} From b5acdfd8b29c60360a1ecb1d5d486f5bc8346f8f Mon Sep 17 00:00:00 2001 From: yingzhou-bjtu Date: Mon, 14 Sep 2026 17:07:09 +0800 Subject: [PATCH 2/9] fix(musa): gate float64 log compatibility patch --- README.md | 2 +- README_CN.md | 2 +- src/torchada/_patch.py | 11 +++++++++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 735c4e4..632da8f 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ That's it! Supported `torch.cuda.*` APIs are automatically redirected to `torch. | C++ nvJPEG porting | nvJPEG source and build settings → MTJPEG | | ctypes Libraries | `ctypes.CDLL` with CUDA function names → MUSA equivalents | | Unified Accelerator API | `torch.accelerator.empty_cache()`, `memory_stats()`, `Stream`, `Event`, ... | -| MUSA float64 in-place log | `Tensor.log_()` reuses the supported out-of-place operation while preserving the in-place contract | +| MUSA float64 in-place log | On `torch_musa < 2.11.0.post2`, `Tensor.log_()` reuses the supported out-of-place operation while preserving the in-place contract | | Triton CUDA Extra | `tl.extra.cuda` → `tl.extra.musa` compatibility on MUSA | | Triton Fused MoE | Triton 3.2.0 MTT S5000 tuning configs for vLLM and SGLang | diff --git a/README_CN.md b/README_CN.md index 579eb66..fb50d20 100644 --- a/README_CN.md +++ b/README_CN.md @@ -66,7 +66,7 @@ torch.cuda.synchronize() | C++ nvJPEG 移植 | nvJPEG 源码及构建配置 → MTJPEG | | ctypes 库加载 | `ctypes.CDLL` 使用 CUDA 函数名 → 自动转换为 MUSA | | 统一加速器 API | `torch.accelerator.empty_cache()`、`memory_stats()`、`Stream`、`Event` 等 | -| MUSA float64 原地对数 | `Tensor.log_()` 复用受支持的非原地操作,同时保持原地操作契约 | +| MUSA float64 原地对数 | `torch_musa < 2.11.0.post2` 时,`Tensor.log_()` 复用受支持的非原地操作,同时保持原地操作契约 | | Triton CUDA Extra | MUSA 上的 `tl.extra.cuda` → `tl.extra.musa` 兼容 | | Triton 融合 MoE | 面向 vLLM 和 SGLang 的 Triton 3.2.0 MTT S5000 调优配置 | diff --git a/src/torchada/_patch.py b/src/torchada/_patch.py index 13228c1..8827e65 100644 --- a/src/torchada/_patch.py +++ b/src/torchada/_patch.py @@ -164,10 +164,17 @@ def _patch_inductor_template_heuristics(): @patch_function @requires_import("torch_musa") def _patch_tensor_log_(): - """Run MUSA float64 ``Tensor.log_`` through the supported out-of-place op.""" + """Backport MUSA float64 ``Tensor.log_`` for torch_musa < 2.11.0.post2.""" global _original_tensor_log_ - if not is_musa_platform() or _original_tensor_log_ is not None: + musa_module = getattr(torch, "musa", None) + if ( + not is_musa_platform() + or not _is_pre_torch_musa_2_11_0_post2( + getattr(musa_module, "__version__", None) + ) + or _original_tensor_log_ is not None + ): return _original_tensor_log_ = torch.Tensor.log_ From 15cb7540c2fc2102ddc70e65ce97b459d3830aec Mon Sep 17 00:00:00 2001 From: yingzhou-bjtu Date: Tue, 15 Sep 2026 19:21:07 +0800 Subject: [PATCH 3/9] test(musa): cover float64 log version gate --- tests/test_cuda_patching.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/test_cuda_patching.py b/tests/test_cuda_patching.py index af1557b..57720d1 100644 --- a/tests/test_cuda_patching.py +++ b/tests/test_cuda_patching.py @@ -3066,6 +3066,34 @@ def test_torch_musa_version_boundary(self, musa_version, expected): assert _is_pre_torch_musa_2_11_0_post2(musa_version) is expected + def test_float64_log_patch_respects_torch_musa_version(self, monkeypatch): + import sys + from types import ModuleType, SimpleNamespace + + import torch + + from torchada import _patch + + original_log = torch.Tensor.log_ + monkeypatch.setitem(sys.modules, "torch_musa", ModuleType("torch_musa")) + monkeypatch.setattr(_patch, "is_musa_platform", lambda: True) + monkeypatch.setattr(_patch, "_original_tensor_log_", None) + monkeypatch.setattr( + torch, + "musa", + SimpleNamespace(__version__="2.11.0.post2"), + raising=False, + ) + + _patch._patch_tensor_log_() + assert torch.Tensor.log_ is original_log + + torch.musa.__version__ = "2.11.0.post1+musa5.2.0" + _patch._patch_tensor_log_() + assert torch.Tensor.log_ is not original_log + + monkeypatch.setattr(torch.Tensor, "log_", original_log) + def _make_wrapper( self, accel_attrs=None, From 5dc1b5f4a20dbd542d51c9ea3139ffa511d24b0f Mon Sep 17 00:00:00 2001 From: yingzhou-bjtu Date: Wed, 16 Sep 2026 15:59:18 +0800 Subject: [PATCH 4/9] test(musa): cover float64 log patch behavior Add a CPU-runnable unit test for the MUSA float64 Tensor.log_() compatibility patch so the version gate and in-place fallback path are both covered without requiring a MUSA device. --- tests/test_cuda_patching.py | 64 +++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/tests/test_cuda_patching.py b/tests/test_cuda_patching.py index 57720d1..0c7d394 100644 --- a/tests/test_cuda_patching.py +++ b/tests/test_cuda_patching.py @@ -3075,6 +3075,7 @@ def test_float64_log_patch_respects_torch_musa_version(self, monkeypatch): from torchada import _patch original_log = torch.Tensor.log_ + monkeypatch.setattr(torch.Tensor, "log_", original_log) monkeypatch.setitem(sys.modules, "torch_musa", ModuleType("torch_musa")) monkeypatch.setattr(_patch, "is_musa_platform", lambda: True) monkeypatch.setattr(_patch, "_original_tensor_log_", None) @@ -3092,7 +3093,70 @@ def test_float64_log_patch_respects_torch_musa_version(self, monkeypatch): _patch._patch_tensor_log_() assert torch.Tensor.log_ is not original_log + def test_float64_log_patch_uses_out_of_place_copy_on_musa(self, monkeypatch): + import sys + from types import ModuleType, SimpleNamespace + + import torch + + from torchada import _patch + + original_log = torch.Tensor.log_ monkeypatch.setattr(torch.Tensor, "log_", original_log) + monkeypatch.setitem(sys.modules, "torch_musa", ModuleType("torch_musa")) + monkeypatch.setattr(_patch, "is_musa_platform", lambda: True) + monkeypatch.setattr(_patch, "_original_tensor_log_", None) + monkeypatch.setattr( + torch, + "musa", + SimpleNamespace(__version__="2.11.0.post1+musa5.2.0"), + raising=False, + ) + + _patch._patch_tensor_log_() + patched_log = torch.Tensor.log_ + + log_args = [] + logged = object() + + def fake_torch_log(tensor): + log_args.append(tensor) + return logged + + original_calls = [] + + def fake_original_log_(tensor): + original_calls.append(tensor) + return tensor + + monkeypatch.setattr(torch, "log", fake_torch_log) + monkeypatch.setattr(_patch, "_original_tensor_log_", fake_original_log_) + + class FakeTensor: + def __init__(self, device_type, dtype): + self.device = SimpleNamespace(type=device_type) + self.dtype = dtype + self.copied = None + + def copy_(self, other): + self.copied = other + return self + + musa_f64 = FakeTensor("musa", torch.float64) + result = patched_log(musa_f64) + assert result is musa_f64 + assert musa_f64.copied is logged + assert log_args == [musa_f64] + assert original_calls == [] + + cpu_f64 = FakeTensor("cpu", torch.float64) + assert patched_log(cpu_f64) is cpu_f64 + assert cpu_f64.copied is None + + musa_f32 = FakeTensor("musa", torch.float32) + assert patched_log(musa_f32) is musa_f32 + assert musa_f32.copied is None + assert original_calls == [cpu_f64, musa_f32] def _make_wrapper( self, From dfc9271f9f41fe72a822f228d9fde505fd528bd8 Mon Sep 17 00:00:00 2001 From: yingzhou-bjtu Date: Fri, 18 Sep 2026 11:03:26 +0800 Subject: [PATCH 5/9] chore: bump version to 0.1.87 Replace remaining 0.1.86 version strings, including the historical benchmark snapshot, so the package version is consistent across the tree. --- README.md | 2 +- README_CN.md | 2 +- benchmarks/benchmark_history.json | 2 +- pyproject.toml | 2 +- src/torchada/__init__.py | 2 +- tests/test_platform.py | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 632da8f..dd9b92b 100644 --- a/README.md +++ b/README.md @@ -393,7 +393,7 @@ See `src/torchada/_mappings/` for 400+ mapping rules grouped by API domain. ``` # pyproject.toml or requirements.txt -torchada>=0.1.86 +torchada>=0.1.87 ``` ### Step 2: Conditional Import diff --git a/README_CN.md b/README_CN.md index fb50d20..fbac631 100644 --- a/README_CN.md +++ b/README_CN.md @@ -377,7 +377,7 @@ if torchada.is_gpu_device(device): # 在 CUDA 和 MUSA 上都能工作 ``` # pyproject.toml 或 requirements.txt -torchada>=0.1.86 +torchada>=0.1.87 ``` ### 步骤 2:条件导入 diff --git a/benchmarks/benchmark_history.json b/benchmarks/benchmark_history.json index 84de6f7..a255c78 100644 --- a/benchmarks/benchmark_history.json +++ b/benchmarks/benchmark_history.json @@ -3,7 +3,7 @@ "description": "Historical benchmark results for torchada performance tracking", "results": [ { - "version": "0.1.86", + "version": "0.1.87", "date": "2026-01-29", "platform": "MUSA", "pytorch_version": "2.7.1", diff --git a/pyproject.toml b/pyproject.toml index 5dd53bf..3685f36 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "torchada" -version = "0.1.86" +version = "0.1.87" description = "Adapter package for torch_musa to act exactly like PyTorch CUDA" readme = "README.md" license = {text = "MIT"} diff --git a/src/torchada/__init__.py b/src/torchada/__init__.py index 738cc55..e4152fc 100644 --- a/src/torchada/__init__.py +++ b/src/torchada/__init__.py @@ -24,7 +24,7 @@ from torch.utils.cpp_extension import CUDAExtension, BuildExtension, CUDA_HOME """ -__version__ = "0.1.86" +__version__ = "0.1.87" from . import cuda, utils diff --git a/tests/test_platform.py b/tests/test_platform.py index 9cdf60e..c0c7777 100644 --- a/tests/test_platform.py +++ b/tests/test_platform.py @@ -59,7 +59,7 @@ def test_get_version(self): version = torchada.get_version() assert version == torchada.__version__ - assert version == "0.1.86" + assert version == "0.1.87" assert isinstance(version, str) def test_project_version_matches_runtime_version(self): From 0a3a6d3481f0607c962e5487f0957f8120ba7d21 Mon Sep 17 00:00:00 2001 From: yingzhou-bjtu Date: Fri, 18 Sep 2026 18:37:55 +0800 Subject: [PATCH 6/9] style(musa): read torch.musa version directly The float64 log_ backport already requires torch_musa. Read torch.musa.__version__ instead of wrapping the known attributes in getattr. --- src/torchada/_patch.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/torchada/_patch.py b/src/torchada/_patch.py index 8827e65..d6d2110 100644 --- a/src/torchada/_patch.py +++ b/src/torchada/_patch.py @@ -167,14 +167,9 @@ def _patch_tensor_log_(): """Backport MUSA float64 ``Tensor.log_`` for torch_musa < 2.11.0.post2.""" global _original_tensor_log_ - musa_module = getattr(torch, "musa", None) - if ( - not is_musa_platform() - or not _is_pre_torch_musa_2_11_0_post2( - getattr(musa_module, "__version__", None) - ) - or _original_tensor_log_ is not None - ): + if not is_musa_platform() or _original_tensor_log_ is not None: + return + if not _is_pre_torch_musa_2_11_0_post2(torch.musa.__version__): return _original_tensor_log_ = torch.Tensor.log_ From 80be885873a2682b065d264a1b1814b316018699 Mon Sep 17 00:00:00 2001 From: yingzhou-bjtu Date: Fri, 18 Sep 2026 19:40:12 +0800 Subject: [PATCH 7/9] style(musa): keep historical benchmark version Read torch.musa.__version__ directly for the float64 log_ gate, document that 2.11.0.post2 also owns the native log_ fix, and restore the dated benchmark snapshot to 0.1.86. --- benchmarks/benchmark_history.json | 2 +- src/torchada/_patch.py | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/benchmarks/benchmark_history.json b/benchmarks/benchmark_history.json index a255c78..84de6f7 100644 --- a/benchmarks/benchmark_history.json +++ b/benchmarks/benchmark_history.json @@ -3,7 +3,7 @@ "description": "Historical benchmark results for torchada performance tracking", "results": [ { - "version": "0.1.87", + "version": "0.1.86", "date": "2026-01-29", "platform": "MUSA", "pytorch_version": "2.7.1", diff --git a/src/torchada/_patch.py b/src/torchada/_patch.py index d6d2110..93f47e8 100644 --- a/src/torchada/_patch.py +++ b/src/torchada/_patch.py @@ -177,8 +177,8 @@ def _patch_tensor_log_(): @functools.wraps(_original_tensor_log_) def patched_log_(self): if self.device.type == "musa" and self.dtype == torch.float64: - # Preserve the in-place contract while avoiding unsupported MUSA - # float64 LOG in the validated torch_musa runtime. + # Old torch_musa rejects float64 log_, but out-of-place log plus + # copy_ keeps the in-place return contract. return self.copy_(torch.log(self)) return _original_tensor_log_(self) @@ -1805,8 +1805,9 @@ def __getitem__(self, name: str): def _is_pre_torch_musa_2_11_0_post2(version) -> bool: """Return whether the torch_musa version predates 2.11.0.post2. - torch_musa 2.11.0.post2 fixes the unified accelerator memory APIs. Older - releases still need torchada to force those calls through torch.musa. + torch_musa 2.11.0.post2 fixes the unified accelerator memory APIs and the + float64 in-place ``Tensor.log_``. Older releases still need torchada to + force those memory calls through torch.musa and to backport the log path. Ignore the local version suffix (for example ``+musa5.2.0``), because it identifies the MUSA stack build rather than the torch_musa fix level. From cf0c95955431da993cf2013b1e7548d6e1ea8aca Mon Sep 17 00:00:00 2001 From: yingzhou-bjtu Date: Fri, 18 Sep 2026 19:54:13 +0800 Subject: [PATCH 8/9] style(musa): isolate float64 log tests Sign the in-place log_ workaround comment and move the CPU coverage out of TestAcceleratorModuleWrapper into TestTensorLogPatch. --- src/torchada/_patch.py | 4 +-- tests/test_cuda_patching.py | 66 ++++++++++++++++++++----------------- 2 files changed, 37 insertions(+), 33 deletions(-) diff --git a/src/torchada/_patch.py b/src/torchada/_patch.py index 93f47e8..46e7087 100644 --- a/src/torchada/_patch.py +++ b/src/torchada/_patch.py @@ -177,8 +177,8 @@ def _patch_tensor_log_(): @functools.wraps(_original_tensor_log_) def patched_log_(self): if self.device.type == "musa" and self.dtype == torch.float64: - # Old torch_musa rejects float64 log_, but out-of-place log plus - # copy_ keeps the in-place return contract. + # note (yingzhou): old torch_musa rejects float64 log_, but + # out-of-place log plus copy_ keeps the in-place return contract. return self.copy_(torch.log(self)) return _original_tensor_log_(self) diff --git a/tests/test_cuda_patching.py b/tests/test_cuda_patching.py index 0c7d394..f3a8324 100644 --- a/tests/test_cuda_patching.py +++ b/tests/test_cuda_patching.py @@ -3034,37 +3034,8 @@ def invalid_lse_result( flash_attn._flash_attn_forward("q", "k", "v") -class TestAcceleratorModuleWrapper: - """Test the _AcceleratorModuleWrapper priority / fallback logic in isolation. - - These tests use mock modules instead of the real torch.accelerator so the - priority rules can be verified deterministically across PyTorch versions - (including the forward-compat behavior expected when torch 2.9+ lands - official implementations of APIs that currently fall back to torch.musa). - """ - - @pytest.mark.parametrize( - ("musa_version", "expected"), - ( - ("2.10.0", True), - ("2.11.0", True), - ("2.11.0.post1+musa5.2.0", True), - ("2.11.0.post1+musa5.3.0", True), - ("2.11.0.post2", False), - ("2.11.0.post2+musa5.2.0", False), - ("2.11.0.post2+musa5.3.0", False), - ("2.11.0.post2+future/musa/build", False), - ("2.11.0.post10+musa5.2.0", False), - ("2.12.0+musa6.0.0", False), - ("not-a-version+musa5.2.0", True), - ("", True), - (None, True), - ), - ) - def test_torch_musa_version_boundary(self, musa_version, expected): - from torchada._patch import _is_pre_torch_musa_2_11_0_post2 - - assert _is_pre_torch_musa_2_11_0_post2(musa_version) is expected +class TestTensorLogPatch: + """CPU coverage for the MUSA float64 Tensor.log_ compatibility patch.""" def test_float64_log_patch_respects_torch_musa_version(self, monkeypatch): import sys @@ -3158,6 +3129,39 @@ def copy_(self, other): assert musa_f32.copied is None assert original_calls == [cpu_f64, musa_f32] + +class TestAcceleratorModuleWrapper: + """Test the _AcceleratorModuleWrapper priority / fallback logic in isolation. + + These tests use mock modules instead of the real torch.accelerator so the + priority rules can be verified deterministically across PyTorch versions + (including the forward-compat behavior expected when torch 2.9+ lands + official implementations of APIs that currently fall back to torch.musa). + """ + + @pytest.mark.parametrize( + ("musa_version", "expected"), + ( + ("2.10.0", True), + ("2.11.0", True), + ("2.11.0.post1+musa5.2.0", True), + ("2.11.0.post1+musa5.3.0", True), + ("2.11.0.post2", False), + ("2.11.0.post2+musa5.2.0", False), + ("2.11.0.post2+musa5.3.0", False), + ("2.11.0.post2+future/musa/build", False), + ("2.11.0.post10+musa5.2.0", False), + ("2.12.0+musa6.0.0", False), + ("not-a-version+musa5.2.0", True), + ("", True), + (None, True), + ), + ) + def test_torch_musa_version_boundary(self, musa_version, expected): + from torchada._patch import _is_pre_torch_musa_2_11_0_post2 + + assert _is_pre_torch_musa_2_11_0_post2(musa_version) is expected + def _make_wrapper( self, accel_attrs=None, From a11dfa7e2165e1d887f5b39caafe95c86c13b0cc Mon Sep 17 00:00:00 2001 From: yingzhou-bjtu Date: Fri, 18 Sep 2026 20:38:44 +0800 Subject: [PATCH 9/9] style(musa): drop signed float64 log comment --- src/torchada/_patch.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/torchada/_patch.py b/src/torchada/_patch.py index 46e7087..038232b 100644 --- a/src/torchada/_patch.py +++ b/src/torchada/_patch.py @@ -177,8 +177,6 @@ def _patch_tensor_log_(): @functools.wraps(_original_tensor_log_) def patched_log_(self): if self.device.type == "musa" and self.dtype == torch.float64: - # note (yingzhou): old torch_musa rejects float64 log_, but - # out-of-place log plus copy_ keeps the in-place return contract. return self.copy_(torch.log(self)) return _original_tensor_log_(self)