amd: require bitsandbytes>=0.50.0 in the amd extra (fixes ROCm 4-bit NaNs) (#7535)

* amd: require bitsandbytes>=0.50.0 in the amd extra

bnb <= 0.49.2 NaNs at decode shape on every AMD GPU. The ROCm 4-bit GEMV
fix (bnb PR #1887) first ships in 0.50.0, on PyPI since 2026-07-24, so the
old >=0.49.1 floor could still resolve the broken range.

Mirrors the same change made on the pip release branch in #7278.

* amd: cite the 0.50.0 ROCm work accurately in the bnb floor comment

The comment credited bnb PR #1887 as "the ROCm 4-bit GEMV fix" for every
AMD GPU. #1887 decouples blocksize from warp size and fixes a hardcoded
warp size of 32 in kgemm_4bit_inference_naive, which is a CDNA problem by
construction. The RDNA-side work is #1979 (fused 4-bit SIMT GEMM) and
#2012 (RDNA3/4 workgroup resonance). All three first ship in 0.50.0, so
the >=0.50.0 floor is unchanged; only the justification was wrong.

* amd: raise the installer bitsandbytes fallback floors to 0.50.0

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* amd: stop reporting the bitsandbytes PyPI fallback as broken

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Tighten AMD bnb floor comments

* Keep the amd extra citation and the AMD install guide reference

* amd: do not promise aarch64 a ROCm 4-bit backend it never gets

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* amd: fall back to the PyPI bitsandbytes floor on Windows ROCm too

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: Daniel Han <danielhanchen@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Leo Borcherding 2026-07-28 20:12:26 -05:00 committed by GitHub
commit 411cb86d62
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 206 additions and 46 deletions

View file

@ -862,3 +862,76 @@ class TestNoTorchPersistenceParity:
manifest = (REPO_ROOT / "studio" / "install_manifest.py").read_text(encoding = "utf-8")
assert 'NO_TORCH_TRUTHY: Tuple[str, ...] = ("1", "true", "yes", "on")' in manifest
assert "install_manifest.NO_TORCH_TRUTHY" in STACK_PY.read_text(encoding = "utf-8")
class TestAmdBnbFloorParity:
"""bitsandbytes <= 0.49.2 NaNs at 4-bit decode shape on every AMD GPU; the ROCm
4-bit GEMV fix (bnb #1887) first ships on PyPI in 0.50.0. The `amd` extra,
install.sh and the Studio stack resolve bitsandbytes independently, so all three
must carry the same floor or an unreachable pre-release wheel silently reinstates
the broken range."""
FLOOR = "0.50.0"
PYPROJECT = REPO_ROOT / "pyproject.toml"
def test_amd_extra_floor(self):
text = self.PYPROJECT.read_text(encoding = "utf-8")
amd = re.search(r"^amd = \[(.*?)^\]", text, re.S | re.M)
assert amd, "pyproject.toml must define an `amd` extra"
specs = re.findall(r'"(bitsandbytes[^"]*)"', amd.group(1))
assert specs, "the amd extra must pin bitsandbytes"
for spec in specs:
assert spec.startswith(
f"bitsandbytes>={self.FLOOR}"
), f"amd extra bitsandbytes floor must be >={self.FLOOR}, got {spec!r}"
def test_install_sh_pypi_fallback_floor(self):
text = INSTALL_SH.read_text(encoding = "utf-8")
assert (
f'_BNB_ROCM_PYPI_FALLBACK="bitsandbytes>={self.FLOOR}"' in text
), f"install.sh _install_bnb_rocm PyPI fallback must floor at {self.FLOOR}"
def test_stack_py_pypi_fallback_floor(self):
text = STACK_PY.read_text(encoding = "utf-8")
assert (
f'_BNB_ROCM_PYPI_FALLBACK = "bitsandbytes>={self.FLOOR}"' in text
), f"install_python_stack.py PyPI fallback must floor at {self.FLOOR}"
def test_no_installer_still_allows_the_broken_range(self):
for path in (INSTALL_SH, INSTALL_PS1, SETUP_PS1, STACK_PY, self.PYPROJECT):
text = path.read_text(encoding = "utf-8")
for line in text.splitlines():
if "bitsandbytes>=0.49" in line and not line.lstrip().startswith(("#", "//")):
raise AssertionError(
f"{path.name} still floors bitsandbytes in the broken ROCm range: {line.strip()!r}"
)
def test_fallback_is_not_reported_as_broken(self):
"""The fallback now installs the first fixed release, so neither installer
may still call 4-bit decode broken on ROCm."""
for path in (INSTALL_SH, STACK_PY):
text = path.read_text(encoding = "utf-8")
assert (
"4-bit decode broken on ROCm" not in text
), f"{path.name} still reports the repaired PyPI fallback as broken"
assert (
"4-bit decode will be broken on ROCm" not in text
), f"{path.name} still reports the repaired PyPI fallback as broken"
def test_aarch64_is_not_told_it_has_a_rocm_backend(self):
"""bitsandbytes ships no ROCm kernels in its aarch64 wheel at any version, so
neither installer may hand aarch64 the x86_64 "carries the ROCm 4-bit fix"
message, and both must warn that 4-bit needs a source build there."""
sh = INSTALL_SH.read_text(encoding = "utf-8")
assert "_bnb_rocm_arch_has_binary()" in sh
assert "_warn_bnb_no_rocm_binary()" in sh
assert (
sh.count("_warn_bnb_no_rocm_binary\n") >= 2
), "install.sh must warn on aarch64 after both the pre-release and the fallback install"
py = STACK_PY.read_text(encoding = "utf-8")
assert "def _bnb_rocm_arch_has_binary(" in py
assert "_bnb_rocm_arch_has_binary()" in py
for text, name in ((sh, "install.sh"), (py, "install_python_stack.py")):
assert (
"4-bit QLoRA needs a source build" in text
), f"{name} must tell aarch64 users 4-bit needs a source build"