@@ -407,6 +407,7 @@ class QuantState:
407407 "nested_blocksize" ,
408408 "nested_dtype" ,
409409 "nested_offset" ,
410+ "k" ,
410411 ]
411412
412413 def __init__ (
@@ -419,6 +420,7 @@ def __init__(
419420 dtype = None ,
420421 offset = None ,
421422 state2 = None ,
423+ k = None ,
422424 ):
423425 self .absmax = absmax
424426 self .shape = shape
@@ -428,6 +430,7 @@ def __init__(
428430 self .quant_type = quant_type
429431 self .offset = offset
430432 self .state2 = state2
433+ self .k = k
431434 self .nested = state2 is not None
432435
433436 def __getitem__ (self , idx ):
@@ -637,6 +640,81 @@ def quantize_blockwise(
637640 return out , quant_state
638641
639642
643+ def quantize_blockwise_kbit (
644+ A : torch .Tensor ,
645+ k : int ,
646+ code : Optional [torch .Tensor ] = None ,
647+ absmax : Optional [torch .Tensor ] = None ,
648+ out : Optional [torch .Tensor ] = None ,
649+ blocksize = 4096 ,
650+ nested = False ,
651+ ) -> tuple [torch .Tensor , QuantState ]:
652+ """Quantize a tensor in blocks using k-bit quantization.
653+
654+ The input tensor is quantized by dividing it into blocks of `blocksize` values.
655+ The the absolute maximum value within these blocks is calculated for scaling
656+ the k-bit quantization.
657+
658+ Args:
659+ A (`torch.Tensor`): The input tensor. Supports `float16`, `bfloat16`, or `float32` datatypes.
660+ k (`int`): The number of bits for quantization (2-8).
661+ code (`torch.Tensor`, *optional*):
662+ A mapping describing the k-bit data type. If not provided, a linear map is created.
663+ absmax (`torch.Tensor`, *optional*): A tensor to use to store the absmax values.
664+ out (`torch.Tensor`, *optional*): A tensor to use to store the result.
665+ blocksize (`int`, *optional*):
666+ The size of the blocks. Defaults to 4096.
667+ Valid values are 64, 128, 256, 512, 1024, 2048, and 4096.
668+ nested (`bool`, *optional*): Whether to additionally quantize the absmax values. Defaults to False.
669+
670+ Raises:
671+ ValueError: Raised when the input data type or k value is not supported.
672+
673+ Returns:
674+ `Tuple[torch.Tensor, QuantState]`: A tuple containing the quantization results.
675+ - `torch.Tensor`: The quantized tensor.
676+ - [`QuantState`]: The state object used to undo the quantization.
677+ """
678+ if k < 2 or k > 8 :
679+ raise ValueError (f"k must be between 2 and 8, got { k } " )
680+
681+ if code is None :
682+ # Create a linear k-bit quantization map
683+ code = create_linear_map (signed = True , total_bits = k ).to (A .device )
684+
685+ _out , _absmax = torch .ops .bitsandbytes .quantize_blockwise_kbit .default (
686+ A ,
687+ k ,
688+ code .to (A .device ),
689+ blocksize ,
690+ )
691+
692+ if nested :
693+ offset = _absmax .mean ()
694+ _absmax -= offset
695+ qabsmax , state2 = quantize_blockwise (_absmax , blocksize = blocksize , nested = False )
696+ quant_state = QuantState (
697+ absmax = qabsmax ,
698+ code = code .to (A .device , copy = True ),
699+ blocksize = blocksize ,
700+ dtype = A .dtype ,
701+ offset = offset ,
702+ state2 = state2 ,
703+ k = k ,
704+ )
705+ else :
706+ quant_state = QuantState (absmax = _absmax , code = code .to (A .device , copy = True ), blocksize = blocksize , dtype = A .dtype , k = k )
707+
708+ # TODO(matthewdouglas): Deprecate out kwarg
709+ out = out .copy_ (_out ) if out is not None else _out
710+
711+ # TODO(matthewdouglas): Deprecate absmax kwarg
712+ if absmax is not None :
713+ quant_state .absmax = absmax .copy_ (quant_state .absmax )
714+
715+ return out , quant_state
716+
717+
640718def dequantize_blockwise (
641719 A : torch .Tensor ,
642720 quant_state : Optional [QuantState ] = None ,
@@ -714,6 +792,91 @@ def dequantize_blockwise(
714792 )
715793
716794
795+ def dequantize_blockwise_kbit (
796+ A : torch .Tensor ,
797+ k : int ,
798+ quant_state : Optional [QuantState ] = None ,
799+ absmax : Optional [torch .Tensor ] = None ,
800+ code : Optional [torch .Tensor ] = None ,
801+ out : Optional [torch .Tensor ] = None ,
802+ blocksize : int = 4096 ,
803+ nested = False ,
804+ ) -> torch .Tensor :
805+ """Dequantize a tensor in blocks using k-bit dequantization.
806+
807+ The input tensor is dequantized by dividing it into blocks of `blocksize` values.
808+ The the absolute maximum value within these blocks is used for scaling
809+ the k-bit dequantization.
810+
811+ Args:
812+ A (`torch.Tensor`): The quantized input tensor.
813+ k (`int`): The number of bits used for quantization (2-8).
814+ quant_state ([`QuantState`], *optional*):
815+ The quantization state as returned by [`quantize_blockwise_kbit`].
816+ Required if `absmax` is not provided.
817+ absmax (`torch.Tensor`, *optional*):
818+ A tensor containing the scaling values.
819+ Required if `quant_state` is not provided and ignored otherwise.
820+ code (`torch.Tensor`, *optional*):
821+ A mapping describing the k-bit data type. If not provided, a linear map is created.
822+ Ignored when `quant_state` is provided.
823+ out (`torch.Tensor`, *optional*): A tensor to use to store the result.
824+ blocksize (`int`, *optional*):
825+ The size of the blocks. Defaults to 4096.
826+ Valid values are 64, 128, 256, 512, 1024, 2048, and 4096.
827+ Ignored when `quant_state` is provided.
828+
829+ Raises:
830+ ValueError: Raised when the input data type or k value is not supported.
831+
832+ Returns:
833+ `torch.Tensor`:
834+ The dequantized tensor. The datatype is indicated by `quant_state.dtype` and defaults to `torch.float32`.
835+ """
836+ if k < 2 or k > 8 :
837+ raise ValueError (f"k must be between 2 and 8, got { k } " )
838+
839+ assert quant_state is not None or absmax is not None
840+ if code is None and quant_state is None :
841+ # Create a linear k-bit quantization map
842+ code = create_linear_map (signed = True , total_bits = k ).to (A .device )
843+
844+ if quant_state is None :
845+ quant_state = QuantState (absmax = absmax , code = code , blocksize = blocksize , dtype = torch .float32 , k = k )
846+
847+ absmax = quant_state .absmax
848+ if quant_state .nested :
849+ absmax = dequantize_blockwise (quant_state .absmax , quant_state .state2 )
850+ absmax += quant_state .offset
851+ if absmax .dtype != torch .float32 :
852+ absmax = absmax .float ()
853+
854+ # Get k from quant_state if available
855+ if hasattr (quant_state , 'k' ):
856+ k = quant_state .k
857+
858+ if out is not None :
859+ torch .ops .bitsandbytes .dequantize_blockwise_kbit .out (
860+ A ,
861+ k ,
862+ absmax ,
863+ quant_state .code .to (A .device ),
864+ quant_state .blocksize ,
865+ quant_state .dtype ,
866+ out = out ,
867+ )
868+ return out
869+
870+ return torch .ops .bitsandbytes .dequantize_blockwise_kbit .default (
871+ A ,
872+ k ,
873+ absmax ,
874+ quant_state .code .to (A .device ),
875+ quant_state .blocksize ,
876+ quant_state .dtype ,
877+ )
878+
879+
717880def get_4bit_type (typename , device = None , blocksize = 64 ):
718881 if device is None :
719882 device = "cuda"
0 commit comments