From 5486a829145c9881d82e3c37ef117a244af28682 Mon Sep 17 00:00:00 2001 From: kritikagarg Date: Fri, 28 Aug 2026 04:47:29 +0000 Subject: [PATCH] fix(optim): mark unused amsgrad/optim_bits params as deprecated in 8-bit Adam classes Replace misleading "Note:" docstring entries with proper `.. deprecated::` Sphinx directives in Adam8bit, PagedAdam8bit, AdamW8bit, and PagedAdamW8bit. These parameters exist only for signature compatibility with the base Adam/AdamW classes and will be removed in a future version. Add test coverage for the Adam8bit/AdamW8bit parameter validation guards which previously only existed for LAMB8bit and Adagrad8bit. Fixes #1261 --- bitsandbytes/optim/adam.py | 27 ++++++++++++++----------- bitsandbytes/optim/adamw.py | 22 ++++++++++++-------- tests/test_optim.py | 40 +++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 20 deletions(-) diff --git a/bitsandbytes/optim/adam.py b/bitsandbytes/optim/adam.py index 63210bdc3..148f9e301 100644 --- a/bitsandbytes/optim/adam.py +++ b/bitsandbytes/optim/adam.py @@ -88,11 +88,14 @@ def __init__( weight_decay (`float`, defaults to 0.0): The weight decay value for the optimizer. amsgrad (`bool`, defaults to `False`): - Whether to use the [AMSGrad](https://hf.co/papers/1904.09237) variant of Adam that uses the maximum of past squared gradients instead. - Note: This parameter is not supported in Adam8bit and must be False. + .. deprecated:: + Not supported in Adam8bit and must be False. This parameter exists only for + signature compatibility with :class:`Adam` and will be removed in a future version. optim_bits (`int`, defaults to 32): - The number of bits of the optimizer state. - Note: This parameter is not used in Adam8bit as it always uses 8-bit optimization. + .. deprecated:: + Not used in Adam8bit. This optimizer always uses 8-bit states regardless of this + value. The parameter exists only for signature compatibility with :class:`Adam` + and will be removed in a future version. args (`object`, defaults to `None`): An object with additional arguments. min_8bit_size (`int`, defaults to 4096): @@ -100,13 +103,10 @@ def __init__( is_paged (`bool`, defaults to `False`): Whether the optimizer is a paged optimizer or not. """ - # Validate unsupported parameters if amsgrad: raise ValueError("Adam8bit does not support amsgrad=True") if optim_bits != 32: - # We allow the default value of 32 to maintain compatibility with the function signature, - # but any other value is invalid since Adam8bit always uses 8-bit optimization raise ValueError("Adam8bit only supports optim_bits=32 (default value for compatibility)") super().__init__( @@ -116,7 +116,7 @@ def __init__( betas, eps, weight_decay, - 8, # Hardcoded to 8 bits + 8, args, min_8bit_size, is_paged=is_paged, @@ -258,11 +258,14 @@ def __init__( weight_decay (`float`, defaults to 0.0): The weight decay value for the optimizer. amsgrad (`bool`, defaults to `False`): - Whether to use the [AMSGrad](https://hf.co/papers/1904.09237) variant of Adam that uses the maximum of past squared gradients instead. - Note: This parameter is not supported in PagedAdam8bit and must be False. + .. deprecated:: + Not supported in PagedAdam8bit and must be False. This parameter exists only for + signature compatibility with :class:`Adam` and will be removed in a future version. optim_bits (`int`, defaults to 32): - The number of bits of the optimizer state. - Note: This parameter is not used in PagedAdam8bit as it always uses 8-bit optimization. + .. deprecated:: + Not used in PagedAdam8bit. This optimizer always uses 8-bit states regardless of + this value. The parameter exists only for signature compatibility with :class:`Adam` + and will be removed in a future version. args (`object`, defaults to `None`): An object with additional arguments. min_8bit_size (`int`, defaults to 4096): diff --git a/bitsandbytes/optim/adamw.py b/bitsandbytes/optim/adamw.py index 36e151dfc..3d7929136 100644 --- a/bitsandbytes/optim/adamw.py +++ b/bitsandbytes/optim/adamw.py @@ -88,11 +88,14 @@ def __init__( weight_decay (`float`, defaults to 1e-2): The weight decay value for the optimizer. amsgrad (`bool`, defaults to `False`): - Whether to use the [AMSGrad](https://hf.co/papers/1904.09237) variant of Adam that uses the maximum of past squared gradients instead. - Note: This parameter is not supported in AdamW8bit and must be False. + .. deprecated:: + Not supported in AdamW8bit and must be False. This parameter exists only for + signature compatibility with :class:`AdamW` and will be removed in a future version. optim_bits (`int`, defaults to 32): - The number of bits of the optimizer state. - Note: This parameter is not used in AdamW8bit as it always uses 8-bit optimization. + .. deprecated:: + Not used in AdamW8bit. This optimizer always uses 8-bit states regardless of this + value. The parameter exists only for signature compatibility with :class:`AdamW` + and will be removed in a future version. args (`object`, defaults to `None`): An object with additional arguments. min_8bit_size (`int`, defaults to 4096): @@ -254,11 +257,14 @@ def __init__( weight_decay (`float`, defaults to 1e-2): The weight decay value for the optimizer. amsgrad (`bool`, defaults to `False`): - Whether to use the [AMSGrad](https://hf.co/papers/1904.09237) variant of Adam that uses the maximum of past squared gradients instead. - Note: This parameter is not supported in PagedAdamW8bit and must be False. + .. deprecated:: + Not supported in PagedAdamW8bit and must be False. This parameter exists only for + signature compatibility with :class:`AdamW` and will be removed in a future version. optim_bits (`int`, defaults to 32): - The number of bits of the optimizer state. - Note: This parameter is not used in PagedAdamW8bit as it always uses 8-bit optimization. + .. deprecated:: + Not used in PagedAdamW8bit. This optimizer always uses 8-bit states regardless of + this value. The parameter exists only for signature compatibility with :class:`AdamW` + and will be removed in a future version. args (`object`, defaults to `None`): An object with additional arguments. min_8bit_size (`int`, defaults to 4096): diff --git a/tests/test_optim.py b/tests/test_optim.py index 29736311d..40dfc50c7 100644 --- a/tests/test_optim.py +++ b/tests/test_optim.py @@ -741,3 +741,43 @@ def test_adagrad8bit_rejects_non_8_optim_bits(): bnb.optim.Adagrad8bit(p, optim_bits=32) # default (optim_bits=8) still constructs bnb.optim.Adagrad8bit(p) + + +def test_adam8bit_rejects_amsgrad(): + # amsgrad is not supported in Adam8bit; must be False (relates to #1261). + p = [torch.nn.Parameter(torch.randn(8, 8))] + with pytest.raises(ValueError): + bnb.optim.Adam8bit(p, amsgrad=True) + # default (amsgrad=False) still constructs + bnb.optim.Adam8bit(p) + + +def test_adam8bit_rejects_non_default_optim_bits(): + # optim_bits is ignored (Adam8bit always uses 8-bit); guard invalid values (relates to #1261). + p = [torch.nn.Parameter(torch.randn(8, 8))] + with pytest.raises(ValueError): + bnb.optim.Adam8bit(p, optim_bits=8) + with pytest.raises(ValueError): + bnb.optim.Adam8bit(p, optim_bits=16) + # default (optim_bits=32) still constructs + bnb.optim.Adam8bit(p) + + +def test_adamw8bit_rejects_amsgrad(): + # amsgrad is not supported in AdamW8bit; must be False (relates to #1261). + p = [torch.nn.Parameter(torch.randn(8, 8))] + with pytest.raises(ValueError): + bnb.optim.AdamW8bit(p, amsgrad=True) + # default (amsgrad=False) still constructs + bnb.optim.AdamW8bit(p) + + +def test_adamw8bit_rejects_non_default_optim_bits(): + # optim_bits is ignored (AdamW8bit always uses 8-bit); guard invalid values (relates to #1261). + p = [torch.nn.Parameter(torch.randn(8, 8))] + with pytest.raises(ValueError): + bnb.optim.AdamW8bit(p, optim_bits=8) + with pytest.raises(ValueError): + bnb.optim.AdamW8bit(p, optim_bits=16) + # default (optim_bits=32) still constructs + bnb.optim.AdamW8bit(p)