Skip to content

Fix CPU quantize_blockwise to pick the nearest code - #2119

Open
damienen wants to merge 1 commit into
bitsandbytes-foundation:mainfrom
damienen:fix/bitsandbytes-02
Open

damienen wants to merge 1 commit into
bitsandbytes-foundation:mainfrom
damienen:fix/bitsandbytes-02

Conversation

@damienen

@damienen damienen commented Nov 2, 2026

Copy link
Copy Markdown

On CPU, quantize_blockwise looks codes up in a 65,536-bin table. Near zero a bin can be wider than the gap between neighbouring codes, so an element can get a neighbouring code instead of the nearest one. In the default signed map, 0.0 is stored as +2.125e-5 * absmax. With Adam8bit on CPU, a weight whose gradient is always 0 then gets a non-zero first moment and drifts, while torch.optim.Adam leaves it unchanged. CUDA and the default backend pick the nearest code.

The fix keeps the table but uses its entry only as a starting index. It then steps to the exact bucket with the same midpoint rule as the default backend (ties go to the lower index), usually 0 or 1 steps. The x86 loop and the NEON loop both use it. The NEON change is the same edit, but I could not compile it here.

I added test_blockwise_quantization_nearest_code: every code value must map to its own index and 0.0 must round-trip, on both the signed and unsigned dynamic map.

Testing, CPU build of main (a616886cacf86701514f0cb1d6be760f967481c0) in Docker:

  • Repro (zero round-trip, then Adam8bit against torch.optim.Adam on a zero-gradient coordinate): exit 1 on main (index 131 instead of 127, drift of -3.6e-6), exit 0 with the fix.
  • pytest tests/test_functional.py -k "cpu and blockwise_quantization": the new test fails on main (2 failed) and all 4 selected tests pass with the fix.
  • Not run: tests/test_optim.py on CPU and the aarch64 build.

Fixes #2118

The 65,536-bin LUT can map a value to a neighbouring code near zero,
so 0.0 was stored as +2.125e-5 * absmax. Use the LUT entry as a start
and walk to the nearest code with the default backend's midpoint rule.

This branch has not been deployed

No deployments
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.

CPU quantize_blockwise stores 0.0 as a neighbouring code, so Adam8bit on CPU moves weights with zero gradient

1 participant