bitsandbytes-cpu-fork 0.50.2.dev1__py3-none-win_amd64.whl → 0.50.2.dev2__py3-none-win_amd64.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 (51) hide show
  1. bitsandbytes/__init__.py +1 -1
  2. bitsandbytes/__main__.py +4 -4
  3. bitsandbytes/_ops.py +526 -526
  4. bitsandbytes/autograd/_functions.py +491 -491
  5. bitsandbytes/backends/cpu/ops.py +805 -805
  6. bitsandbytes/backends/cuda/ops.py +1212 -1212
  7. bitsandbytes/backends/default/ops.py +632 -632
  8. bitsandbytes/backends/hpu/ops.py +53 -53
  9. bitsandbytes/backends/mps/ops.py +277 -277
  10. bitsandbytes/backends/triton/kernels_4bit.py +577 -577
  11. bitsandbytes/backends/triton/kernels_8bit_quant.py +195 -195
  12. bitsandbytes/backends/triton/kernels_optim.py +1177 -1177
  13. bitsandbytes/backends/triton/ops.py +304 -304
  14. bitsandbytes/backends/utils.py +94 -94
  15. bitsandbytes/backends/xpu/ops.py +305 -305
  16. bitsandbytes/cextension.py +405 -405
  17. bitsandbytes/cli.py +50 -50
  18. bitsandbytes/consts.py +12 -12
  19. bitsandbytes/cuda_specs.py +111 -111
  20. bitsandbytes/detect_cpu.py +189 -189
  21. bitsandbytes/diagnostics/cuda.py +193 -193
  22. bitsandbytes/diagnostics/main.py +134 -134
  23. bitsandbytes/diagnostics/utils.py +12 -12
  24. bitsandbytes/functional.py +1836 -1836
  25. bitsandbytes/gdn_cpu.py +602 -602
  26. bitsandbytes/libbitsandbytes_cpu.dll +0 -0
  27. bitsandbytes/nn/__init__.py +19 -19
  28. bitsandbytes/nn/modules.py +1220 -1220
  29. bitsandbytes/nn/parametrize.py +206 -206
  30. bitsandbytes/optim/__init__.py +4 -0
  31. bitsandbytes/optim/adagrad.py +187 -187
  32. bitsandbytes/optim/adam.py +346 -346
  33. bitsandbytes/optim/adamw.py +337 -337
  34. bitsandbytes/optim/adamw4bit.py +183 -0
  35. bitsandbytes/optim/ademamix.py +410 -410
  36. bitsandbytes/optim/lamb.py +190 -190
  37. bitsandbytes/optim/lars.py +259 -259
  38. bitsandbytes/optim/lion.py +266 -266
  39. bitsandbytes/optim/optimizer.py +756 -756
  40. bitsandbytes/optim/rmsprop.py +170 -170
  41. bitsandbytes/optim/sgd.py +152 -152
  42. bitsandbytes/torch_cpu_kit.py +468 -468
  43. bitsandbytes/utils.py +208 -208
  44. bitsandbytes_cpu_cli.py +949 -949
  45. {bitsandbytes_cpu_fork-0.50.2.dev1.dist-info → bitsandbytes_cpu_fork-0.50.2.dev2.dist-info}/METADATA +1 -1
  46. bitsandbytes_cpu_fork-0.50.2.dev2.dist-info/RECORD +63 -0
  47. {bitsandbytes_cpu_fork-0.50.2.dev1.dist-info → bitsandbytes_cpu_fork-0.50.2.dev2.dist-info}/licenses/LICENSE +21 -21
  48. bitsandbytes_cpu_fork-0.50.2.dev1.dist-info/RECORD +0 -62
  49. {bitsandbytes_cpu_fork-0.50.2.dev1.dist-info → bitsandbytes_cpu_fork-0.50.2.dev2.dist-info}/WHEEL +0 -0
  50. {bitsandbytes_cpu_fork-0.50.2.dev1.dist-info → bitsandbytes_cpu_fork-0.50.2.dev2.dist-info}/entry_points.txt +0 -0
  51. {bitsandbytes_cpu_fork-0.50.2.dev1.dist-info → bitsandbytes_cpu_fork-0.50.2.dev2.dist-info}/top_level.txt +0 -0
bitsandbytes/_ops.py CHANGED
@@ -1,526 +1,526 @@
1
- from collections.abc import Sequence
2
- from typing import Optional
3
-
4
- import torch
5
-
6
- register_fake = torch.library.register_fake
7
- register_kernel = torch.library.register_kernel
8
-
9
- # Int8 mixed precision matmul + dequant + bias
10
- torch.library.define(
11
- "bitsandbytes::int8_mixed_scaled_mm",
12
- "(Tensor A, Tensor CA, Tensor CB, Tensor SCA, Tensor SCB, Tensor? outlier_cols=None, Tensor? bias=None) -> (Tensor, Tensor?)",
13
- )
14
-
15
-
16
- @register_fake("bitsandbytes::int8_mixed_scaled_mm")
17
- def _(
18
- A: torch.Tensor,
19
- CA: torch.Tensor,
20
- CB: torch.Tensor,
21
- SCA: torch.Tensor,
22
- SCB: torch.Tensor,
23
- outlier_cols: Optional[torch.Tensor] = None,
24
- bias: Optional[torch.Tensor] = None,
25
- ) -> tuple[torch.Tensor, Optional[torch.Tensor]]:
26
- shapeC = (*CA.shape[:-1], CB.shape[0])
27
-
28
- out = torch.empty(shapeC, device=A.device, dtype=A.dtype)
29
-
30
- outlier_cols = torch.library.get_ctx().new_dynamic_size()
31
- subA = A.new_empty(outlier_cols, dtype=torch.int64)
32
-
33
- return out, subA
34
-
35
-
36
- # Higher level op: int8 matmul + dequant + bias
37
- torch.library.define(
38
- "bitsandbytes::int8_scaled_mm",
39
- "(Tensor A, Tensor B, Tensor row_stats, Tensor col_stats, Tensor? bias=None, ScalarType? dtype=None) -> Tensor",
40
- )
41
-
42
-
43
- @register_fake("bitsandbytes::int8_scaled_mm")
44
- def _(
45
- A: torch.Tensor,
46
- B: torch.Tensor,
47
- row_stats: torch.Tensor,
48
- col_stats: torch.Tensor,
49
- bias: Optional[torch.Tensor] = None,
50
- dtype: Optional[torch.dtype] = None,
51
- ) -> torch.Tensor:
52
- shapeC = (*A.shape[:-1], B.shape[0])
53
- return torch.empty(shapeC, device=A.device, dtype=dtype or torch.float16)
54
-
55
-
56
- torch.library.define(
57
- "bitsandbytes::int8_linear_matmul",
58
- "(Tensor A, Tensor B) -> Tensor",
59
- )
60
-
61
-
62
- @register_fake("bitsandbytes::int8_linear_matmul")
63
- def _(A: torch.Tensor, B: torch.Tensor):
64
- torch._check(A.dtype == torch.int8, lambda: "A must be int8")
65
- torch._check(B.dtype == torch.int8, lambda: "B must be int8")
66
- shapeC = (*A.shape[:-1], B.shape[0])
67
- return torch.empty(shapeC, device=A.device, dtype=torch.int32)
68
-
69
-
70
- # More info on `out` overloads:
71
- # https://github.com/pytorch/pytorch/issues/125044
72
- torch.library.define(
73
- "bitsandbytes::int8_linear_matmul.out",
74
- "(Tensor A, Tensor B, Tensor! out) -> ()",
75
- )
76
-
77
-
78
- @register_fake("bitsandbytes::int8_linear_matmul.out")
79
- def _(A: torch.Tensor, B: torch.Tensor, out: torch.Tensor):
80
- shapeC = (*A.shape[:-1], B.shape[0])
81
-
82
- torch._check(A.dtype == torch.int8, lambda: "A must be int8")
83
- torch._check(B.dtype == torch.int8, lambda: "B must be int8")
84
- torch._check(out.shape == shapeC, lambda: f"Expected out.shape == {shapeC}, got {out.shape}")
85
- torch._check(out.device == A.device, lambda: f"Expected out.device == {A.device}, got {out.device}")
86
- torch._check(out.dtype == torch.int32, lambda: f"Expected out.dtype == int32, got {out.dtype}")
87
-
88
-
89
- torch.library.define(
90
- "bitsandbytes::int8_vectorwise_quant",
91
- "(Tensor A, float threshold=0.0) -> (Tensor, Tensor, Tensor?)",
92
- )
93
-
94
-
95
- @register_fake("bitsandbytes::int8_vectorwise_quant")
96
- def _(A: torch.Tensor, threshold=0.0):
97
- out_row = torch.empty(A.shape, device=A.device, dtype=torch.int8)
98
- row_stats = torch.empty(A.numel() // A.shape[-1], device=A.device, dtype=torch.float32)
99
-
100
- if threshold == 0.0:
101
- return out_row, row_stats, None
102
-
103
- outlier_cols = torch.library.get_ctx().new_dynamic_size()
104
-
105
- return out_row, row_stats, A.new_empty(outlier_cols, dtype=torch.int64)
106
-
107
-
108
- torch.library.define("bitsandbytes::int8_vectorwise_dequant", "(Tensor A, Tensor stats) -> Tensor")
109
-
110
-
111
- @register_fake("bitsandbytes::int8_vectorwise_dequant")
112
- def _(A: torch.Tensor, stats: torch.Tensor) -> torch.Tensor:
113
- torch._check(A.dtype == torch.int8, lambda: "A must be int8")
114
- return torch.empty_like(A, dtype=torch.float32)
115
-
116
-
117
- # Default PyTorch-native implementation
118
- @register_kernel("bitsandbytes::int8_vectorwise_dequant", "default")
119
- def _(A: torch.Tensor, stats: torch.Tensor):
120
- # To dequantize we divide by 127, or multiply by the reciprocal.
121
- return A * stats.view(-1, 1) * 7.874015718698502e-3
122
-
123
-
124
- torch.library.define(
125
- "bitsandbytes::int8_mm_dequant",
126
- "(Tensor A, Tensor row_stats, Tensor col_stats, ScalarType? dtype=None, Tensor? bias=None) -> Tensor",
127
- )
128
-
129
-
130
- @register_fake("bitsandbytes::int8_mm_dequant")
131
- def _(
132
- A: torch.Tensor,
133
- row_stats: torch.Tensor,
134
- col_stats: torch.Tensor,
135
- dtype: Optional[torch.dtype] = None,
136
- bias: Optional[torch.Tensor] = None,
137
- ) -> torch.Tensor:
138
- torch._check(A.dtype == torch.int32, lambda: "A must be int32")
139
- return torch.empty_like(A, dtype=dtype or torch.float16)
140
-
141
-
142
- torch.library.define(
143
- "bitsandbytes::int8_double_quant",
144
- "(Tensor A, float threshold=0.0) -> (Tensor, Tensor, Tensor, Tensor, Tensor?)",
145
- )
146
-
147
-
148
- @register_fake("bitsandbytes::int8_double_quant")
149
- def _(
150
- A: torch.Tensor,
151
- threshold=0.0,
152
- ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor, Optional[torch.Tensor]]:
153
- out_row = torch.empty_like(A, dtype=torch.int8)
154
- out_col = torch.empty_like(A, dtype=torch.int8)
155
- row_stats = torch.empty(A.numel() // A.shape[-1], device=A.device, dtype=torch.float32)
156
- col_stats = torch.empty(A.shape[-1], device=A.device, dtype=torch.float32)
157
- outlier_n = torch.library.get_ctx().new_dynamic_size()
158
- outlier_cols = A.new_empty(outlier_n, dtype=torch.int64)
159
- return out_row, out_col, row_stats, col_stats, outlier_cols
160
-
161
-
162
- torch.library.define(
163
- "bitsandbytes::dequantize_4bit",
164
- "(Tensor A, Tensor absmax, int blocksize, str quant_type, int[] shape, ScalarType dtype) -> Tensor",
165
- )
166
-
167
-
168
- @register_fake("bitsandbytes::dequantize_4bit")
169
- def _(
170
- A: torch.Tensor,
171
- absmax: torch.Tensor,
172
- blocksize: int,
173
- quant_type: str,
174
- shape: Sequence[int],
175
- dtype: torch.dtype,
176
- ) -> torch.Tensor:
177
- torch._check(blocksize in (32, 64, 128, 256, 512, 1024, 2048, 4096), lambda: f"invalid blocksize {blocksize}")
178
- torch._check(quant_type in ("nf4", "fp4"), lambda: f"quant_type must be 'nf4' or 'fp4', got {quant_type!r}")
179
- torch._check(absmax.dtype == torch.float32, lambda: f"absmax must be float32, got {absmax.dtype}")
180
- torch._check(
181
- dtype in (torch.float16, torch.bfloat16, torch.float32),
182
- lambda: f"Blockwise 4bit dequantization only supports 16/32-bit floats, but got {dtype}",
183
- )
184
- return torch.empty(shape, dtype=dtype, device=A.device)
185
-
186
-
187
- torch.library.define(
188
- "bitsandbytes::dequantize_4bit.out",
189
- "(Tensor A, Tensor absmax, int blocksize, str quant_type, int[] shape, ScalarType dtype, Tensor! out) -> ()",
190
- )
191
-
192
-
193
- @register_fake("bitsandbytes::dequantize_4bit.out")
194
- def _(
195
- A: torch.Tensor,
196
- absmax: torch.Tensor,
197
- blocksize: int,
198
- quant_type: str,
199
- shape: Sequence[int],
200
- dtype: torch.dtype,
201
- out: torch.Tensor,
202
- ) -> None:
203
- torch._check(blocksize in (32, 64, 128, 256, 512, 1024, 2048, 4096), lambda: f"invalid blocksize {blocksize}")
204
- torch._check(quant_type in ("nf4", "fp4"), lambda: f"quant_type must be 'nf4' or 'fp4', got {quant_type!r}")
205
- torch._check(absmax.dtype == torch.float32, lambda: f"absmax must be float32, got {absmax.dtype}")
206
- torch._check(
207
- dtype in (torch.float16, torch.bfloat16, torch.float32),
208
- lambda: f"Blockwise 4bit dequantization only supports 16/32-bit floats, but got {dtype}",
209
- )
210
- torch._check(out.shape == shape, lambda: f"Expected out.shape == {shape}, got {out.shape}")
211
- torch._check(out.device == A.device, lambda: f"Expected out.device == {A.device}, got {out.device}")
212
- torch._check(out.dtype == dtype, lambda: f"Expected out.dtype == {dtype}, got {out.dtype}")
213
-
214
-
215
- torch.library.define(
216
- "bitsandbytes::quantize_4bit",
217
- "(Tensor A, int blocksize, str quant_type, ScalarType quant_storage) -> (Tensor, Tensor)",
218
- )
219
-
220
-
221
- @register_fake("bitsandbytes::quantize_4bit")
222
- def _(
223
- A: torch.Tensor, blocksize: int, quant_type: str, quant_storage: torch.dtype
224
- ) -> tuple[torch.Tensor, torch.Tensor]:
225
- torch._check(blocksize in (32, 64, 128, 256, 512, 1024, 2048, 4096), lambda: f"invalid blocksize {blocksize}")
226
- torch._check(quant_type in ("nf4", "fp4"), lambda: f"quant_type must be 'nf4' or 'fp4', got {quant_type!r}")
227
- torch._check(
228
- A.dtype in (torch.float16, torch.bfloat16, torch.float32),
229
- lambda: f"Blockwise 4bit quantization only supports 16/32-bit floats, but got {A.dtype}",
230
- )
231
-
232
- n = A.numel()
233
- blocks = -(n // -blocksize)
234
- absmax = torch.empty((blocks,), device=A.device, dtype=torch.float32)
235
- out = torch.empty(((n + 1) // (quant_storage.itemsize * 2), 1), device=A.device, dtype=quant_storage)
236
- return out, absmax
237
-
238
-
239
- torch.library.define(
240
- "bitsandbytes::gemm_4bit",
241
- "(Tensor A, Tensor B, int[] shapeB, Tensor absmax, int blocksize, str quant_type, "
242
- "Tensor? bias=None, Tensor? absmax_8bit=None, Tensor? absmax_code=None, Tensor? absmax_offset=None) -> Tensor",
243
- )
244
-
245
-
246
- @register_fake("bitsandbytes::gemm_4bit")
247
- def _(
248
- A: torch.Tensor,
249
- B: torch.Tensor,
250
- shapeB: Sequence[int],
251
- absmax: torch.Tensor,
252
- blocksize: int,
253
- quant_type: str,
254
- bias: Optional[torch.Tensor] = None,
255
- absmax_8bit: Optional[torch.Tensor] = None,
256
- absmax_code: Optional[torch.Tensor] = None,
257
- absmax_offset: Optional[torch.Tensor] = None,
258
- ) -> torch.Tensor:
259
- torch._check(len(shapeB) == 2, lambda: f"shapeB must be 2D [N, K], got {list(shapeB)}")
260
- torch._check(A.shape[-1] == shapeB[1], lambda: f"A inner dim ({A.shape[-1]}) must match shapeB ({shapeB[1]})")
261
- torch._check(
262
- A.dtype in (torch.float16, torch.bfloat16, torch.float32),
263
- lambda: f"A must be float16, bfloat16, or float32, got {A.dtype}",
264
- )
265
- torch._check(
266
- B.dtype in (torch.uint8, torch.bfloat16, torch.float16, torch.float32),
267
- lambda: f"B must be backed by storage of type uint8, bfloat16, float16, or float32, got {B.dtype}",
268
- )
269
- torch._check(blocksize in (32, 64, 128, 256, 512, 1024, 2048, 4096), lambda: f"invalid blocksize {blocksize}")
270
- torch._check(quant_type in ("nf4", "fp4"), lambda: f"quant_type must be 'nf4' or 'fp4', got {quant_type!r}")
271
- torch._check(absmax.dtype == torch.float32, lambda: f"absmax must be float32, got {absmax.dtype}")
272
- if absmax_8bit is not None:
273
- torch._check(absmax_8bit.ndim == 1, lambda: f"absmax_8bit must be 1D, got {absmax_8bit.ndim}D")
274
- torch._check(absmax_8bit.dtype == torch.uint8, lambda: f"absmax_8bit must be uint8, got {absmax_8bit.dtype}")
275
- torch._check(absmax_code is not None, lambda: "absmax_code required when absmax_8bit is provided")
276
- torch._check(absmax_code.ndim == 1, lambda: f"absmax_code must be 1D, got {absmax_code.ndim}D")
277
- torch._check(
278
- absmax_code.shape[0] == 256, lambda: f"absmax_code must have 256 entries, got {absmax_code.shape[0]}"
279
- )
280
- torch._check(
281
- absmax_code.dtype == torch.float32, lambda: f"absmax_code must be float32, got {absmax_code.dtype}"
282
- )
283
- torch._check(absmax_offset is not None, lambda: "absmax_offset required when absmax_8bit is provided")
284
- torch._check(
285
- absmax_offset.ndim == 0, lambda: f"absmax_offset must be a scalar (0-dim), got {absmax_offset.ndim}D"
286
- )
287
- torch._check(
288
- absmax_offset.dtype == torch.float32, lambda: f"absmax_offset must be float32, got {absmax_offset.dtype}"
289
- )
290
- if bias is not None:
291
- torch._check(bias.ndim == 1, lambda: f"bias must be 1D, got {bias.ndim}D")
292
- torch._check(bias.shape[0] == shapeB[0], lambda: f"bias length ({bias.shape[0]}) must match N ({shapeB[0]})")
293
- torch._check(bias.dtype == A.dtype, lambda: f"bias dtype ({bias.dtype}) must match A dtype ({A.dtype})")
294
- N = shapeB[0]
295
- return torch.empty((*A.shape[:-1], N), dtype=A.dtype, device=A.device)
296
-
297
-
298
- torch.library.define(
299
- "bitsandbytes::dequantize_blockwise",
300
- "(Tensor A, Tensor absmax, Tensor code, int blocksize, ScalarType dtype) -> Tensor",
301
- )
302
-
303
-
304
- @register_fake("bitsandbytes::dequantize_blockwise")
305
- def _(A: torch.Tensor, absmax: torch.Tensor, code: torch.Tensor, blocksize: int, dtype: torch.dtype) -> torch.Tensor:
306
- torch._check(blocksize > 0, lambda: f"blocksize must be positive, got {blocksize}")
307
- torch._check(A.dtype == torch.uint8, lambda: f"A must be uint8, got {A.dtype}")
308
- torch._check(
309
- dtype in (torch.float16, torch.bfloat16, torch.float32),
310
- lambda: f"Blockwise dequantization only supports 16/32-bit floats, but got {dtype}",
311
- )
312
- return torch.empty_like(A, dtype=dtype)
313
-
314
-
315
- torch.library.define(
316
- "bitsandbytes::dequantize_blockwise.out",
317
- "(Tensor A, Tensor absmax, Tensor code, int blocksize, ScalarType dtype, Tensor! out) -> ()",
318
- )
319
-
320
-
321
- @register_fake("bitsandbytes::dequantize_blockwise.out")
322
- def _(
323
- A: torch.Tensor, absmax: torch.Tensor, code: torch.Tensor, blocksize: int, dtype: torch.dtype, out: torch.Tensor
324
- ):
325
- torch._check(blocksize > 0, lambda: f"blocksize must be positive, got {blocksize}")
326
- torch._check(A.dtype == torch.uint8, lambda: f"A must be uint8, got {A.dtype}")
327
- torch._check(
328
- dtype in (torch.float16, torch.bfloat16, torch.float32),
329
- lambda: f"Blockwise dequantization only supports 16/32-bit floats, but got {dtype}",
330
- )
331
- torch._check(out.shape == A.shape, lambda: f"Expected out.shape == {A.shape}, got {out.shape}")
332
- torch._check(out.device == A.device, lambda: f"Expected out.device == {A.device}, got {out.device}")
333
- torch._check(out.dtype == dtype, lambda: f"Expected out.dtype == {dtype}, got {out.dtype}")
334
-
335
-
336
- torch.library.define("bitsandbytes::quantize_blockwise", "(Tensor A, Tensor code, int blocksize) -> (Tensor, Tensor)")
337
-
338
-
339
- @register_fake("bitsandbytes::quantize_blockwise")
340
- def _(A: torch.Tensor, code: torch.Tensor, blocksize: int) -> tuple[torch.Tensor, torch.Tensor]:
341
- torch._check(blocksize > 0, lambda: f"blocksize must be positive, got {blocksize}")
342
- torch._check(
343
- A.dtype in (torch.float16, torch.bfloat16, torch.float32),
344
- lambda: f"Blockwise quantization only supports 16/32-bit floats, but got {A.dtype}",
345
- )
346
- n = A.numel()
347
- blocks = -(n // -blocksize)
348
- absmax = torch.empty((blocks,), device=A.device, dtype=torch.float32)
349
- out = torch.empty_like(A, dtype=torch.uint8)
350
- return out, absmax
351
-
352
-
353
- torch.library.define(
354
- "bitsandbytes::gemm_8bit",
355
- "(Tensor A, Tensor B, Tensor absmax, int blocksize) -> Tensor",
356
- )
357
-
358
-
359
- @register_fake("bitsandbytes::gemm_8bit")
360
- def _(A: torch.Tensor, B: torch.Tensor, absmax: torch.Tensor, blocksize: int) -> torch.Tensor:
361
- torch._check(blocksize > 0, lambda: f"blocksize must be positive, got {blocksize}")
362
- torch._check(A.dtype == torch.float32, lambda: f"A must be float32, got {A.dtype}")
363
- torch._check(B.dtype == torch.uint8, lambda: f"B must be uint8, got {B.dtype}")
364
- torch._check(absmax.dtype == torch.float32, lambda: f"absmax must be float32, got {absmax.dtype}")
365
- N = B.shape[0]
366
- return torch.empty((*A.shape[:-1], N), device=A.device, dtype=A.dtype)
367
-
368
-
369
- torch.library.define(
370
- "bitsandbytes::gemv_4bit",
371
- "(Tensor A, Tensor B, int[] shapeB, Tensor absmax, Tensor code, int blocksize) -> Tensor",
372
- )
373
-
374
-
375
- @register_fake("bitsandbytes::gemv_4bit")
376
- def _(
377
- A: torch.Tensor, B: torch.Tensor, shapeB: Sequence[int], absmax: torch.Tensor, code: torch.Tensor, blocksize: int
378
- ) -> torch.Tensor:
379
- torch._check(blocksize in (32, 64, 128, 256, 512, 1024, 2048, 4096), lambda: f"invalid blocksize {blocksize}")
380
- torch._check(
381
- A.dtype in (torch.float16, torch.bfloat16, torch.float32),
382
- lambda: f"A must be float16, bfloat16, or float32, got {A.dtype}",
383
- )
384
- torch._check(
385
- B.dtype in (torch.uint8, torch.bfloat16, torch.float16, torch.float32),
386
- lambda: f"B must be backed by storage of type uint8, bfloat16, float16, or float32, got {B.dtype}",
387
- )
388
- shape = (*A.shape[:-1], shapeB[0])
389
- return torch.empty(shape, device=A.device, dtype=A.dtype)
390
-
391
-
392
- torch.library.define(
393
- "bitsandbytes::gemv_4bit.out",
394
- "(Tensor A, Tensor B, int[] shapeB, Tensor absmax, Tensor code, int blocksize, Tensor! out) -> ()",
395
- )
396
-
397
-
398
- @register_fake("bitsandbytes::gemv_4bit.out")
399
- def _(
400
- A: torch.Tensor,
401
- B: torch.Tensor,
402
- shapeB: Sequence[int],
403
- absmax: torch.Tensor,
404
- code: torch.Tensor,
405
- blocksize: int,
406
- out: torch.Tensor,
407
- ) -> None:
408
- torch._check(blocksize in (32, 64, 128, 256, 512, 1024, 2048, 4096), lambda: f"invalid blocksize {blocksize}")
409
- torch._check(
410
- A.dtype in (torch.float16, torch.bfloat16, torch.float32),
411
- lambda: f"A must be float16, bfloat16, or float32, got {A.dtype}",
412
- )
413
- torch._check(
414
- B.dtype in (torch.uint8, torch.bfloat16, torch.float16, torch.float32),
415
- lambda: f"B must be backed by storage of type uint8, bfloat16, float16, or float32, got {B.dtype}",
416
- )
417
- torch._check(
418
- out.shape == (*A.shape[:-1], shapeB[0]),
419
- lambda: f"Expected out.shape == {(*A.shape[:-1], shapeB[0])}, got {out.shape}",
420
- )
421
- torch._check(out.device == A.device, lambda: f"Expected out.device == {A.device}, got {out.device}")
422
- torch._check(out.dtype == A.dtype, lambda: f"Expected out.dtype == {A.dtype}, got {out.dtype}")
423
-
424
-
425
- torch.library.define(
426
- "bitsandbytes::optimizer_update_32bit",
427
- "(str optimizer_name, Tensor(a0!) g, Tensor(a1!) p, Tensor(a2!) state1, Tensor(a3!)? state2, Tensor(a4!)? unorm_vec, float max_unorm, float param_norm, float beta1, float beta2, float beta3, float alpha, float eps, float weight_decay, int step, float lr, float gnorm_scale, bool skip_zeros=False) -> ()",
428
- )
429
-
430
-
431
- @register_fake("bitsandbytes::optimizer_update_32bit")
432
- def _(
433
- optimizer_name: str,
434
- g: torch.Tensor,
435
- p: torch.Tensor,
436
- state1: torch.Tensor,
437
- state2: Optional[torch.Tensor],
438
- unorm_vec: Optional[torch.Tensor],
439
- max_unorm: float,
440
- param_norm: float,
441
- beta1: float,
442
- beta2: float,
443
- beta3: float,
444
- alpha: float,
445
- eps: float,
446
- weight_decay: float,
447
- step: int,
448
- lr: float,
449
- gnorm_scale: float,
450
- skip_zeros=False,
451
- ) -> None:
452
- torch._check(
453
- g.numel() == p.numel(),
454
- lambda: f"g and p must have the same number of elements, got {g.numel()} and {p.numel()}",
455
- )
456
- compute_dtypes = [torch.float16, torch.bfloat16, torch.float32]
457
-
458
- torch._check(
459
- g.dtype in compute_dtypes,
460
- lambda: f"g must be bfloat16, float16, or float32, got {g.dtype}",
461
- )
462
- torch._check(
463
- g.dtype == p.dtype,
464
- lambda: f"Expected all tensors to have the same dtype, got g.dtype={g.dtype}, p.dtype={p.dtype}",
465
- )
466
-
467
-
468
- torch.library.define(
469
- "bitsandbytes::optimizer_update_8bit_blockwise",
470
- "(str optimizer_name, Tensor(a0!) g, Tensor(a1!) p, Tensor(a2!) state1, Tensor(a3!)? state2, float beta1, float beta2, float beta3, float alpha, float eps, int step, float lr, Tensor(a4!) qmap1, Tensor(a5!)? qmap2, Tensor(a6!) absmax1, Tensor(a7!)? absmax2, float weight_decay, float gnorm_scale, bool skip_zeros=False) -> ()",
471
- )
472
-
473
-
474
- @register_fake("bitsandbytes::optimizer_update_8bit_blockwise")
475
- def _(
476
- optimizer_name: str,
477
- g: torch.Tensor,
478
- p: torch.Tensor,
479
- state1: torch.Tensor,
480
- state2: Optional[torch.Tensor],
481
- beta1: float,
482
- beta2: float,
483
- beta3: float,
484
- alpha: float,
485
- eps: float,
486
- step: int,
487
- lr: float,
488
- qmap1: torch.Tensor,
489
- qmap2: Optional[torch.Tensor],
490
- absmax1: torch.Tensor,
491
- absmax2: Optional[torch.Tensor],
492
- weight_decay: float,
493
- gnorm_scale: float,
494
- skip_zeros=False,
495
- ) -> None:
496
- torch._check(
497
- g.numel() == p.numel(),
498
- lambda: f"g and p must have the same number of elements, got {g.numel()} and {p.numel()}",
499
- )
500
- compute_dtypes = [torch.float16, torch.bfloat16, torch.float32]
501
-
502
- torch._check(
503
- g.dtype in compute_dtypes,
504
- lambda: f"g must be bfloat16, float16, or float32, got {g.dtype}",
505
- )
506
- torch._check(
507
- g.dtype == p.dtype,
508
- lambda: f"Expected all tensors to have the same dtype, got g.dtype={g.dtype}, p.dtype={p.dtype}",
509
- )
510
- torch._check(
511
- state1.dtype == torch.uint8,
512
- lambda: f"state1 must be uint8, got {state1.dtype}",
513
- )
514
- torch._check(
515
- qmap1.dtype == absmax1.dtype == torch.float32,
516
- lambda: f"Expected qmap1 and absmax1 to be float32, got qmap1.dtype={qmap1.dtype}, absmax1.dtype={absmax1.dtype}",
517
- )
518
- if state2 is not None:
519
- torch._check(
520
- state2.dtype == torch.uint8,
521
- lambda: f"state2 must be uint8, got {state2.dtype}",
522
- )
523
- torch._check(
524
- qmap2.dtype == absmax2.dtype == torch.float32,
525
- lambda: f"Expected qmap2 and absmax2 to be float32, got qmap2.dtype={qmap2.dtype}, absmax2.dtype={absmax2.dtype}",
526
- )
1
+ from collections.abc import Sequence
2
+ from typing import Optional
3
+
4
+ import torch
5
+
6
+ register_fake = torch.library.register_fake
7
+ register_kernel = torch.library.register_kernel
8
+
9
+ # Int8 mixed precision matmul + dequant + bias
10
+ torch.library.define(
11
+ "bitsandbytes::int8_mixed_scaled_mm",
12
+ "(Tensor A, Tensor CA, Tensor CB, Tensor SCA, Tensor SCB, Tensor? outlier_cols=None, Tensor? bias=None) -> (Tensor, Tensor?)",
13
+ )
14
+
15
+
16
+ @register_fake("bitsandbytes::int8_mixed_scaled_mm")
17
+ def _(
18
+ A: torch.Tensor,
19
+ CA: torch.Tensor,
20
+ CB: torch.Tensor,
21
+ SCA: torch.Tensor,
22
+ SCB: torch.Tensor,
23
+ outlier_cols: Optional[torch.Tensor] = None,
24
+ bias: Optional[torch.Tensor] = None,
25
+ ) -> tuple[torch.Tensor, Optional[torch.Tensor]]:
26
+ shapeC = (*CA.shape[:-1], CB.shape[0])
27
+
28
+ out = torch.empty(shapeC, device=A.device, dtype=A.dtype)
29
+
30
+ outlier_cols = torch.library.get_ctx().new_dynamic_size()
31
+ subA = A.new_empty(outlier_cols, dtype=torch.int64)
32
+
33
+ return out, subA
34
+
35
+
36
+ # Higher level op: int8 matmul + dequant + bias
37
+ torch.library.define(
38
+ "bitsandbytes::int8_scaled_mm",
39
+ "(Tensor A, Tensor B, Tensor row_stats, Tensor col_stats, Tensor? bias=None, ScalarType? dtype=None) -> Tensor",
40
+ )
41
+
42
+
43
+ @register_fake("bitsandbytes::int8_scaled_mm")
44
+ def _(
45
+ A: torch.Tensor,
46
+ B: torch.Tensor,
47
+ row_stats: torch.Tensor,
48
+ col_stats: torch.Tensor,
49
+ bias: Optional[torch.Tensor] = None,
50
+ dtype: Optional[torch.dtype] = None,
51
+ ) -> torch.Tensor:
52
+ shapeC = (*A.shape[:-1], B.shape[0])
53
+ return torch.empty(shapeC, device=A.device, dtype=dtype or torch.float16)
54
+
55
+
56
+ torch.library.define(
57
+ "bitsandbytes::int8_linear_matmul",
58
+ "(Tensor A, Tensor B) -> Tensor",
59
+ )
60
+
61
+
62
+ @register_fake("bitsandbytes::int8_linear_matmul")
63
+ def _(A: torch.Tensor, B: torch.Tensor):
64
+ torch._check(A.dtype == torch.int8, lambda: "A must be int8")
65
+ torch._check(B.dtype == torch.int8, lambda: "B must be int8")
66
+ shapeC = (*A.shape[:-1], B.shape[0])
67
+ return torch.empty(shapeC, device=A.device, dtype=torch.int32)
68
+
69
+
70
+ # More info on `out` overloads:
71
+ # https://github.com/pytorch/pytorch/issues/125044
72
+ torch.library.define(
73
+ "bitsandbytes::int8_linear_matmul.out",
74
+ "(Tensor A, Tensor B, Tensor! out) -> ()",
75
+ )
76
+
77
+
78
+ @register_fake("bitsandbytes::int8_linear_matmul.out")
79
+ def _(A: torch.Tensor, B: torch.Tensor, out: torch.Tensor):
80
+ shapeC = (*A.shape[:-1], B.shape[0])
81
+
82
+ torch._check(A.dtype == torch.int8, lambda: "A must be int8")
83
+ torch._check(B.dtype == torch.int8, lambda: "B must be int8")
84
+ torch._check(out.shape == shapeC, lambda: f"Expected out.shape == {shapeC}, got {out.shape}")
85
+ torch._check(out.device == A.device, lambda: f"Expected out.device == {A.device}, got {out.device}")
86
+ torch._check(out.dtype == torch.int32, lambda: f"Expected out.dtype == int32, got {out.dtype}")
87
+
88
+
89
+ torch.library.define(
90
+ "bitsandbytes::int8_vectorwise_quant",
91
+ "(Tensor A, float threshold=0.0) -> (Tensor, Tensor, Tensor?)",
92
+ )
93
+
94
+
95
+ @register_fake("bitsandbytes::int8_vectorwise_quant")
96
+ def _(A: torch.Tensor, threshold=0.0):
97
+ out_row = torch.empty(A.shape, device=A.device, dtype=torch.int8)
98
+ row_stats = torch.empty(A.numel() // A.shape[-1], device=A.device, dtype=torch.float32)
99
+
100
+ if threshold == 0.0:
101
+ return out_row, row_stats, None
102
+
103
+ outlier_cols = torch.library.get_ctx().new_dynamic_size()
104
+
105
+ return out_row, row_stats, A.new_empty(outlier_cols, dtype=torch.int64)
106
+
107
+
108
+ torch.library.define("bitsandbytes::int8_vectorwise_dequant", "(Tensor A, Tensor stats) -> Tensor")
109
+
110
+
111
+ @register_fake("bitsandbytes::int8_vectorwise_dequant")
112
+ def _(A: torch.Tensor, stats: torch.Tensor) -> torch.Tensor:
113
+ torch._check(A.dtype == torch.int8, lambda: "A must be int8")
114
+ return torch.empty_like(A, dtype=torch.float32)
115
+
116
+
117
+ # Default PyTorch-native implementation
118
+ @register_kernel("bitsandbytes::int8_vectorwise_dequant", "default")
119
+ def _(A: torch.Tensor, stats: torch.Tensor):
120
+ # To dequantize we divide by 127, or multiply by the reciprocal.
121
+ return A * stats.view(-1, 1) * 7.874015718698502e-3
122
+
123
+
124
+ torch.library.define(
125
+ "bitsandbytes::int8_mm_dequant",
126
+ "(Tensor A, Tensor row_stats, Tensor col_stats, ScalarType? dtype=None, Tensor? bias=None) -> Tensor",
127
+ )
128
+
129
+
130
+ @register_fake("bitsandbytes::int8_mm_dequant")
131
+ def _(
132
+ A: torch.Tensor,
133
+ row_stats: torch.Tensor,
134
+ col_stats: torch.Tensor,
135
+ dtype: Optional[torch.dtype] = None,
136
+ bias: Optional[torch.Tensor] = None,
137
+ ) -> torch.Tensor:
138
+ torch._check(A.dtype == torch.int32, lambda: "A must be int32")
139
+ return torch.empty_like(A, dtype=dtype or torch.float16)
140
+
141
+
142
+ torch.library.define(
143
+ "bitsandbytes::int8_double_quant",
144
+ "(Tensor A, float threshold=0.0) -> (Tensor, Tensor, Tensor, Tensor, Tensor?)",
145
+ )
146
+
147
+
148
+ @register_fake("bitsandbytes::int8_double_quant")
149
+ def _(
150
+ A: torch.Tensor,
151
+ threshold=0.0,
152
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor, Optional[torch.Tensor]]:
153
+ out_row = torch.empty_like(A, dtype=torch.int8)
154
+ out_col = torch.empty_like(A, dtype=torch.int8)
155
+ row_stats = torch.empty(A.numel() // A.shape[-1], device=A.device, dtype=torch.float32)
156
+ col_stats = torch.empty(A.shape[-1], device=A.device, dtype=torch.float32)
157
+ outlier_n = torch.library.get_ctx().new_dynamic_size()
158
+ outlier_cols = A.new_empty(outlier_n, dtype=torch.int64)
159
+ return out_row, out_col, row_stats, col_stats, outlier_cols
160
+
161
+
162
+ torch.library.define(
163
+ "bitsandbytes::dequantize_4bit",
164
+ "(Tensor A, Tensor absmax, int blocksize, str quant_type, int[] shape, ScalarType dtype) -> Tensor",
165
+ )
166
+
167
+
168
+ @register_fake("bitsandbytes::dequantize_4bit")
169
+ def _(
170
+ A: torch.Tensor,
171
+ absmax: torch.Tensor,
172
+ blocksize: int,
173
+ quant_type: str,
174
+ shape: Sequence[int],
175
+ dtype: torch.dtype,
176
+ ) -> torch.Tensor:
177
+ torch._check(blocksize in (32, 64, 128, 256, 512, 1024, 2048, 4096), lambda: f"invalid blocksize {blocksize}")
178
+ torch._check(quant_type in ("nf4", "fp4"), lambda: f"quant_type must be 'nf4' or 'fp4', got {quant_type!r}")
179
+ torch._check(absmax.dtype == torch.float32, lambda: f"absmax must be float32, got {absmax.dtype}")
180
+ torch._check(
181
+ dtype in (torch.float16, torch.bfloat16, torch.float32),
182
+ lambda: f"Blockwise 4bit dequantization only supports 16/32-bit floats, but got {dtype}",
183
+ )
184
+ return torch.empty(shape, dtype=dtype, device=A.device)
185
+
186
+
187
+ torch.library.define(
188
+ "bitsandbytes::dequantize_4bit.out",
189
+ "(Tensor A, Tensor absmax, int blocksize, str quant_type, int[] shape, ScalarType dtype, Tensor! out) -> ()",
190
+ )
191
+
192
+
193
+ @register_fake("bitsandbytes::dequantize_4bit.out")
194
+ def _(
195
+ A: torch.Tensor,
196
+ absmax: torch.Tensor,
197
+ blocksize: int,
198
+ quant_type: str,
199
+ shape: Sequence[int],
200
+ dtype: torch.dtype,
201
+ out: torch.Tensor,
202
+ ) -> None:
203
+ torch._check(blocksize in (32, 64, 128, 256, 512, 1024, 2048, 4096), lambda: f"invalid blocksize {blocksize}")
204
+ torch._check(quant_type in ("nf4", "fp4"), lambda: f"quant_type must be 'nf4' or 'fp4', got {quant_type!r}")
205
+ torch._check(absmax.dtype == torch.float32, lambda: f"absmax must be float32, got {absmax.dtype}")
206
+ torch._check(
207
+ dtype in (torch.float16, torch.bfloat16, torch.float32),
208
+ lambda: f"Blockwise 4bit dequantization only supports 16/32-bit floats, but got {dtype}",
209
+ )
210
+ torch._check(out.shape == shape, lambda: f"Expected out.shape == {shape}, got {out.shape}")
211
+ torch._check(out.device == A.device, lambda: f"Expected out.device == {A.device}, got {out.device}")
212
+ torch._check(out.dtype == dtype, lambda: f"Expected out.dtype == {dtype}, got {out.dtype}")
213
+
214
+
215
+ torch.library.define(
216
+ "bitsandbytes::quantize_4bit",
217
+ "(Tensor A, int blocksize, str quant_type, ScalarType quant_storage) -> (Tensor, Tensor)",
218
+ )
219
+
220
+
221
+ @register_fake("bitsandbytes::quantize_4bit")
222
+ def _(
223
+ A: torch.Tensor, blocksize: int, quant_type: str, quant_storage: torch.dtype
224
+ ) -> tuple[torch.Tensor, torch.Tensor]:
225
+ torch._check(blocksize in (32, 64, 128, 256, 512, 1024, 2048, 4096), lambda: f"invalid blocksize {blocksize}")
226
+ torch._check(quant_type in ("nf4", "fp4"), lambda: f"quant_type must be 'nf4' or 'fp4', got {quant_type!r}")
227
+ torch._check(
228
+ A.dtype in (torch.float16, torch.bfloat16, torch.float32),
229
+ lambda: f"Blockwise 4bit quantization only supports 16/32-bit floats, but got {A.dtype}",
230
+ )
231
+
232
+ n = A.numel()
233
+ blocks = -(n // -blocksize)
234
+ absmax = torch.empty((blocks,), device=A.device, dtype=torch.float32)
235
+ out = torch.empty(((n + 1) // (quant_storage.itemsize * 2), 1), device=A.device, dtype=quant_storage)
236
+ return out, absmax
237
+
238
+
239
+ torch.library.define(
240
+ "bitsandbytes::gemm_4bit",
241
+ "(Tensor A, Tensor B, int[] shapeB, Tensor absmax, int blocksize, str quant_type, "
242
+ "Tensor? bias=None, Tensor? absmax_8bit=None, Tensor? absmax_code=None, Tensor? absmax_offset=None) -> Tensor",
243
+ )
244
+
245
+
246
+ @register_fake("bitsandbytes::gemm_4bit")
247
+ def _(
248
+ A: torch.Tensor,
249
+ B: torch.Tensor,
250
+ shapeB: Sequence[int],
251
+ absmax: torch.Tensor,
252
+ blocksize: int,
253
+ quant_type: str,
254
+ bias: Optional[torch.Tensor] = None,
255
+ absmax_8bit: Optional[torch.Tensor] = None,
256
+ absmax_code: Optional[torch.Tensor] = None,
257
+ absmax_offset: Optional[torch.Tensor] = None,
258
+ ) -> torch.Tensor:
259
+ torch._check(len(shapeB) == 2, lambda: f"shapeB must be 2D [N, K], got {list(shapeB)}")
260
+ torch._check(A.shape[-1] == shapeB[1], lambda: f"A inner dim ({A.shape[-1]}) must match shapeB ({shapeB[1]})")
261
+ torch._check(
262
+ A.dtype in (torch.float16, torch.bfloat16, torch.float32),
263
+ lambda: f"A must be float16, bfloat16, or float32, got {A.dtype}",
264
+ )
265
+ torch._check(
266
+ B.dtype in (torch.uint8, torch.bfloat16, torch.float16, torch.float32),
267
+ lambda: f"B must be backed by storage of type uint8, bfloat16, float16, or float32, got {B.dtype}",
268
+ )
269
+ torch._check(blocksize in (32, 64, 128, 256, 512, 1024, 2048, 4096), lambda: f"invalid blocksize {blocksize}")
270
+ torch._check(quant_type in ("nf4", "fp4"), lambda: f"quant_type must be 'nf4' or 'fp4', got {quant_type!r}")
271
+ torch._check(absmax.dtype == torch.float32, lambda: f"absmax must be float32, got {absmax.dtype}")
272
+ if absmax_8bit is not None:
273
+ torch._check(absmax_8bit.ndim == 1, lambda: f"absmax_8bit must be 1D, got {absmax_8bit.ndim}D")
274
+ torch._check(absmax_8bit.dtype == torch.uint8, lambda: f"absmax_8bit must be uint8, got {absmax_8bit.dtype}")
275
+ torch._check(absmax_code is not None, lambda: "absmax_code required when absmax_8bit is provided")
276
+ torch._check(absmax_code.ndim == 1, lambda: f"absmax_code must be 1D, got {absmax_code.ndim}D")
277
+ torch._check(
278
+ absmax_code.shape[0] == 256, lambda: f"absmax_code must have 256 entries, got {absmax_code.shape[0]}"
279
+ )
280
+ torch._check(
281
+ absmax_code.dtype == torch.float32, lambda: f"absmax_code must be float32, got {absmax_code.dtype}"
282
+ )
283
+ torch._check(absmax_offset is not None, lambda: "absmax_offset required when absmax_8bit is provided")
284
+ torch._check(
285
+ absmax_offset.ndim == 0, lambda: f"absmax_offset must be a scalar (0-dim), got {absmax_offset.ndim}D"
286
+ )
287
+ torch._check(
288
+ absmax_offset.dtype == torch.float32, lambda: f"absmax_offset must be float32, got {absmax_offset.dtype}"
289
+ )
290
+ if bias is not None:
291
+ torch._check(bias.ndim == 1, lambda: f"bias must be 1D, got {bias.ndim}D")
292
+ torch._check(bias.shape[0] == shapeB[0], lambda: f"bias length ({bias.shape[0]}) must match N ({shapeB[0]})")
293
+ torch._check(bias.dtype == A.dtype, lambda: f"bias dtype ({bias.dtype}) must match A dtype ({A.dtype})")
294
+ N = shapeB[0]
295
+ return torch.empty((*A.shape[:-1], N), dtype=A.dtype, device=A.device)
296
+
297
+
298
+ torch.library.define(
299
+ "bitsandbytes::dequantize_blockwise",
300
+ "(Tensor A, Tensor absmax, Tensor code, int blocksize, ScalarType dtype) -> Tensor",
301
+ )
302
+
303
+
304
+ @register_fake("bitsandbytes::dequantize_blockwise")
305
+ def _(A: torch.Tensor, absmax: torch.Tensor, code: torch.Tensor, blocksize: int, dtype: torch.dtype) -> torch.Tensor:
306
+ torch._check(blocksize > 0, lambda: f"blocksize must be positive, got {blocksize}")
307
+ torch._check(A.dtype == torch.uint8, lambda: f"A must be uint8, got {A.dtype}")
308
+ torch._check(
309
+ dtype in (torch.float16, torch.bfloat16, torch.float32),
310
+ lambda: f"Blockwise dequantization only supports 16/32-bit floats, but got {dtype}",
311
+ )
312
+ return torch.empty_like(A, dtype=dtype)
313
+
314
+
315
+ torch.library.define(
316
+ "bitsandbytes::dequantize_blockwise.out",
317
+ "(Tensor A, Tensor absmax, Tensor code, int blocksize, ScalarType dtype, Tensor! out) -> ()",
318
+ )
319
+
320
+
321
+ @register_fake("bitsandbytes::dequantize_blockwise.out")
322
+ def _(
323
+ A: torch.Tensor, absmax: torch.Tensor, code: torch.Tensor, blocksize: int, dtype: torch.dtype, out: torch.Tensor
324
+ ):
325
+ torch._check(blocksize > 0, lambda: f"blocksize must be positive, got {blocksize}")
326
+ torch._check(A.dtype == torch.uint8, lambda: f"A must be uint8, got {A.dtype}")
327
+ torch._check(
328
+ dtype in (torch.float16, torch.bfloat16, torch.float32),
329
+ lambda: f"Blockwise dequantization only supports 16/32-bit floats, but got {dtype}",
330
+ )
331
+ torch._check(out.shape == A.shape, lambda: f"Expected out.shape == {A.shape}, got {out.shape}")
332
+ torch._check(out.device == A.device, lambda: f"Expected out.device == {A.device}, got {out.device}")
333
+ torch._check(out.dtype == dtype, lambda: f"Expected out.dtype == {dtype}, got {out.dtype}")
334
+
335
+
336
+ torch.library.define("bitsandbytes::quantize_blockwise", "(Tensor A, Tensor code, int blocksize) -> (Tensor, Tensor)")
337
+
338
+
339
+ @register_fake("bitsandbytes::quantize_blockwise")
340
+ def _(A: torch.Tensor, code: torch.Tensor, blocksize: int) -> tuple[torch.Tensor, torch.Tensor]:
341
+ torch._check(blocksize > 0, lambda: f"blocksize must be positive, got {blocksize}")
342
+ torch._check(
343
+ A.dtype in (torch.float16, torch.bfloat16, torch.float32),
344
+ lambda: f"Blockwise quantization only supports 16/32-bit floats, but got {A.dtype}",
345
+ )
346
+ n = A.numel()
347
+ blocks = -(n // -blocksize)
348
+ absmax = torch.empty((blocks,), device=A.device, dtype=torch.float32)
349
+ out = torch.empty_like(A, dtype=torch.uint8)
350
+ return out, absmax
351
+
352
+
353
+ torch.library.define(
354
+ "bitsandbytes::gemm_8bit",
355
+ "(Tensor A, Tensor B, Tensor absmax, int blocksize) -> Tensor",
356
+ )
357
+
358
+
359
+ @register_fake("bitsandbytes::gemm_8bit")
360
+ def _(A: torch.Tensor, B: torch.Tensor, absmax: torch.Tensor, blocksize: int) -> torch.Tensor:
361
+ torch._check(blocksize > 0, lambda: f"blocksize must be positive, got {blocksize}")
362
+ torch._check(A.dtype == torch.float32, lambda: f"A must be float32, got {A.dtype}")
363
+ torch._check(B.dtype == torch.uint8, lambda: f"B must be uint8, got {B.dtype}")
364
+ torch._check(absmax.dtype == torch.float32, lambda: f"absmax must be float32, got {absmax.dtype}")
365
+ N = B.shape[0]
366
+ return torch.empty((*A.shape[:-1], N), device=A.device, dtype=A.dtype)
367
+
368
+
369
+ torch.library.define(
370
+ "bitsandbytes::gemv_4bit",
371
+ "(Tensor A, Tensor B, int[] shapeB, Tensor absmax, Tensor code, int blocksize) -> Tensor",
372
+ )
373
+
374
+
375
+ @register_fake("bitsandbytes::gemv_4bit")
376
+ def _(
377
+ A: torch.Tensor, B: torch.Tensor, shapeB: Sequence[int], absmax: torch.Tensor, code: torch.Tensor, blocksize: int
378
+ ) -> torch.Tensor:
379
+ torch._check(blocksize in (32, 64, 128, 256, 512, 1024, 2048, 4096), lambda: f"invalid blocksize {blocksize}")
380
+ torch._check(
381
+ A.dtype in (torch.float16, torch.bfloat16, torch.float32),
382
+ lambda: f"A must be float16, bfloat16, or float32, got {A.dtype}",
383
+ )
384
+ torch._check(
385
+ B.dtype in (torch.uint8, torch.bfloat16, torch.float16, torch.float32),
386
+ lambda: f"B must be backed by storage of type uint8, bfloat16, float16, or float32, got {B.dtype}",
387
+ )
388
+ shape = (*A.shape[:-1], shapeB[0])
389
+ return torch.empty(shape, device=A.device, dtype=A.dtype)
390
+
391
+
392
+ torch.library.define(
393
+ "bitsandbytes::gemv_4bit.out",
394
+ "(Tensor A, Tensor B, int[] shapeB, Tensor absmax, Tensor code, int blocksize, Tensor! out) -> ()",
395
+ )
396
+
397
+
398
+ @register_fake("bitsandbytes::gemv_4bit.out")
399
+ def _(
400
+ A: torch.Tensor,
401
+ B: torch.Tensor,
402
+ shapeB: Sequence[int],
403
+ absmax: torch.Tensor,
404
+ code: torch.Tensor,
405
+ blocksize: int,
406
+ out: torch.Tensor,
407
+ ) -> None:
408
+ torch._check(blocksize in (32, 64, 128, 256, 512, 1024, 2048, 4096), lambda: f"invalid blocksize {blocksize}")
409
+ torch._check(
410
+ A.dtype in (torch.float16, torch.bfloat16, torch.float32),
411
+ lambda: f"A must be float16, bfloat16, or float32, got {A.dtype}",
412
+ )
413
+ torch._check(
414
+ B.dtype in (torch.uint8, torch.bfloat16, torch.float16, torch.float32),
415
+ lambda: f"B must be backed by storage of type uint8, bfloat16, float16, or float32, got {B.dtype}",
416
+ )
417
+ torch._check(
418
+ out.shape == (*A.shape[:-1], shapeB[0]),
419
+ lambda: f"Expected out.shape == {(*A.shape[:-1], shapeB[0])}, got {out.shape}",
420
+ )
421
+ torch._check(out.device == A.device, lambda: f"Expected out.device == {A.device}, got {out.device}")
422
+ torch._check(out.dtype == A.dtype, lambda: f"Expected out.dtype == {A.dtype}, got {out.dtype}")
423
+
424
+
425
+ torch.library.define(
426
+ "bitsandbytes::optimizer_update_32bit",
427
+ "(str optimizer_name, Tensor(a0!) g, Tensor(a1!) p, Tensor(a2!) state1, Tensor(a3!)? state2, Tensor(a4!)? unorm_vec, float max_unorm, float param_norm, float beta1, float beta2, float beta3, float alpha, float eps, float weight_decay, int step, float lr, float gnorm_scale, bool skip_zeros=False) -> ()",
428
+ )
429
+
430
+
431
+ @register_fake("bitsandbytes::optimizer_update_32bit")
432
+ def _(
433
+ optimizer_name: str,
434
+ g: torch.Tensor,
435
+ p: torch.Tensor,
436
+ state1: torch.Tensor,
437
+ state2: Optional[torch.Tensor],
438
+ unorm_vec: Optional[torch.Tensor],
439
+ max_unorm: float,
440
+ param_norm: float,
441
+ beta1: float,
442
+ beta2: float,
443
+ beta3: float,
444
+ alpha: float,
445
+ eps: float,
446
+ weight_decay: float,
447
+ step: int,
448
+ lr: float,
449
+ gnorm_scale: float,
450
+ skip_zeros=False,
451
+ ) -> None:
452
+ torch._check(
453
+ g.numel() == p.numel(),
454
+ lambda: f"g and p must have the same number of elements, got {g.numel()} and {p.numel()}",
455
+ )
456
+ compute_dtypes = [torch.float16, torch.bfloat16, torch.float32]
457
+
458
+ torch._check(
459
+ g.dtype in compute_dtypes,
460
+ lambda: f"g must be bfloat16, float16, or float32, got {g.dtype}",
461
+ )
462
+ torch._check(
463
+ g.dtype == p.dtype,
464
+ lambda: f"Expected all tensors to have the same dtype, got g.dtype={g.dtype}, p.dtype={p.dtype}",
465
+ )
466
+
467
+
468
+ torch.library.define(
469
+ "bitsandbytes::optimizer_update_8bit_blockwise",
470
+ "(str optimizer_name, Tensor(a0!) g, Tensor(a1!) p, Tensor(a2!) state1, Tensor(a3!)? state2, float beta1, float beta2, float beta3, float alpha, float eps, int step, float lr, Tensor(a4!) qmap1, Tensor(a5!)? qmap2, Tensor(a6!) absmax1, Tensor(a7!)? absmax2, float weight_decay, float gnorm_scale, bool skip_zeros=False) -> ()",
471
+ )
472
+
473
+
474
+ @register_fake("bitsandbytes::optimizer_update_8bit_blockwise")
475
+ def _(
476
+ optimizer_name: str,
477
+ g: torch.Tensor,
478
+ p: torch.Tensor,
479
+ state1: torch.Tensor,
480
+ state2: Optional[torch.Tensor],
481
+ beta1: float,
482
+ beta2: float,
483
+ beta3: float,
484
+ alpha: float,
485
+ eps: float,
486
+ step: int,
487
+ lr: float,
488
+ qmap1: torch.Tensor,
489
+ qmap2: Optional[torch.Tensor],
490
+ absmax1: torch.Tensor,
491
+ absmax2: Optional[torch.Tensor],
492
+ weight_decay: float,
493
+ gnorm_scale: float,
494
+ skip_zeros=False,
495
+ ) -> None:
496
+ torch._check(
497
+ g.numel() == p.numel(),
498
+ lambda: f"g and p must have the same number of elements, got {g.numel()} and {p.numel()}",
499
+ )
500
+ compute_dtypes = [torch.float16, torch.bfloat16, torch.float32]
501
+
502
+ torch._check(
503
+ g.dtype in compute_dtypes,
504
+ lambda: f"g must be bfloat16, float16, or float32, got {g.dtype}",
505
+ )
506
+ torch._check(
507
+ g.dtype == p.dtype,
508
+ lambda: f"Expected all tensors to have the same dtype, got g.dtype={g.dtype}, p.dtype={p.dtype}",
509
+ )
510
+ torch._check(
511
+ state1.dtype == torch.uint8,
512
+ lambda: f"state1 must be uint8, got {state1.dtype}",
513
+ )
514
+ torch._check(
515
+ qmap1.dtype == absmax1.dtype == torch.float32,
516
+ lambda: f"Expected qmap1 and absmax1 to be float32, got qmap1.dtype={qmap1.dtype}, absmax1.dtype={absmax1.dtype}",
517
+ )
518
+ if state2 is not None:
519
+ torch._check(
520
+ state2.dtype == torch.uint8,
521
+ lambda: f"state2 must be uint8, got {state2.dtype}",
522
+ )
523
+ torch._check(
524
+ qmap2.dtype == absmax2.dtype == torch.float32,
525
+ lambda: f"Expected qmap2 and absmax2 to be float32, got qmap2.dtype={qmap2.dtype}, absmax2.dtype={absmax2.dtype}",
526
+ )