@@ -315,9 +315,37 @@ def forward(ctx, A, B, out=None, bias=None, quant_state: Optional[F.QuantState]
315315 else :
316316 return torch .empty (A .shape [:- 1 ] + B_shape [:1 ], dtype = A .dtype , device = A .device )
317317
318- # 1. Dequantize
319- # 2. MatmulnN
320- output = torch .nn .functional .linear (A , F .dequantize_4bit (B , quant_state ).to (A .dtype ).t (), bias )
318+ # Normalize to canonical [(N*K+1)//2, 1]. Packed weights are always contiguous
319+ # in this orientation (B.t() callers get strides [1,1], still compatible).
320+ # quant_state.shape is the source of truth for N and K.
321+ B = B .view (- 1 , 1 )
322+
323+ if not quant_state .nested :
324+ output = torch .ops .bitsandbytes .gemm_4bit .default (
325+ A ,
326+ B ,
327+ quant_state .shape ,
328+ quant_state .absmax ,
329+ quant_state .blocksize ,
330+ quant_state .quant_type ,
331+ bias = bias ,
332+ )
333+ elif quant_state .state2 .blocksize == 256 :
334+ output = torch .ops .bitsandbytes .gemm_4bit .default (
335+ A ,
336+ B ,
337+ quant_state .shape ,
338+ quant_state .state2 .absmax ,
339+ quant_state .blocksize ,
340+ quant_state .quant_type ,
341+ bias = bias ,
342+ absmax_8bit = quant_state .absmax ,
343+ absmax_code = quant_state .state2 .code ,
344+ absmax_offset = quant_state .offset ,
345+ )
346+ else :
347+ raise NotImplementedError ("nested quantization with state2.blocksize != 256 is not supported" )
348+
321349 if out is not None :
322350 out .copy_ (output )
323351 output = out
@@ -351,7 +379,9 @@ def backward(ctx, grad_output):
351379 # not supported by PyTorch. TODO: create work-around
352380 # if req_gradB: grad_B = torch.matmul(grad_output.t(), A)
353381 if req_gradA :
354- grad_A = torch .matmul (grad_output , F .dequantize_4bit (B , ctx .state ).to (grad_output .dtype ).t ())
382+ # B in ctx.tensors is already in canonical [(N*K+1)//2, 1] form (normalized in forward).
383+ # dequantize returns [N, K]; matmul(grad_output[M,N], [N,K]) = grad_A[M,K].
384+ grad_A = torch .matmul (grad_output , F .dequantize_4bit (B , ctx .state ).to (grad_output .dtype ))
355385
356386 return grad_A , grad_B , None , grad_bias , None
357387
@@ -381,26 +411,81 @@ def matmul_4bit(
381411 out : Optional [torch .Tensor ] = None ,
382412 bias : Optional [torch .Tensor ] = None ,
383413):
384- assert quant_state is not None
385- if A .device .type == "cpu" :
386- if getattr (quant_state , "packing_format_for_cpu" , False ):
387- out = F .gemv_4bit (A , B , out , state = quant_state )
388- if bias is not None :
389- out += bias
390- return out
391- else :
392- return MatMul4Bit .apply (A , B , out , bias , quant_state )
393-
394- if A .numel () == A .shape [- 1 ] and A .requires_grad == False and A .device .type != "hpu" :
395- if A .shape [- 1 ] % quant_state .blocksize != 0 :
414+ if quant_state is None :
415+ raise ValueError ("quant_state is required" )
416+ if len (quant_state .shape ) != 2 :
417+ raise ValueError ("matmul_4bit: quant_state.shape must be 2D [N, K]" )
418+
419+ # packing_format_for_cpu uses a different memory layout optimized for AVX512BF16.
420+ # This flag is only set for inference (weight conversion happens at eval time).
421+ # The underlying kernel supports any M via tiled GEMM despite the gemv name.
422+ if A .device .type == "cpu" and getattr (quant_state , "packing_format_for_cpu" , False ):
423+ result = F .gemv_4bit (A , B , out = out , state = quant_state )
424+ if bias is not None :
425+ result += bias
426+ return result
427+
428+ # Normalize B to canonical [(N*K+1)//2, 1]. Packed weights are always contiguous
429+ # in this orientation (B.t() callers get strides [1,1], still compatible).
430+ # quant_state.shape is the source of truth for N and K.
431+ B = B .view (- 1 , 1 )
432+
433+ K = A .shape [- 1 ]
434+
435+ # Weight is in [K, N] orientation when A's inner dim matches shape[0] not shape[1].
436+ # Square weights (K==N) are ambiguous and treated as [N, K].
437+ if K == quant_state .shape [0 ] and K != quant_state .shape [1 ]:
438+ if not _is_compiling ():
396439 warn (
397- f"Some matrices hidden dimension is not a multiple of { quant_state .blocksize } and efficient inference kernels are not supported for these (slow). Matrix input size found: { A .shape } " ,
440+ f"matmul_4bit: weight was quantized from a [K, N] tensor (quant_state.shape={ list (quant_state .shape )} ). "
441+ "Re-quantize from the weight in [N, K] (out_features, in_features) orientation. "
442+ "This will be an error in a future version." ,
443+ DeprecationWarning ,
444+ stacklevel = 2 ,
445+ )
446+ B_dq = F .dequantize_4bit (B , quant_state ).to (A .dtype )
447+ result = torch .nn .functional .linear (A , B_dq .t (), bias )
448+ if out is not None :
449+ out .copy_ (result )
450+ return out
451+ return result
452+
453+ needs_grad = torch .is_grad_enabled () and (A .requires_grad or (bias is not None and bias .requires_grad ))
454+ if not needs_grad :
455+ A_numel = A .numel ()
456+ if A_numel == 0 :
457+ if out is not None :
458+ return out
459+ return torch .empty ((* A .shape [:- 1 ], quant_state .shape [0 ]), dtype = A .dtype , device = A .device )
460+
461+ if not quant_state .nested :
462+ result = torch .ops .bitsandbytes .gemm_4bit .default (
463+ A ,
464+ B ,
465+ quant_state .shape ,
466+ quant_state .absmax ,
467+ quant_state .blocksize ,
468+ quant_state .quant_type ,
469+ bias = bias ,
470+ )
471+ elif quant_state .state2 .blocksize == 256 :
472+ result = torch .ops .bitsandbytes .gemm_4bit .default (
473+ A ,
474+ B ,
475+ quant_state .shape ,
476+ quant_state .state2 .absmax ,
477+ quant_state .blocksize ,
478+ quant_state .quant_type ,
479+ bias = bias ,
480+ absmax_8bit = quant_state .absmax ,
481+ absmax_code = quant_state .state2 .code ,
482+ absmax_offset = quant_state .offset ,
398483 )
399- return MatMul4Bit .apply (A , B , out , bias , quant_state )
400484 else :
401- out = F . gemv_4bit ( A , B . t (), out , state = quant_state )
402- if bias is not None :
403- out += bias
485+ raise NotImplementedError ( "nested quantization with state2.blocksize != 256 is not supported" )
486+ if out is not None :
487+ out . copy_ ( result )
404488 return out
405- else :
406- return MatMul4Bit .apply (A , B , out , bias , quant_state )
489+ return result
490+
491+ return MatMul4Bit .apply (A , B , out , bias , quant_state )
0 commit comments