bitsandbytes 0.50.0__py3-none-win_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. bitsandbytes/__init__.py +78 -0
  2. bitsandbytes/__main__.py +4 -0
  3. bitsandbytes/_ops.py +510 -0
  4. bitsandbytes/autograd/__init__.py +0 -0
  5. bitsandbytes/autograd/_functions.py +491 -0
  6. bitsandbytes/backends/__init__.py +0 -0
  7. bitsandbytes/backends/cpu/__init__.py +0 -0
  8. bitsandbytes/backends/cpu/ops.py +580 -0
  9. bitsandbytes/backends/cuda/__init__.py +0 -0
  10. bitsandbytes/backends/cuda/ops.py +1199 -0
  11. bitsandbytes/backends/default/__init__.py +0 -0
  12. bitsandbytes/backends/default/ops.py +632 -0
  13. bitsandbytes/backends/hpu/__init__.py +0 -0
  14. bitsandbytes/backends/hpu/ops.py +53 -0
  15. bitsandbytes/backends/mps/__init__.py +0 -0
  16. bitsandbytes/backends/mps/ops.py +277 -0
  17. bitsandbytes/backends/triton/__init__.py +0 -0
  18. bitsandbytes/backends/triton/kernels_4bit.py +577 -0
  19. bitsandbytes/backends/triton/kernels_8bit_quant.py +195 -0
  20. bitsandbytes/backends/triton/kernels_optim.py +1177 -0
  21. bitsandbytes/backends/triton/ops.py +304 -0
  22. bitsandbytes/backends/utils.py +94 -0
  23. bitsandbytes/backends/xpu/__init__.py +0 -0
  24. bitsandbytes/backends/xpu/ops.py +305 -0
  25. bitsandbytes/cextension.py +405 -0
  26. bitsandbytes/consts.py +12 -0
  27. bitsandbytes/cuda_specs.py +111 -0
  28. bitsandbytes/diagnostics/__init__.py +0 -0
  29. bitsandbytes/diagnostics/cuda.py +193 -0
  30. bitsandbytes/diagnostics/main.py +134 -0
  31. bitsandbytes/diagnostics/utils.py +12 -0
  32. bitsandbytes/functional.py +1810 -0
  33. bitsandbytes/libbitsandbytes_cpu.dll +0 -0
  34. bitsandbytes/nn/__init__.py +19 -0
  35. bitsandbytes/nn/modules.py +1220 -0
  36. bitsandbytes/nn/parametrize.py +206 -0
  37. bitsandbytes/optim/__init__.py +22 -0
  38. bitsandbytes/optim/adagrad.py +187 -0
  39. bitsandbytes/optim/adam.py +346 -0
  40. bitsandbytes/optim/adamw.py +337 -0
  41. bitsandbytes/optim/ademamix.py +410 -0
  42. bitsandbytes/optim/lamb.py +190 -0
  43. bitsandbytes/optim/lars.py +259 -0
  44. bitsandbytes/optim/lion.py +266 -0
  45. bitsandbytes/optim/optimizer.py +756 -0
  46. bitsandbytes/optim/rmsprop.py +170 -0
  47. bitsandbytes/optim/sgd.py +152 -0
  48. bitsandbytes/py.typed +0 -0
  49. bitsandbytes/utils.py +208 -0
  50. bitsandbytes-0.50.0.dist-info/METADATA +288 -0
  51. bitsandbytes-0.50.0.dist-info/RECORD +54 -0
  52. bitsandbytes-0.50.0.dist-info/WHEEL +5 -0
  53. bitsandbytes-0.50.0.dist-info/licenses/LICENSE +21 -0
  54. bitsandbytes-0.50.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1199 @@
1
+ from collections.abc import Sequence
2
+ import ctypes as ct
3
+ import functools
4
+ from math import prod
5
+ from typing import Optional
6
+ from warnings import warn
7
+
8
+ import torch
9
+
10
+ from bitsandbytes.functional import CUBLAS_Context, _cuda_device_of, get_ptr
11
+
12
+ from ..._ops import register_kernel
13
+ from ...cextension import lib
14
+
15
+
16
+ def _setup_ctypes(names, argtypes, restype=None):
17
+ for name in names:
18
+ fn = getattr(lib, name)
19
+ fn.argtypes = argtypes
20
+ fn.restype = restype
21
+
22
+
23
+ # 4-bit/8-bit dequantize: (code, A, absmax, out, blocksize, numel, stream)
24
+ _setup_ctypes(
25
+ [f"cdequantize_blockwise_{d}_{q}" for d in ("fp32", "bf16", "fp16") for q in ("nf4", "fp4")]
26
+ + [f"cdequantize_blockwise_{d}" for d in ("fp32", "bf16", "fp16")],
27
+ [ct.c_void_p] * 4 + [ct.c_int32, ct.c_int32, ct.c_void_p],
28
+ )
29
+
30
+ # 4-bit GEMM: (A, B, absmax, absmax_8bit, absmax_code, absmax_offset, out, bias, M, N, K, blocksize, quant_type, stream)
31
+ _setup_ctypes(
32
+ [f"cgemm_4bit_{d}" for d in ("bf16", "fp16", "fp32")],
33
+ [ct.c_void_p] * 8 + [ct.c_int32, ct.c_int32, ct.c_int32, ct.c_int32, ct.c_int32, ct.c_void_p],
34
+ )
35
+
36
+ # 4-bit GEMV: (m, n, k, A, B, absmax, code, out, lda, ldb, ldc, blocksize, stream)
37
+ _setup_ctypes(
38
+ [f"cgemm_4bit_inference_naive_{d}" for d in ("bf16", "fp16", "fp32")],
39
+ [ct.c_int32] * 3 + [ct.c_void_p] * 5 + [ct.c_int32] * 3 + [ct.c_int32, ct.c_void_p],
40
+ )
41
+
42
+ # int8 igemm: (ctx, m, n, k, A, B, C, rowscale, lda, ldb, ldc, stream) -> int32
43
+ _setup_ctypes(
44
+ ["cigemmlt_32"],
45
+ [ct.c_void_p] + [ct.c_int32] * 3 + [ct.c_void_p] * 4 + [ct.c_int32] * 3 + [ct.c_void_p],
46
+ restype=ct.c_int32,
47
+ )
48
+
49
+ # int8 mm dequant: (A, row_stats, col_stats, out, bias, numRows, numCols, stream)
50
+ _setup_ctypes(
51
+ ["cdequant_mm_int32_fp16"],
52
+ [ct.c_void_p] * 5 + [ct.c_int32, ct.c_int32, ct.c_void_p],
53
+ )
54
+
55
+ # int8 vectorwise quant: (A, out, row_stats, threshold, rows, cols, stream)
56
+ _setup_ctypes(
57
+ ["cint8_vector_quant"],
58
+ [ct.c_void_p] * 3 + [ct.c_float, ct.c_int32, ct.c_int32, ct.c_void_p],
59
+ )
60
+
61
+ # 4-bit/8-bit blockwise quantize: (code, A, absmax, out, blocksize, n)
62
+ _setup_ctypes(
63
+ [f"cquantize_blockwise_{d}_{q}" for d in ("fp32", "bf16", "fp16") for q in ("nf4", "fp4")]
64
+ + [f"cquantize_blockwise_{d}" for d in ("fp32", "bf16", "fp16")],
65
+ [ct.c_void_p] * 4 + [ct.c_int32, ct.c_int32],
66
+ )
67
+
68
+
69
+ _get_raw_stream = torch._C._cuda_getCurrentRawStream
70
+
71
+
72
+ @functools.cache
73
+ def _gpu_dispatch_props(device_index):
74
+ props = torch.cuda.get_device_properties(device_index)
75
+ return props.multi_processor_count, props.major, props.minor
76
+
77
+
78
+ @register_kernel("bitsandbytes::int8_linear_matmul", "cuda")
79
+ def _(A: torch.Tensor, B: torch.Tensor):
80
+ out = torch.empty((*A.shape[:-1], B.shape[0]), device=A.device, dtype=torch.int32)
81
+ return _int8_linear_matmul_impl(A, B, out)
82
+
83
+
84
+ @register_kernel("bitsandbytes::int8_linear_matmul.out", "cuda")
85
+ def _(A: torch.Tensor, B: torch.Tensor, out: torch.Tensor):
86
+ _int8_linear_matmul_impl(A, B, out)
87
+
88
+
89
+ def _int8_linear_matmul_impl(A: torch.Tensor, B: torch.Tensor, out: torch.Tensor):
90
+ A, B = B, A
91
+
92
+ shapeA = A.shape
93
+ shapeB = B.shape
94
+
95
+ if A.dtype != torch.int8:
96
+ raise ValueError("B must be int8")
97
+ if B.dtype != torch.int8:
98
+ raise ValueError("A must be int8")
99
+ if A.ndim != 2:
100
+ raise ValueError("Only two dimensional matrices are supported for argument B")
101
+ if B.ndim not in (2, 3):
102
+ raise ValueError("Only two or three dimensional matrices are supported for argument A")
103
+ if prod(shapeB) <= 0:
104
+ raise ValueError(f"Input tensor dimensions need to be > 0: {shapeB}")
105
+ if out.dtype != torch.int32:
106
+ raise ValueError(f"out must be int32, got {out.dtype}")
107
+
108
+ shapeC = (*shapeB[:-1], shapeA[0])
109
+ if out.shape != shapeC:
110
+ raise ValueError(f"Output shape {out.shape} does not match expected shape {shapeC}")
111
+
112
+ k, m = shapeA
113
+ n = prod(shapeB[:-1])
114
+ lda = shapeA[-1] # Weights (outputs, inputs)
115
+ ldb = shapeB[-1] # Activations (batch, tokens, inputs)
116
+ ldc = shapeC[-1] # Output (batch, tokens, outputs)
117
+
118
+ if lda != ldb:
119
+ raise ValueError(
120
+ f"int8_linear_matmul only supports B^T @ A. Inner dimensions do not match: B @ A = {shapeB} @ {shapeA}"
121
+ )
122
+
123
+ # cuBLASLt does not support int8 matmul with inner dimensions that are not divisible by 4.
124
+ # We'll fall back to a slower fp32 calculation in this circumstance.
125
+ # Fortunately, this should not be very common.
126
+ if lda % 4 != 0:
127
+ result = torch.matmul(B.float(), A.float().t()).to(torch.int32)
128
+ return out.copy_(result)
129
+
130
+ with _cuda_device_of(A):
131
+ ctx = CUBLAS_Context.get_instance().get_context(A.device)
132
+ has_error = lib.cigemmlt_32(
133
+ ctx,
134
+ m,
135
+ n,
136
+ k,
137
+ A.data_ptr(),
138
+ B.data_ptr(),
139
+ out.data_ptr(),
140
+ None,
141
+ lda,
142
+ ldb,
143
+ ldc,
144
+ _get_raw_stream(A.device.index),
145
+ )
146
+
147
+ if has_error:
148
+ if has_error == 100:
149
+ # `ERR_NOT_IMPLEMENTED` is defined as 100 in `ops.cu`. The HIP backend
150
+ # also returns this when no usable hipBLASLt algo exists for the shape
151
+ # (seen on MI300X for some small-n int8 gemms). Fall back to fp32 — same
152
+ # path used for the `lda % 4 != 0` case above.
153
+ import warnings
154
+
155
+ warnings.warn(
156
+ f"int8_linear_matmul has no usable (hip|cu)blasLt algo for shape "
157
+ f"{shapeA=} {shapeB=}; falling back to fp32 matmul.",
158
+ RuntimeWarning,
159
+ stacklevel=2,
160
+ )
161
+ result = torch.matmul(B.float(), A.float().t()).to(torch.int32)
162
+ return out.copy_(result)
163
+ else:
164
+ raise RuntimeError(
165
+ f"cublasLt ran into an error!\n\t{shapeA=}, {shapeB=}, {shapeC=}\n\t{(lda, ldb, ldc)=}\n\t{(m, n, k)=}"
166
+ )
167
+
168
+ return out
169
+
170
+
171
+ @register_kernel("bitsandbytes::int8_mm_dequant", "cuda")
172
+ def _(
173
+ A: torch.Tensor,
174
+ row_stats: torch.Tensor,
175
+ col_stats: torch.Tensor,
176
+ dtype: Optional[torch.dtype] = None,
177
+ bias: Optional[torch.Tensor] = None,
178
+ ) -> torch.Tensor:
179
+ if A.dtype != torch.int32:
180
+ raise ValueError(f"A must be int32, got {A.dtype}")
181
+ if row_stats.dtype != torch.float32:
182
+ raise ValueError(f"row_stats must be float32, got {row_stats.dtype}")
183
+ if col_stats.dtype != torch.float32:
184
+ raise ValueError(f"col_stats must be float32, got {col_stats.dtype}")
185
+
186
+ # Note: cuda kernel only currently supports fp16 output.
187
+ # We'll later cast to desired dtype if needed.
188
+ out = torch.empty_like(A, dtype=torch.float16)
189
+
190
+ # Note: fused bias in the kernel is only supported for fp16
191
+ # TODO(matthewdouglas): Consider supporting bf16 fused bias
192
+ bias_ptr = bias.data_ptr() if bias is not None and bias.dtype == torch.float16 else None
193
+
194
+ with _cuda_device_of(A):
195
+ lib.cdequant_mm_int32_fp16(
196
+ A.data_ptr(),
197
+ row_stats.data_ptr(),
198
+ col_stats.data_ptr(),
199
+ out.data_ptr(),
200
+ bias_ptr,
201
+ A.numel() // A.shape[-1],
202
+ A.shape[-1],
203
+ _get_raw_stream(A.device.index),
204
+ )
205
+
206
+ # Add bias separately if not fused in kernel
207
+ if bias is not None and bias.dtype != torch.float16:
208
+ out.add_(bias)
209
+
210
+ return out.to(dtype or torch.float16)
211
+
212
+
213
+ @register_kernel("bitsandbytes::int8_vectorwise_quant", "cuda")
214
+ def _(A: torch.Tensor, threshold=0.0):
215
+ if A.dtype != torch.float16:
216
+ raise ValueError(f"A must be float16, got {A.dtype}")
217
+ if threshold < 0.0:
218
+ raise ValueError("threshold must be non-negative")
219
+
220
+ rows = A.numel() // A.shape[-1]
221
+ cols = A.shape[-1]
222
+
223
+ row_stats = torch.empty(rows, device=A.device, dtype=torch.float32)
224
+ out_row = torch.empty(A.shape, device=A.device, dtype=torch.int8)
225
+
226
+ outlier_cols = None
227
+
228
+ if threshold > 0.0:
229
+ # TODO we could improve perf of this
230
+ outliers = A.abs() >= threshold
231
+
232
+ if outliers.any():
233
+ outlier_cols = torch.argwhere(outliers.any(dim=0)).view(-1)
234
+ else:
235
+ # Needed for torch.compile support.
236
+ outlier_cols = torch.empty(0, device=A.device, dtype=torch.int64)
237
+
238
+ with _cuda_device_of(A):
239
+ lib.cint8_vector_quant(
240
+ A.data_ptr(),
241
+ out_row.data_ptr(),
242
+ row_stats.data_ptr(),
243
+ threshold,
244
+ rows,
245
+ cols,
246
+ _get_raw_stream(A.device.index),
247
+ )
248
+
249
+ # Zero out values from outlier columns across all rows.
250
+ # The kernel will handle this for outliers themselves, so we can optimize for rows=1.
251
+ if rows > 1 and outlier_cols is not None:
252
+ out_row[:, outlier_cols] = 0
253
+
254
+ return out_row, row_stats, outlier_cols
255
+
256
+
257
+ @register_kernel("bitsandbytes::int8_double_quant", "cuda")
258
+ def _(
259
+ A: torch.Tensor,
260
+ threshold=0.0,
261
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor, Optional[torch.Tensor]]:
262
+ # Use CUDA kernel for rowwise quant and outlier column detection
263
+ quant_row, row_stats, outlier_cols = torch.ops.bitsandbytes.int8_vectorwise_quant.default(
264
+ A,
265
+ threshold=threshold,
266
+ )
267
+
268
+ # PyTorch impl for colwise
269
+ col_stats, outlier_mask = _get_col_absmax(A, threshold=threshold)
270
+ if threshold > 0.0 and outlier_mask is not None:
271
+ A = A.masked_fill(outlier_mask, 0.0)
272
+ quant_col = torch.round(A.mul(127.0) / col_stats.unsqueeze(0)).to(torch.int8)
273
+
274
+ return quant_row, quant_col, row_stats, col_stats.flatten().float(), outlier_cols
275
+
276
+
277
+ def _get_col_absmax(
278
+ A: torch.Tensor,
279
+ threshold=0.0,
280
+ ) -> tuple[torch.Tensor, Optional[torch.Tensor]]:
281
+ if not A.is_floating_point():
282
+ raise ValueError(f"A must be a floating point tensor, got {A.dtype}")
283
+
284
+ outlier_mask = None
285
+
286
+ absA = A.abs().view(-1, A.shape[-1])
287
+
288
+ if threshold > 0.0:
289
+ # Filter outliers from stats when enabled
290
+ outlier_mask = absA >= threshold
291
+ absA.masked_fill_(outlier_mask, 0.0)
292
+
293
+ # shape [cols]; unsqueeze(0) gives [1,cols]
294
+ col_stats = absA.amax(dim=0, keepdim=False).float()
295
+
296
+ return col_stats, outlier_mask
297
+
298
+
299
+ @register_kernel("bitsandbytes::quantize_blockwise", "cuda")
300
+ def _(A: torch.Tensor, code: torch.Tensor, blocksize: int) -> tuple[torch.Tensor, torch.Tensor]:
301
+ A = A.contiguous()
302
+
303
+ if code.dtype != torch.float32:
304
+ raise ValueError(f"code must be float32, got {code.dtype}")
305
+ if blocksize not in (64, 128, 256, 512, 1024, 2048, 4096):
306
+ raise ValueError(f"invalid blocksize {blocksize}")
307
+
308
+ n = A.numel()
309
+ blocks = -(n // -blocksize)
310
+ absmax = torch.empty((blocks,), device=A.device, dtype=torch.float32)
311
+ out = torch.empty_like(A, dtype=torch.uint8)
312
+
313
+ if A.dtype == torch.float32:
314
+ fn = lib.cquantize_blockwise_fp32
315
+ elif A.dtype == torch.float16:
316
+ fn = lib.cquantize_blockwise_fp16
317
+ elif A.dtype == torch.bfloat16:
318
+ fn = lib.cquantize_blockwise_bf16
319
+ else:
320
+ raise ValueError(f"Blockwise quantization only supports 16/32-bit floats, but got {A.dtype}")
321
+
322
+ with _cuda_device_of(A):
323
+ fn(
324
+ code.data_ptr(),
325
+ A.data_ptr(),
326
+ absmax.data_ptr(),
327
+ out.data_ptr(),
328
+ blocksize,
329
+ n,
330
+ )
331
+
332
+ return out, absmax
333
+
334
+
335
+ @register_kernel("bitsandbytes::dequantize_blockwise", "cuda")
336
+ def _(A: torch.Tensor, absmax: torch.Tensor, code: torch.Tensor, blocksize: int, dtype: torch.dtype) -> torch.Tensor:
337
+ out = torch.empty_like(A, dtype=dtype)
338
+ _dequantize_blockwise_impl(A, absmax, code, blocksize, dtype, out=out)
339
+ return out
340
+
341
+
342
+ @register_kernel("bitsandbytes::dequantize_blockwise.out", "cuda")
343
+ def _(
344
+ A: torch.Tensor,
345
+ absmax: torch.Tensor,
346
+ code: torch.Tensor,
347
+ blocksize: int,
348
+ dtype: torch.dtype,
349
+ out: torch.Tensor,
350
+ ) -> None:
351
+ if out.dtype != dtype:
352
+ raise ValueError(f"Expected out.dtype == {dtype}, got {out.dtype}")
353
+ if out.shape != A.shape:
354
+ raise ValueError(f"Expected out.shape == {A.shape}, got {out.shape}")
355
+ _dequantize_blockwise_impl(A, absmax, code, blocksize, dtype, out=out)
356
+
357
+
358
+ def _dequantize_blockwise_impl(
359
+ A: torch.Tensor, absmax: torch.Tensor, code: torch.Tensor, blocksize: int, dtype: torch.dtype, out: torch.Tensor
360
+ ) -> None:
361
+ A = A.contiguous()
362
+
363
+ if dtype == torch.float32:
364
+ fn = lib.cdequantize_blockwise_fp32
365
+ elif dtype == torch.float16:
366
+ fn = lib.cdequantize_blockwise_fp16
367
+ elif dtype == torch.bfloat16:
368
+ fn = lib.cdequantize_blockwise_bf16
369
+ else:
370
+ raise ValueError(f"Blockwise dequantization only supports 16/32-bit floats, but got {dtype}")
371
+
372
+ with _cuda_device_of(A):
373
+ fn(
374
+ code.data_ptr(),
375
+ A.data_ptr(),
376
+ absmax.data_ptr(),
377
+ out.data_ptr(),
378
+ blocksize,
379
+ A.numel(),
380
+ _get_raw_stream(A.device.index),
381
+ )
382
+
383
+
384
+ @register_kernel("bitsandbytes::quantize_4bit", "cuda")
385
+ def _(
386
+ A: torch.Tensor, blocksize: int, quant_type: str, quant_storage: torch.dtype
387
+ ) -> tuple[torch.Tensor, torch.Tensor]:
388
+ A = A.contiguous()
389
+ n = A.numel()
390
+ blocks = -(n // -blocksize)
391
+ absmax = torch.empty((blocks,), device=A.device, dtype=torch.float32)
392
+ out = torch.empty(((n + 1) // (quant_storage.itemsize * 2), 1), device=A.device, dtype=quant_storage)
393
+
394
+ if A.dtype == torch.bfloat16:
395
+ if quant_type == "fp4":
396
+ fn = lib.cquantize_blockwise_bf16_fp4
397
+ else:
398
+ fn = lib.cquantize_blockwise_bf16_nf4
399
+ elif A.dtype == torch.float16:
400
+ if quant_type == "fp4":
401
+ fn = lib.cquantize_blockwise_fp16_fp4
402
+ else:
403
+ fn = lib.cquantize_blockwise_fp16_nf4
404
+ elif A.dtype == torch.float32:
405
+ if quant_type == "fp4":
406
+ fn = lib.cquantize_blockwise_fp32_fp4
407
+ else:
408
+ fn = lib.cquantize_blockwise_fp32_nf4
409
+
410
+ with _cuda_device_of(A):
411
+ fn(
412
+ None,
413
+ A.data_ptr(),
414
+ absmax.data_ptr(),
415
+ out.data_ptr(),
416
+ blocksize,
417
+ n,
418
+ )
419
+
420
+ return out, absmax
421
+
422
+
423
+ @register_kernel("bitsandbytes::dequantize_4bit", "cuda")
424
+ def _(
425
+ A: torch.Tensor,
426
+ absmax: torch.Tensor,
427
+ blocksize: int,
428
+ quant_type: str,
429
+ shape: Sequence[int],
430
+ dtype: torch.dtype,
431
+ ) -> torch.Tensor:
432
+ out = torch.empty(shape, dtype=dtype, device=A.device)
433
+ _dequantize_4bit_impl(A, absmax, blocksize, quant_type, dtype, out=out)
434
+ return out
435
+
436
+
437
+ @register_kernel("bitsandbytes::dequantize_4bit.out", "cuda")
438
+ def _(
439
+ A: torch.Tensor,
440
+ absmax: torch.Tensor,
441
+ blocksize: int,
442
+ quant_type: str,
443
+ shape: Sequence[int],
444
+ dtype: torch.dtype,
445
+ out: torch.Tensor,
446
+ ) -> None:
447
+ if out.shape != tuple(shape):
448
+ raise ValueError(f"Expected out.shape == {shape}, got {out.shape}")
449
+ if out.dtype != dtype:
450
+ raise ValueError(f"Expected out.dtype == {dtype}, got {out.dtype}")
451
+ _dequantize_4bit_impl(A, absmax, blocksize, quant_type, dtype, out=out)
452
+
453
+
454
+ def _dequantize_4bit_impl(
455
+ A: torch.Tensor,
456
+ absmax: torch.Tensor,
457
+ blocksize: int,
458
+ quant_type: str,
459
+ dtype: torch.dtype,
460
+ out: torch.Tensor,
461
+ ) -> None:
462
+ A = A.contiguous()
463
+
464
+ if dtype == torch.bfloat16:
465
+ if quant_type == "fp4":
466
+ fn = lib.cdequantize_blockwise_bf16_fp4
467
+ else:
468
+ fn = lib.cdequantize_blockwise_bf16_nf4
469
+ elif dtype == torch.float16:
470
+ if quant_type == "fp4":
471
+ fn = lib.cdequantize_blockwise_fp16_fp4
472
+ else:
473
+ fn = lib.cdequantize_blockwise_fp16_nf4
474
+ elif dtype == torch.float32:
475
+ if quant_type == "fp4":
476
+ fn = lib.cdequantize_blockwise_fp32_fp4
477
+ else:
478
+ fn = lib.cdequantize_blockwise_fp32_nf4
479
+ else:
480
+ raise ValueError(f"Blockwise 4bit dequantization only supports 16/32-bit floats, but got {dtype}")
481
+
482
+ with _cuda_device_of(A):
483
+ fn(
484
+ None,
485
+ A.data_ptr(),
486
+ absmax.data_ptr(),
487
+ out.data_ptr(),
488
+ blocksize,
489
+ out.numel(),
490
+ _get_raw_stream(A.device.index),
491
+ )
492
+
493
+
494
+ @register_kernel("bitsandbytes::gemv_4bit", "cuda")
495
+ def _(
496
+ A: torch.Tensor, B: torch.Tensor, shapeB: Sequence[int], absmax: torch.Tensor, code: torch.Tensor, blocksize: int
497
+ ) -> torch.Tensor:
498
+ shape = (*A.shape[:-1], shapeB[0])
499
+ out = torch.empty(shape, device=A.device, dtype=A.dtype)
500
+ _gemv_4bit_impl(A, B, shapeB, absmax, code, blocksize, out=out)
501
+ return out
502
+
503
+
504
+ @register_kernel("bitsandbytes::gemv_4bit.out", "cuda")
505
+ def _(
506
+ A: torch.Tensor,
507
+ B: torch.Tensor,
508
+ shapeB: Sequence[int],
509
+ absmax: torch.Tensor,
510
+ code: torch.Tensor,
511
+ blocksize: int,
512
+ out: torch.Tensor,
513
+ ) -> None:
514
+ expected_shape = (*A.shape[:-1], shapeB[0])
515
+ if out.shape != expected_shape:
516
+ raise ValueError(f"Expected out.shape == {expected_shape}, got {out.shape}")
517
+ if out.dtype != A.dtype:
518
+ raise ValueError(f"Expected out.dtype == {A.dtype}, got {out.dtype}")
519
+ _gemv_4bit_impl(A, B, shapeB, absmax, code, blocksize, out=out)
520
+
521
+
522
+ def _gemv_4bit_impl(
523
+ A: torch.Tensor,
524
+ B: torch.Tensor,
525
+ shapeB: Sequence[int],
526
+ absmax: torch.Tensor,
527
+ code: torch.Tensor,
528
+ blocksize: int,
529
+ out: torch.Tensor,
530
+ ) -> None:
531
+ if blocksize not in (32, 64, 128, 256, 512, 1024, 2048, 4096):
532
+ raise ValueError(f"invalid blocksize {blocksize}")
533
+
534
+ # Note: these checks are not strictly necessary, and cost more than they are worth, so they are commented out for now.
535
+ # torch._check(
536
+ # A.numel() == A.size(-1),
537
+ # lambda: f"A must be a vector with leading dimensions of 1, got {A.shape}",
538
+ # )
539
+ # torch._check(
540
+ # A.dtype in [torch.float16, torch.bfloat16, torch.float32],
541
+ # lambda: f"A must be float16, bfloat16, or float32, got {A.dtype}",
542
+ # )
543
+ # torch._check(
544
+ # B.dtype in [torch.uint8, torch.bfloat16, torch.float16, torch.float32],
545
+ # lambda: f"B must be backed by storage of type uint8, bfloat16, float16, or float32, got {B.dtype}",
546
+ # )
547
+ # torch._check(absmax.dtype == torch.float32, lambda: f"absmax must be float32, got {absmax.dtype}")
548
+ # torch._check(code.dtype == torch.float32, lambda: f"code must be float32, got {code.dtype}")
549
+
550
+ m = shapeB[0]
551
+ n = 1
552
+ k = shapeB[1]
553
+
554
+ lda = m
555
+ ldb = (A.shape[-1] + 1) // 2
556
+ ldc = m
557
+
558
+ if A.dtype == torch.float16:
559
+ fn = lib.cgemm_4bit_inference_naive_fp16
560
+ elif A.dtype == torch.bfloat16:
561
+ fn = lib.cgemm_4bit_inference_naive_bf16
562
+ elif A.dtype == torch.float32:
563
+ fn = lib.cgemm_4bit_inference_naive_fp32
564
+
565
+ with _cuda_device_of(A):
566
+ fn(
567
+ m,
568
+ n,
569
+ k,
570
+ A.data_ptr(),
571
+ B.data_ptr(),
572
+ absmax.data_ptr(),
573
+ code.data_ptr(),
574
+ out.data_ptr(),
575
+ lda,
576
+ ldb,
577
+ ldc,
578
+ blocksize,
579
+ _get_raw_stream(A.device.index),
580
+ )
581
+
582
+
583
+ @functools.cache
584
+ def _gemm_4bit_use_custom_cuda(device_index, dtype, M, N, K):
585
+ """Custom kernel vs dequant+F.linear heuristic for M in [5, 1536].
586
+
587
+ Per-arch notes (bf16/fp16, M >= 8, large weight):
588
+ sm75 (T4, ~300 GB/s GDDR6): fp16 MMA only; GDDR makes dequant expensive.
589
+ sm80 (A100, ~2 TB/s HBM2e): mma.sync; HBM thresholds; K-heavy shapes handled explicitly.
590
+ sm86 (A10, ~600 GB/s GDDR6): dedicated block; wider M caps than sm89 at medium N.
591
+ sm89 (4090, L40S, GDDR6X): default fallback; tall-K and large-N get higher M caps.
592
+ sm90 (H100/H200, HBM3/HBM3e): dequant+linear is much faster; thresholds are tight.
593
+ sm100 (B200/B300, HBM3e): exits early at top of function.
594
+ sm120 (RTX 5000, GDDR7): dedicated block; medium-N tiers differ from sm89.
595
+ """
596
+ if M <= _GEMM_4BIT_CUSTOM_FLOOR_M:
597
+ return True
598
+
599
+ num_sms, major, minor = _gpu_dispatch_props(device_index)
600
+ n_blocks = (N + 63) // 64
601
+
602
+ # fp32 has no MMA kernel; pre-sm75 has no MMA kernel; sm75 has fp16 MMA only.
603
+ # For all of these, custom only wins in the SIMT range (M<8).
604
+ if dtype == torch.float32 or major < 7:
605
+ return M < 8
606
+ if major == 7 and (minor < 5 or dtype != torch.float16):
607
+ return M < 8
608
+
609
+ # sm87 and sm110: no calibration data, conservative fallback.
610
+ if (major == 8 and minor == 7) or major == 11:
611
+ return False
612
+
613
+ # sm100 (B200/B300): dequant+F.linear is significantly faster than our mma.sync kernel.
614
+ if major == 10:
615
+ if n_blocks >= num_sms * 3:
616
+ return M <= 32
617
+ if n_blocks >= num_sms:
618
+ return False if K >= N else M <= 8
619
+ return False
620
+
621
+ is_sm75 = major == 7 and minor == 5
622
+ is_sm80 = major == 8 and minor == 0
623
+ is_sm86 = major == 8 and minor == 6
624
+ is_sm90 = major == 9
625
+ is_sm120 = major == 12 and minor == 0
626
+ is_hbm = is_sm80 or is_sm90 # sm100 already returned above
627
+ tall_k_2xn = K > N * 2
628
+
629
+ # Small-weight path (N*K < 4MB): dequant overhead dominates.
630
+ if N * K < 4 * 1024 * 1024:
631
+ if K * 2 < N:
632
+ # Very short K (K < N/2): latency-dominated, custom 3-9x cheaper.
633
+ if is_hbm:
634
+ # Calibrated on A100: custom wins to M=1536 (low wave), M=512 (high wave).
635
+ # Calibrated on H100/H200: custom wins to M=512 (low wave), M=320 (high wave).
636
+ low_wave = n_blocks * 3 < num_sms
637
+ if is_sm80:
638
+ return M <= (1536 if low_wave else 512)
639
+ return M <= (512 if low_wave else 320)
640
+ if is_sm75:
641
+ # T4: wins require >=3 waves; M cap scales with K depth.
642
+ if n_blocks >= num_sms * 3:
643
+ return M <= 320
644
+ if K >= 1024:
645
+ return M <= 64
646
+ if K >= 704:
647
+ return M <= 96
648
+ return M <= 320
649
+ # sm86/sm89/sm120: well-subscribed wins to M=320; undersubscribed tighter.
650
+ if n_blocks >= num_sms:
651
+ return M <= 320
652
+ return M <= 192 if n_blocks * K > num_sms * 320 else M <= 320
653
+ # K*2 >= N: arch-specific handling at low occupancy.
654
+ quarter_wave = n_blocks * 4 <= num_sms
655
+ if is_sm80 and quarter_wave:
656
+ # A100 <1/4 wave: K>=N loses earlier (K-tiling efficient on HBM2e).
657
+ if K >= N:
658
+ return M <= (32 if n_blocks * 8 <= num_sms else 128)
659
+ return M <= 384
660
+ # T4 <1 wave non-short-K: M>8 routes through occupancy caps below.
661
+ if is_sm75 and n_blocks < num_sms and M > 8:
662
+ return M <= 64
663
+ # General tiers (sm90, sm86, sm89, sm120):
664
+ # GDDR tall-K (K>=N) at <1/4 wave: K-tiling in default impl wins above M=23.
665
+ if quarter_wave:
666
+ return M <= (32 if (K < N or is_hbm) else 23)
667
+ if n_blocks * 2 <= num_sms:
668
+ return M <= 16
669
+ return False # >=1/2 wave: no validated wins for remaining small-weight shapes
670
+
671
+ # Non-small-weight: custom wins up to M=512; dequant+F.linear wins above that.
672
+ if M > 512:
673
+ return False
674
+
675
+ # M=5-7: custom SIMT generally wins because dequant cost dominates.
676
+ # Exceptions where K-tiling efficiency or MMA occupancy favors dequant+F.linear:
677
+ # HBM at M=6-7: tall-K (K>N) at ~3/4 MMA wave.
678
+ # sm90 square (K==N) at specific occupancy bands: arch-specific crossover.
679
+ if M < 8:
680
+ hbm_m67_thresh = 36 if is_sm90 else 48
681
+ if is_hbm and M >= 6 and n_blocks >= hbm_m67_thresh:
682
+ lt_75pct_wave = n_blocks * 4 < num_sms * 3
683
+ lt_60pct_wave = n_blocks * 5 < num_sms * 3
684
+ # Tall-K: K-tiling in default impl wins when under-subscribed.
685
+ if K > N and lt_75pct_wave:
686
+ return False
687
+ # Square: arch-specific crossover around 0.6 wave.
688
+ # A100 (HBM2e): loses below 0.6 wave. H100/H200 (HBM3/3e): loses above.
689
+ if K == N:
690
+ if is_sm80 and lt_60pct_wave:
691
+ return False
692
+ if is_sm90 and lt_75pct_wave and not lt_60pct_wave:
693
+ return False
694
+ return True
695
+
696
+ # M in [8, 512]: per-arch tier ladders.
697
+
698
+ if is_sm75:
699
+ # fp16 MMA (m16n8k8). GDDR bandwidth makes dequant relatively expensive.
700
+ if n_blocks >= num_sms * 3:
701
+ return M <= (128 if K < N else 64)
702
+ if n_blocks >= num_sms // 2:
703
+ return M <= 64
704
+ return M <= 32
705
+
706
+ if is_sm80:
707
+ # mma.sync (m16n8k16). HBM2e thresholds; K-heavy shapes handled explicitly.
708
+ if n_blocks >= num_sms * 3:
709
+ return M <= 128
710
+ if n_blocks >= num_sms:
711
+ return M <= (64 if K < N else 32)
712
+ # Very tall-K (K>=3N) at >1/4 wave: K-tiling in default impl wins at all M.
713
+ # Uses >= to catch K==3N (e.g. N=4096,K=12288 M=9-16: measured regression on A100).
714
+ if K >= N * 3 and n_blocks * 4 > num_sms:
715
+ return False
716
+ # Square (K==N) at 0.5-1 wave: K-tiling wins at ~0.6 wave.
717
+ # n_blocks>=48 excludes small N where SIMT still wins.
718
+ if K == N and n_blocks >= 48 and n_blocks * 5 < num_sms * 3:
719
+ return False
720
+ # <0.5 wave: K<=N custom wins to M=128; K>N default wins above wave threshold.
721
+ if n_blocks * 2 < num_sms:
722
+ if K <= N:
723
+ return M <= 128
724
+ if n_blocks * 3 >= num_sms:
725
+ return False
726
+ # 0.5-1 wave K<N: calibrated on A100 to M=128 (e.g. N=4096,K=1536 M=17-384).
727
+ if n_blocks >= num_sms // 2 and K < N:
728
+ return M <= 128
729
+ return M <= 16
730
+
731
+ if is_sm86:
732
+ # ~600-940 GB/s GDDR6/GDDR6X. Dedicated block: sm89 fallback tiers are too
733
+ # loose for 600 GB/s bandwidth and cause regressions at medium N (~N=4096).
734
+ if n_blocks >= num_sms:
735
+ return M <= 128
736
+ if n_blocks >= num_sms // 2:
737
+ return M <= 64
738
+ return M <= 16
739
+
740
+ if is_sm90:
741
+ # HBM3/HBM3e. dequant+F.linear (WGMMA path) is significantly faster than our
742
+ # mma.sync kernel; thresholds are calibrated conservatively (H100/H200 share path).
743
+ if n_blocks >= num_sms * 3:
744
+ return M <= 64
745
+ if n_blocks >= num_sms * 2:
746
+ return M <= 48
747
+ if n_blocks >= num_sms:
748
+ return M <= 32
749
+ if n_blocks >= num_sms // 2:
750
+ # Square/tall-K at <3/4 wave: K-tiling too efficient on HBM3e.
751
+ if K >= N and n_blocks * 4 < num_sms * 3:
752
+ return False
753
+ return M <= 16
754
+ return False
755
+
756
+ if is_sm120:
757
+ # GDDR7 (~1-1.8 TB/s). Medium-N threshold tiers differ from sm89.
758
+ # sm121 (DGX Spark) has a different bandwidth/SM profile; uses sm89
759
+ # fallback below until validated.
760
+ if n_blocks >= num_sms * 3:
761
+ return M <= 256
762
+ if n_blocks >= num_sms * 2:
763
+ return M <= 128
764
+ # Short-K (K<N) at ~0.8 wave: default impl competitive above M=64.
765
+ if n_blocks * 5 >= num_sms * 4:
766
+ return M <= (96 if K >= N else 64)
767
+ if n_blocks >= num_sms:
768
+ return M <= 64
769
+ if n_blocks >= num_sms // 2:
770
+ # Large-N (n_blocks>=128, N>=8192) with K>=N/2: calibrated on RTX Pro 6000 to M=64.
771
+ return M <= (64 if (K * 2 >= N and n_blocks >= 128) else 8)
772
+ if tall_k_2xn and n_blocks > 64:
773
+ return M <= 16
774
+ return M <= 8
775
+
776
+ # Fallback: sm89 (4090, L40S, L4), sm121 (DGX Spark), unrecognized arches.
777
+ # GDDR bandwidth makes dequant relatively expensive so custom wins at higher M.
778
+ if n_blocks >= num_sms * 3:
779
+ return M <= 256
780
+ if n_blocks >= num_sms * 2:
781
+ return M <= 128
782
+ # Near-wave (~0.8x): tall-K and very large N (n_blocks>=200, N>=14336) raise cap to M=128.
783
+ # N=10240 (n_blocks=160) deliberately excluded to avoid regressions there.
784
+ if n_blocks * 5 >= num_sms * 4:
785
+ if tall_k_2xn or n_blocks >= 200:
786
+ return M <= 128
787
+ # Square/tall-K: >=60 SMs wins to M=128; <60 SMs default wins earlier.
788
+ if K >= N:
789
+ return M <= (128 if num_sms >= 60 else 32)
790
+ return M <= 64
791
+ if n_blocks >= num_sms // 2:
792
+ if tall_k_2xn:
793
+ return M <= 64
794
+ if n_blocks >= 64:
795
+ return M <= 8
796
+ return M <= 32
797
+ # Tall-K (K>N) at narrow N (n_blocks<=48): M-driven crossover.
798
+ # K>=3N (e.g. N=2560,K=10240): SIMT wins to M=12. Moderate K>N: M=10.
799
+ if K > N and n_blocks <= 48:
800
+ return M <= (12 if K >= N * 3 else 10)
801
+ return M <= (16 if (tall_k_2xn or n_blocks < 48) else 8)
802
+
803
+
804
+ @functools.cache
805
+ def _gemm_4bit_use_custom_rocm(device_index, dtype, M, N, K):
806
+ """
807
+ Fused SIMT kernel vs dequant+F.linear heuristic for ROCm.
808
+
809
+ RDNA3/RDNA4 calibration keeps the SIMT kernel through ~M=8.
810
+ CDNA/gfx9 is calibrated on MI308X (gfx942): bf16/fp16 win through M<=4
811
+ after the SIMT math-path tuning, while fp32 only has a broad win through M<=2.
812
+
813
+ TODO: revisit once WMMA/MFMA kernels land.
814
+ """
815
+ if M <= _GEMM_4BIT_CUSTOM_FLOOR_M and dtype != torch.float32:
816
+ return True
817
+
818
+ arch = _rocm_gfx_arch(device_index)
819
+ if arch.startswith("gfx11") or arch.startswith("gfx12"): # RDNA3 / RDNA4
820
+ return M <= 8
821
+ if arch.startswith("gfx9"): # CDNA / MI-series
822
+ return M <= (2 if dtype == torch.float32 else 4)
823
+ return M <= 4 # unknown ROCm arch: conservative tiny-batch floor
824
+
825
+
826
+ @functools.cache
827
+ def _rocm_gfx_arch(device_index):
828
+ """gfx arch string (e.g. 'gfx1100') for a ROCm device, feature flags stripped."""
829
+ name = getattr(torch.cuda.get_device_properties(device_index), "gcnArchName", "") or ""
830
+ return name.split(":")[0]
831
+
832
+
833
+ def _gemm_4bit_kernel_impl(
834
+ A, B, shapeB, absmax, blocksize, quant_type, bias=None, absmax_8bit=None, absmax_code=None, absmax_offset=None
835
+ ):
836
+ """Invoke the fused cgemm_4bit_* kernel (shared by the CUDA and ROCm dispatch; the
837
+ C dispatch in gemm_4bit.cu picks SIMT vs MMA per arch/shape). A is made contiguous
838
+ because the kernel reads it as row-major (stride K)."""
839
+ K = A.shape[-1]
840
+ M = A.numel() // K
841
+ N = shapeB[0]
842
+
843
+ if K != shapeB[1]:
844
+ raise RuntimeError(f"A inner dim ({K}) does not match weight ({shapeB[1]})")
845
+ if absmax.dtype != torch.float32:
846
+ raise RuntimeError(f"absmax must be float32, got {absmax.dtype}")
847
+ if bias is not None:
848
+ if bias.ndim != 1:
849
+ raise RuntimeError(f"bias must be 1D, got {bias.ndim}D")
850
+ if bias.dtype != A.dtype:
851
+ raise RuntimeError(f"bias dtype ({bias.dtype}) must match A dtype ({A.dtype})")
852
+
853
+ A = A.contiguous()
854
+ quant_type_int = 1 if quant_type == "fp4" else 2
855
+ out = torch.empty((*A.shape[:-1], N), dtype=A.dtype, device=A.device)
856
+ stream = _get_raw_stream(A.device.index)
857
+
858
+ if A.dtype == torch.bfloat16:
859
+ fn = lib.cgemm_4bit_bf16
860
+ elif A.dtype == torch.float16:
861
+ fn = lib.cgemm_4bit_fp16
862
+ elif A.dtype == torch.float32:
863
+ fn = lib.cgemm_4bit_fp32
864
+ else:
865
+ raise RuntimeError(f"unsupported dtype {A.dtype}")
866
+
867
+ # Offset is expected to be a float32 tensor.
868
+ absmax_offset_f32 = absmax_offset.to(dtype=torch.float32) if absmax_offset is not None else None
869
+
870
+ with _cuda_device_of(A):
871
+ fn(
872
+ A.data_ptr(),
873
+ B.data_ptr(),
874
+ absmax.data_ptr(),
875
+ absmax_8bit.data_ptr() if absmax_8bit is not None else None,
876
+ absmax_code.data_ptr() if absmax_code is not None else None,
877
+ absmax_offset_f32.data_ptr() if absmax_offset_f32 is not None else None,
878
+ out.data_ptr(),
879
+ bias.data_ptr() if bias is not None else None,
880
+ M,
881
+ N,
882
+ K,
883
+ blocksize,
884
+ quant_type_int,
885
+ stream,
886
+ )
887
+
888
+ return out
889
+
890
+
891
+ def _dequant_linear_fallback(
892
+ A, B, shapeB, absmax, blocksize, quant_type, bias=None, absmax_8bit=None, absmax_code=None, absmax_offset=None
893
+ ):
894
+ """Unfused fallback shared by CUDA and ROCm: reconstruct the (optionally nested)
895
+ absmax, dequantize the 4-bit weight via the backend dequant impls (reusing
896
+ preallocated buffers), then F.linear."""
897
+ if absmax_8bit is not None:
898
+ absmax_dq = torch.empty_like(absmax_8bit, dtype=torch.float32)
899
+ _dequantize_blockwise_impl(absmax_8bit, absmax, absmax_code, 256, torch.float32, out=absmax_dq)
900
+ absmax = absmax_dq + absmax_offset
901
+ B_dq = torch.empty(shapeB, dtype=A.dtype, device=A.device)
902
+ _dequantize_4bit_impl(B, absmax, blocksize, quant_type, A.dtype, out=B_dq)
903
+ return torch.nn.functional.linear(A, B_dq, bias)
904
+
905
+
906
+ # Unified CUDA/ROCm dispatch for bitsandbytes::gemm_4bit. The choice *among* custom
907
+ # kernels (CUDA SIMT vs MMA; ROCm SIMT) is made in the C dispatch (csrc/gemm_4bit.cu).
908
+ _GEMM_4BIT_CUSTOM_FLOOR_M = 4
909
+ if torch.version.hip is None:
910
+ _gemm_4bit_use_custom_fn = _gemm_4bit_use_custom_cuda
911
+ # CUDA: dequant+F.linear wins past M=1536 (dequant savings negligible at very
912
+ # large batch).
913
+ _gemm_4bit_custom_max_m = 1536
914
+ else:
915
+ _gemm_4bit_use_custom_fn = _gemm_4bit_use_custom_rocm
916
+ # ROCm: the custom path is SIMT-only today; the per-arch heuristic above owns
917
+ # RDNA/CDNA thresholds. Keep a hard upper cap while WMMA/MFMA paths are absent.
918
+ _gemm_4bit_custom_max_m = 256
919
+
920
+
921
+ @register_kernel("bitsandbytes::gemm_4bit", "cuda")
922
+ def _(
923
+ A: torch.Tensor,
924
+ B: torch.Tensor,
925
+ shapeB: Sequence[int],
926
+ absmax: torch.Tensor,
927
+ blocksize: int,
928
+ quant_type: str,
929
+ bias: Optional[torch.Tensor] = None,
930
+ absmax_8bit: Optional[torch.Tensor] = None,
931
+ absmax_code: Optional[torch.Tensor] = None,
932
+ absmax_offset: Optional[torch.Tensor] = None,
933
+ ) -> torch.Tensor:
934
+ K = A.shape[-1]
935
+ M = A.numel() // K
936
+ N = shapeB[0]
937
+
938
+ # The backend-specific heuristic owns tiny-M floors and per-arch thresholds.
939
+ # Past custom_max_m (or for blocksize-misaligned K), use the dequant+F.linear
940
+ # fallback.
941
+ if M > _gemm_4bit_custom_max_m:
942
+ use_custom = False
943
+ elif K % blocksize != 0:
944
+ warn(
945
+ f"inner dimension ({K}) is not aligned for fast kernel "
946
+ f"with blocksize={blocksize}, falling back to slower implementation.",
947
+ UserWarning,
948
+ )
949
+ use_custom = False
950
+ else:
951
+ use_custom = _gemm_4bit_use_custom_fn(A.device.index, A.dtype, M, N, K)
952
+
953
+ if not use_custom:
954
+ return _dequant_linear_fallback(
955
+ A,
956
+ B,
957
+ shapeB,
958
+ absmax,
959
+ blocksize,
960
+ quant_type,
961
+ bias,
962
+ absmax_8bit=absmax_8bit,
963
+ absmax_code=absmax_code,
964
+ absmax_offset=absmax_offset,
965
+ )
966
+
967
+ return _gemm_4bit_kernel_impl(
968
+ A, B, shapeB, absmax, blocksize, quant_type, bias, absmax_8bit, absmax_code, absmax_offset
969
+ )
970
+
971
+
972
+ """C FUNCTIONS FOR OPTIMIZERS"""
973
+ str2optimizer32bit = {
974
+ "adam": (
975
+ lib.cadam32bit_grad_fp32,
976
+ lib.cadam32bit_grad_fp16,
977
+ lib.cadam32bit_grad_bf16,
978
+ ),
979
+ "momentum": (
980
+ lib.cmomentum32bit_grad_32,
981
+ lib.cmomentum32bit_grad_16,
982
+ ),
983
+ "rmsprop": (
984
+ lib.crmsprop32bit_grad_32,
985
+ lib.crmsprop32bit_grad_16,
986
+ ),
987
+ "lion": (
988
+ lib.clion32bit_grad_fp32,
989
+ lib.clion32bit_grad_fp16,
990
+ lib.clion32bit_grad_bf16,
991
+ ),
992
+ "adagrad": (
993
+ lib.cadagrad32bit_grad_32,
994
+ lib.cadagrad32bit_grad_16,
995
+ ),
996
+ "lamb": (
997
+ lib.cadam32bit_grad_fp32,
998
+ lib.cadam32bit_grad_fp16,
999
+ lib.cadam32bit_grad_bf16,
1000
+ ),
1001
+ "ademamix": (
1002
+ lib.cademamix32bit_grad_fp32,
1003
+ lib.cademamix32bit_grad_fp16,
1004
+ lib.cademamix32bit_grad_bf16,
1005
+ ),
1006
+ "lars": (
1007
+ lib.cmomentum32bit_grad_32,
1008
+ lib.cmomentum32bit_grad_16,
1009
+ ),
1010
+ }
1011
+
1012
+ str2optimizer8bit_blockwise = {
1013
+ "adam": (
1014
+ lib.cadam_8bit_blockwise_grad_fp32,
1015
+ lib.cadam_8bit_blockwise_grad_fp16,
1016
+ lib.cadam_8bit_blockwise_grad_bf16,
1017
+ ),
1018
+ "momentum": (
1019
+ lib.cmomentum_8bit_blockwise_grad_fp32,
1020
+ lib.cmomentum_8bit_blockwise_grad_fp16,
1021
+ lib.cmomentum_8bit_blockwise_grad_bf16,
1022
+ ),
1023
+ "rmsprop": (
1024
+ lib.crmsprop_8bit_blockwise_grad_fp32,
1025
+ lib.crmsprop_8bit_blockwise_grad_fp16,
1026
+ lib.crmsprop_8bit_blockwise_grad_bf16,
1027
+ ),
1028
+ "lion": (
1029
+ lib.clion_8bit_blockwise_grad_fp32,
1030
+ lib.clion_8bit_blockwise_grad_fp16,
1031
+ lib.clion_8bit_blockwise_grad_bf16,
1032
+ ),
1033
+ "adagrad": (
1034
+ lib.cadagrad_8bit_blockwise_grad_fp32,
1035
+ lib.cadagrad_8bit_blockwise_grad_fp16,
1036
+ lib.cadagrad_8bit_blockwise_grad_bf16,
1037
+ ),
1038
+ "ademamix": (
1039
+ lib.cademamix_8bit_blockwise_grad_fp32,
1040
+ lib.cademamix_8bit_blockwise_grad_fp16,
1041
+ lib.cademamix_8bit_blockwise_grad_bf16,
1042
+ ),
1043
+ }
1044
+
1045
+
1046
+ def _optimizer_update_32bit_impl(
1047
+ optimizer_name: str,
1048
+ g: torch.Tensor,
1049
+ p: torch.Tensor,
1050
+ state1: torch.Tensor,
1051
+ state2: Optional[torch.Tensor],
1052
+ unorm_vec: Optional[torch.Tensor],
1053
+ max_unorm: float,
1054
+ param_norm: float,
1055
+ beta1: float,
1056
+ beta2: float,
1057
+ beta3: float,
1058
+ alpha: float,
1059
+ eps: float,
1060
+ weight_decay: float,
1061
+ step: int,
1062
+ lr: float,
1063
+ gnorm_scale: float,
1064
+ skip_zeros=False,
1065
+ ) -> None:
1066
+ optim_fns = str2optimizer32bit.get(optimizer_name, None)
1067
+ if optim_fns is None:
1068
+ raise ValueError(
1069
+ f"Unsupported optimizer name: {optimizer_name}. Supported optimizers: {list(str2optimizer32bit.keys())}"
1070
+ )
1071
+ if g.dtype == torch.float32:
1072
+ optim_func = optim_fns[0]
1073
+ elif g.dtype == torch.float16:
1074
+ optim_func = optim_fns[1]
1075
+ elif g.dtype == torch.bfloat16 and len(optim_fns) == 3:
1076
+ optim_func = optim_fns[2]
1077
+ else:
1078
+ raise ValueError(
1079
+ f"Gradient+optimizer bit data type combination not supported: grad {g.dtype}, optimizer {state1.dtype}",
1080
+ )
1081
+
1082
+ with _cuda_device_of(g):
1083
+ optim_func(
1084
+ get_ptr(g),
1085
+ get_ptr(p),
1086
+ get_ptr(state1),
1087
+ get_ptr(state2),
1088
+ get_ptr(unorm_vec),
1089
+ ct.c_float(max_unorm),
1090
+ ct.c_float(param_norm),
1091
+ ct.c_float(beta1),
1092
+ ct.c_float(beta2),
1093
+ ct.c_float(beta3),
1094
+ ct.c_float(alpha),
1095
+ ct.c_float(eps),
1096
+ ct.c_float(weight_decay),
1097
+ ct.c_int32(step),
1098
+ ct.c_float(lr),
1099
+ ct.c_float(gnorm_scale),
1100
+ ct.c_bool(skip_zeros),
1101
+ ct.c_int32(g.numel()),
1102
+ )
1103
+
1104
+
1105
+ def _optimizer_update_8bit_blockwise_impl(
1106
+ optimizer_name: str,
1107
+ g: torch.Tensor,
1108
+ p: torch.Tensor,
1109
+ state1: torch.Tensor,
1110
+ state2: Optional[torch.Tensor],
1111
+ beta1: float,
1112
+ beta2: float,
1113
+ beta3: float,
1114
+ alpha: float,
1115
+ eps: float,
1116
+ step: int,
1117
+ lr: float,
1118
+ qmap1: torch.Tensor,
1119
+ qmap2: Optional[torch.Tensor],
1120
+ absmax1: torch.Tensor,
1121
+ absmax2: Optional[torch.Tensor],
1122
+ weight_decay: float,
1123
+ gnorm_scale: float,
1124
+ skip_zeros=False,
1125
+ ) -> None:
1126
+ # torch._check(
1127
+ # g.numel() == p.numel(),
1128
+ # lambda: f"g and p must have the same number of elements, got {g.numel()} and {p.numel()}",
1129
+ # )
1130
+ # compute_dtypes = [torch.float16, torch.bfloat16, torch.float32]
1131
+
1132
+ # torch._check(
1133
+ # g.dtype in compute_dtypes,
1134
+ # lambda: f"g must be bfloat16, float16, or float32, got {g.dtype}",
1135
+ # )
1136
+ # torch._check(
1137
+ # g.dtype == p.dtype,
1138
+ # lambda: f"Expected all tensors to have the same dtype, got g.dtype={g.dtype}, p.dtype={p.dtype}",
1139
+ # )
1140
+ # torch._check(
1141
+ # state1.dtype == torch.uint8,
1142
+ # lambda: f"state1 must be uint8, got {state1.dtype}",
1143
+ # )
1144
+ # torch._check(
1145
+ # qmap1.dtype == absmax1.dtype == torch.float32,
1146
+ # lambda: f"Expected qmap1 and absmax1 to be float32, got qmap1.dtype={qmap1.dtype}, absmax1.dtype={absmax1.dtype}",
1147
+ # )
1148
+ # if state2 is not None:
1149
+ # torch._check(
1150
+ # state2.dtype == torch.uint8,
1151
+ # lambda: f"state2 must be uint8, got {state2.dtype}",
1152
+ # )
1153
+ # torch._check(
1154
+ # qmap2.dtype == absmax2.dtype == torch.float32,
1155
+ # lambda: f"Expected qmap2 and absmax2 to be float32, got qmap2.dtype={qmap2.dtype}, absmax2.dtype={absmax2.dtype}",
1156
+ # )
1157
+ optimizer_fns = str2optimizer8bit_blockwise.get(optimizer_name)
1158
+ if optimizer_fns is None:
1159
+ raise ValueError(
1160
+ f"Unsupported optimizer name: {optimizer_name}. Supported optimizers: {list(str2optimizer8bit_blockwise.keys())}"
1161
+ )
1162
+
1163
+ if g.dtype == torch.float32:
1164
+ optimizer_fn = optimizer_fns[0]
1165
+ elif g.dtype == torch.float16:
1166
+ optimizer_fn = optimizer_fns[1]
1167
+ elif g.dtype == torch.bfloat16:
1168
+ optimizer_fn = optimizer_fns[2]
1169
+ else:
1170
+ raise ValueError(
1171
+ f"Unsupported gradient dtype: {g.dtype}. Supported dtypes: torch.float32, torch.float16, torch.bfloat16"
1172
+ )
1173
+
1174
+ with _cuda_device_of(g):
1175
+ optimizer_fn(
1176
+ get_ptr(p),
1177
+ get_ptr(g),
1178
+ get_ptr(state1),
1179
+ get_ptr(state2),
1180
+ ct.c_float(beta1),
1181
+ ct.c_float(beta2),
1182
+ ct.c_float(beta3),
1183
+ ct.c_float(alpha),
1184
+ ct.c_float(eps),
1185
+ ct.c_int32(step),
1186
+ ct.c_float(lr),
1187
+ get_ptr(qmap1),
1188
+ get_ptr(qmap2),
1189
+ get_ptr(absmax1),
1190
+ get_ptr(absmax2),
1191
+ ct.c_float(weight_decay),
1192
+ ct.c_float(gnorm_scale),
1193
+ ct.c_bool(skip_zeros),
1194
+ ct.c_int32(g.numel()),
1195
+ )
1196
+
1197
+
1198
+ register_kernel("bitsandbytes::optimizer_update_8bit_blockwise", "cuda")(_optimizer_update_8bit_blockwise_impl)
1199
+ register_kernel("bitsandbytes::optimizer_update_32bit", "cuda")(_optimizer_update_32bit_impl)