bnb.optim.AdEMAMix32bit and bnb.optim.PagedAdEMAMix32bit subclass Optimizer2State directly (bitsandbytes/optim/ademamix.py:355 and :385 on 8336490), so they never run AdEMAMix.init_state. That override allocates state1 as (2, *p.shape) to hold m1 and m2. The base Optimizer2State.init_state allocates p.shape. Every ademamix kernel then reads m2 from the second half of state1:
- CPU backend (
_optimizer_update_32bit_cpu in bitsandbytes/backends/cpu/ops.py) indexes state1[0] and state1[1], so the first step() raises RuntimeError: output with shape [] doesn't match the broadcast shape [4096].
- Default backend (
_optimizer_update_32bit in bitsandbytes/backends/default/ops.py, used on MPS) does the same indexing and raises the same error.
- CUDA (
kOptimizer32bit2State in csrc/kernels.cu, line 674) loads m2 with Load(&(state1[n + i]), s3_vals, ...) on a buffer of n floats. I do not have a CUDA device to run this. From the source, it reads and later writes past the end of state1.
The scheduler is lost as well. Optimizer2State.update_step never applies t_alpha or t_beta3, so AdEMAMix32bit(t_alpha=100, t_beta3=100) runs unscheduled without any warning.
AdEMAMix8bit is not affected because it subclasses AdEMAMix. AdEMAMix(optim_bits=32) works, and that is what tests/test_optim.py constructs under the id "AdEMAMix32bit" (line 580), so the class itself has no test coverage. The class has had this parent since #1360 introduced it.
Repro, run on main at 8336490 with torch 2.14.0, Python 3.13.12, macOS arm64, pip install -e . (CPU build). Same output with dev = "mps".
import torch, bitsandbytes as bnb
dev = "cpu"
for cls in (bnb.optim.AdEMAMix32bit, bnb.optim.PagedAdEMAMix32bit, bnb.optim.AdEMAMix, bnb.optim.AdEMAMix8bit):
p = torch.nn.Parameter(torch.randn(4096, device=dev))
opt = cls([p])
p.grad = torch.randn_like(p)
try:
opt.step()
print(f"{cls.__name__:22s} OK state1.shape={tuple(opt.state[p]['state1'].shape)}")
except Exception as e:
print(f"{cls.__name__:22s} FAIL state1.shape={tuple(opt.state[p]['state1'].shape)} {type(e).__name__}: {e}")
AdEMAMix32bit FAIL state1.shape=(4096,) RuntimeError: output with shape [] doesn't match the broadcast shape [4096]
PagedAdEMAMix32bit FAIL state1.shape=(4096,) RuntimeError: output with shape [] doesn't match the broadcast shape [4096]
AdEMAMix OK state1.shape=(2, 4096)
AdEMAMix8bit OK state1.shape=(2, 4096)
Fix: make AdEMAMix32bit subclass AdEMAMix with optim_bits=32, the same way AdEMAMix8bit does. PagedAdEMAMix32bit already subclasses AdEMAMix32bit and picks it up. PR follows.
bnb.optim.AdEMAMix32bitandbnb.optim.PagedAdEMAMix32bitsubclassOptimizer2Statedirectly (bitsandbytes/optim/ademamix.py:355and:385on8336490), so they never runAdEMAMix.init_state. That override allocatesstate1as(2, *p.shape)to hold m1 and m2. The baseOptimizer2State.init_stateallocatesp.shape. Every ademamix kernel then reads m2 from the second half ofstate1:_optimizer_update_32bit_cpuinbitsandbytes/backends/cpu/ops.py) indexesstate1[0]andstate1[1], so the firststep()raisesRuntimeError: output with shape [] doesn't match the broadcast shape [4096]._optimizer_update_32bitinbitsandbytes/backends/default/ops.py, used on MPS) does the same indexing and raises the same error.kOptimizer32bit2Stateincsrc/kernels.cu, line 674) loads m2 withLoad(&(state1[n + i]), s3_vals, ...)on a buffer ofnfloats. I do not have a CUDA device to run this. From the source, it reads and later writes past the end ofstate1.The scheduler is lost as well.
Optimizer2State.update_stepnever appliest_alphaort_beta3, soAdEMAMix32bit(t_alpha=100, t_beta3=100)runs unscheduled without any warning.AdEMAMix8bitis not affected because it subclassesAdEMAMix.AdEMAMix(optim_bits=32)works, and that is whattests/test_optim.pyconstructs under the id"AdEMAMix32bit"(line 580), so the class itself has no test coverage. The class has had this parent since #1360 introduced it.Repro, run on
mainat8336490with torch 2.14.0, Python 3.13.12, macOS arm64,pip install -e .(CPU build). Same output withdev = "mps".Fix: make
AdEMAMix32bitsubclassAdEMAMixwithoptim_bits=32, the same wayAdEMAMix8bitdoes.PagedAdEMAMix32bitalready subclassesAdEMAMix32bitand picks it up. PR follows.