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,580 @@
1
+ from collections.abc import Sequence
2
+ import ctypes as ct
3
+ import logging
4
+ import math
5
+ from math import prod
6
+ from typing import Optional
7
+
8
+ import torch
9
+
10
+ from bitsandbytes.functional import get_ptr, has_avx512bf16
11
+
12
+ from ..._ops import register_kernel
13
+ from ...cextension import ErrorHandlerMockBNBNativeLibrary, lib
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+ _has_avx512 = torch.backends.cpu.get_cpu_capability() == "AVX512"
18
+
19
+ # torch._int_mm for s8@s8->s32 is supported on CPU from torch 2.4+.
20
+ # However, we can overflow if we use this without AVX512_VNNI support.
21
+ # This is fixed in torch 2.6+, so we set this as the minimum to be safe.
22
+ # For more information: https://github.com/pytorch/pytorch/pull/136942
23
+ #
24
+ # Without AVX-512 (including aarch64), torch._int_mm uses a scalar fallback
25
+ # that is much slower than fp32 matmul. Only use it when AVX-512 is available.
26
+ if torch.__version__ >= (2, 6) and _has_avx512:
27
+
28
+ @register_kernel("bitsandbytes::int8_linear_matmul", "cpu")
29
+ def _(A: torch.Tensor, B: torch.Tensor):
30
+ return torch._int_mm(
31
+ A.reshape(-1, A.shape[-1]),
32
+ B.t(),
33
+ ).reshape(*A.shape[:-1], B.shape[0])
34
+
35
+
36
+ if not isinstance(lib, ErrorHandlerMockBNBNativeLibrary):
37
+
38
+ @register_kernel("bitsandbytes::quantize_blockwise", "cpu")
39
+ def _(A: torch.Tensor, code: torch.Tensor, blocksize: int) -> tuple[torch.Tensor, torch.Tensor]:
40
+ A = A.contiguous()
41
+ n = A.numel()
42
+ blocks = -(n // -blocksize)
43
+
44
+ absmax = torch.empty((blocks,), device=A.device, dtype=torch.float32)
45
+ out = torch.empty(A.shape, device=A.device, dtype=torch.uint8)
46
+
47
+ if A.dtype == torch.float32:
48
+ lib.cquantize_blockwise_cpu_fp32(
49
+ get_ptr(code),
50
+ get_ptr(A),
51
+ get_ptr(absmax),
52
+ get_ptr(out),
53
+ ct.c_longlong(blocksize),
54
+ ct.c_longlong(n),
55
+ )
56
+ elif A.dtype == torch.bfloat16:
57
+ lib.cquantize_blockwise_cpu_bf16(
58
+ get_ptr(code),
59
+ get_ptr(A),
60
+ get_ptr(absmax),
61
+ get_ptr(out),
62
+ ct.c_longlong(blocksize),
63
+ ct.c_longlong(n),
64
+ )
65
+ elif A.dtype == torch.float16:
66
+ lib.cquantize_blockwise_cpu_fp16(
67
+ get_ptr(code),
68
+ get_ptr(A),
69
+ get_ptr(absmax),
70
+ get_ptr(out),
71
+ ct.c_longlong(blocksize),
72
+ ct.c_longlong(n),
73
+ )
74
+ else:
75
+ # Generic fallback for other dtypes
76
+ A_flat = A.reshape(n).float()
77
+ rem = n % blocksize
78
+ has_rem = rem > 0
79
+ A_com = A_flat[: n - rem]
80
+ A_com_reshaped = A_com.reshape(n // blocksize, blocksize)
81
+ absmax[: blocks - has_rem] = torch.abs(A_com_reshaped).max(dim=-1)[0]
82
+ scaled_A = torch.clamp(A_com_reshaped * (1 / absmax[: blocks - has_rem].view(-1, 1)), -1, 1)
83
+ scaled_A = scaled_A.reshape(-1)
84
+ if has_rem:
85
+ absmax[-1] = torch.abs(A_flat[n - rem :]).max()
86
+ scaled_A_rem = torch.clamp(A_flat[n - rem :] * (1 / absmax[-1]), -1, 1)
87
+ scaled_A = torch.cat([scaled_A, scaled_A_rem], dim=0)
88
+
89
+ diff = torch.abs(scaled_A.unsqueeze(-1) - code.to(scaled_A.device))
90
+ out = torch.argmin(diff, dim=-1).to(torch.uint8).to(scaled_A.device).reshape(A.shape)
91
+
92
+ return out, absmax
93
+
94
+ @register_kernel("bitsandbytes::dequantize_blockwise", "cpu")
95
+ def _(
96
+ A: torch.Tensor, absmax: torch.Tensor, code: torch.Tensor, blocksize: int, dtype: torch.dtype
97
+ ) -> torch.Tensor:
98
+ A = A.contiguous()
99
+ out = torch.empty_like(A, dtype=dtype)
100
+ if dtype == torch.float32:
101
+ lib.cdequantize_blockwise_cpu_fp32(
102
+ get_ptr(code),
103
+ get_ptr(A),
104
+ get_ptr(absmax),
105
+ get_ptr(out),
106
+ ct.c_longlong(blocksize),
107
+ ct.c_longlong(A.numel()),
108
+ )
109
+ elif dtype == torch.bfloat16:
110
+ lib.cdequantize_blockwise_cpu_bf16(
111
+ get_ptr(code),
112
+ get_ptr(A),
113
+ get_ptr(absmax),
114
+ get_ptr(out),
115
+ ct.c_longlong(blocksize),
116
+ ct.c_longlong(A.numel()),
117
+ )
118
+ elif dtype == torch.float16:
119
+ lib.cdequantize_blockwise_cpu_fp16(
120
+ get_ptr(code),
121
+ get_ptr(A),
122
+ get_ptr(absmax),
123
+ get_ptr(out),
124
+ ct.c_longlong(blocksize),
125
+ ct.c_longlong(A.numel()),
126
+ )
127
+ else:
128
+ out = code[A.reshape(-1).int()]
129
+ blocks = out.shape[-1] // blocksize
130
+ res = out.shape[-1] % blocksize
131
+ if res != 0:
132
+ out = torch.nn.functional.pad(out, (0, blocksize - res), mode="constant", value=0)
133
+ out = (out.view(-1, blocksize) * absmax.view(-1, 1)).to(dtype).reshape(-1)
134
+ out = out[: blocks * blocksize + res]
135
+ out = out.reshape(A.shape)
136
+
137
+ return out
138
+
139
+ @register_kernel("bitsandbytes::dequantize_4bit", "cpu")
140
+ def _(
141
+ A: torch.Tensor,
142
+ absmax: torch.Tensor,
143
+ blocksize: int,
144
+ quant_type: str,
145
+ shape: Sequence[int],
146
+ dtype: torch.dtype,
147
+ ) -> torch.Tensor:
148
+ # Fallback as AVX512 implementation has accuracy issues with blocksize >= 2048.
149
+ # Note: this is not a common use case.
150
+ avx512_fallback = _has_avx512 and blocksize >= 2048
151
+
152
+ # Odd shape is not supported by this kernel; fallback to generic implementation
153
+ shape_fallback = shape[-1] % 2 != 0
154
+
155
+ if avx512_fallback or shape_fallback:
156
+ from ..default.ops import _dequantize_4bit_compute
157
+ from ..utils import _get_4bit_code
158
+
159
+ if A.dtype != torch.uint8:
160
+ A = A.view(torch.uint8)
161
+ code = _get_4bit_code(quant_type, A.device)
162
+ return _dequantize_4bit_compute(A.reshape(-1), absmax, code, blocksize, shape, dtype)
163
+
164
+ # Enable non uint8 dtype
165
+ if A.dtype != torch.uint8:
166
+ A = A.view(torch.uint8)
167
+
168
+ # TODO: support half precision absmax
169
+ if absmax.dtype != torch.float32:
170
+ absmax = absmax.float()
171
+
172
+ if len(shape) == 1:
173
+ shape = (1, shape[0])
174
+
175
+ m = prod(shape[:-1])
176
+ n = shape[-1]
177
+
178
+ A = A.reshape(m, n // 2)
179
+ out = torch.empty(shape, dtype=dtype, device=A.device)
180
+
181
+ if quant_type == "fp4":
182
+ if dtype == torch.float32:
183
+ lib.cdequantize_blockwise_cpu_fp4_fp32(
184
+ get_ptr(A),
185
+ get_ptr(absmax),
186
+ get_ptr(out),
187
+ ct.c_longlong(blocksize),
188
+ ct.c_longlong(m),
189
+ ct.c_longlong(n),
190
+ )
191
+ elif dtype == torch.bfloat16:
192
+ lib.cdequantize_blockwise_cpu_fp4_bf16(
193
+ get_ptr(A),
194
+ get_ptr(absmax),
195
+ get_ptr(out),
196
+ ct.c_longlong(blocksize),
197
+ ct.c_longlong(m),
198
+ ct.c_longlong(n),
199
+ )
200
+ elif dtype == torch.float16:
201
+ lib.cdequantize_blockwise_cpu_fp4_fp16(
202
+ get_ptr(A),
203
+ get_ptr(absmax),
204
+ get_ptr(out),
205
+ ct.c_longlong(blocksize),
206
+ ct.c_longlong(m),
207
+ ct.c_longlong(n),
208
+ )
209
+ elif quant_type == "nf4":
210
+ if dtype == torch.float32:
211
+ lib.cdequantize_blockwise_cpu_nf4_fp32(
212
+ get_ptr(A),
213
+ get_ptr(absmax),
214
+ get_ptr(out),
215
+ ct.c_longlong(blocksize),
216
+ ct.c_longlong(m),
217
+ ct.c_longlong(n),
218
+ )
219
+ elif dtype == torch.bfloat16:
220
+ lib.cdequantize_blockwise_cpu_nf4_bf16(
221
+ get_ptr(A),
222
+ get_ptr(absmax),
223
+ get_ptr(out),
224
+ ct.c_longlong(blocksize),
225
+ ct.c_longlong(m),
226
+ ct.c_longlong(n),
227
+ )
228
+ elif dtype == torch.float16:
229
+ lib.cdequantize_blockwise_cpu_nf4_fp16(
230
+ get_ptr(A),
231
+ get_ptr(absmax),
232
+ get_ptr(out),
233
+ ct.c_longlong(blocksize),
234
+ ct.c_longlong(m),
235
+ ct.c_longlong(n),
236
+ )
237
+ else:
238
+ raise ValueError
239
+
240
+ return out
241
+
242
+ if has_avx512bf16():
243
+ gemm_4bit_forward_kernel = None
244
+ try:
245
+ from kernels import get_kernel
246
+
247
+ gemm_4bit_forward_kernel = get_kernel(
248
+ "kernels-community/quantization-bitsandbytes", version=1
249
+ ).gemm_4bit_forward
250
+ except Exception as exc: # pragma: no cover - best effort fallback
251
+ gemm_4bit_forward_kernel = None
252
+ logger.warning(
253
+ "Failed to load CPU gemm_4bit_forward from kernels-community: %s. Please make sure you already `pip install kernels` and the kernels >= 0.11.1",
254
+ exc,
255
+ )
256
+
257
+ @register_kernel("bitsandbytes::gemv_4bit", "cpu")
258
+ def _(
259
+ A: torch.Tensor,
260
+ B: torch.Tensor,
261
+ shapeB: Sequence[int],
262
+ absmax: torch.Tensor,
263
+ code: torch.Tensor,
264
+ blocksize: int,
265
+ ) -> torch.Tensor:
266
+ if B.dtype != torch.uint8:
267
+ B = B.contiguous().view(torch.uint8)
268
+ dtype = A.dtype
269
+ quant_type = "fp4" if code[1] > 0 else "nf4"
270
+ # cpu fused op only support bf16 for now.
271
+ if dtype != torch.bfloat16:
272
+ A = A.to(torch.bfloat16)
273
+ if absmax.dtype != torch.bfloat16:
274
+ absmax = absmax.to(torch.bfloat16)
275
+
276
+ final_out_shape = (*A.shape[:-1], shapeB[0])
277
+ A = A.reshape(-1, A.shape[-1])
278
+ out_shape = (*A.shape[:-1], shapeB[0])
279
+ if gemm_4bit_forward_kernel is not None:
280
+ quant_type_num = 1 if quant_type == "fp4" else 0
281
+ # C++ kernel expects weight shape (N, K_packed), ensure 2D contiguous
282
+ B_2d = B.reshape(shapeB[0], -1).contiguous()
283
+ out = gemm_4bit_forward_kernel(A, B_2d, absmax, blocksize, quant_type_num)
284
+ else:
285
+ out = torch.empty(out_shape, dtype=A.dtype, device=A.device)
286
+ M = A.shape[0]
287
+ N = shapeB[0]
288
+ K = A.shape[1]
289
+ x_strideM = A.stride(0)
290
+ out_strideM = out.stride(0)
291
+ if quant_type == "fp4":
292
+ lib.gemv_4bit_inference_cpu_fp4_bf16(
293
+ ct.c_int64(M),
294
+ ct.c_int64(N),
295
+ ct.c_int64(K),
296
+ get_ptr(A),
297
+ get_ptr(B),
298
+ get_ptr(absmax),
299
+ get_ptr(out),
300
+ ct.c_int64(blocksize),
301
+ ct.c_int64(x_strideM),
302
+ ct.c_int64(out_strideM),
303
+ )
304
+ elif quant_type == "nf4":
305
+ lib.gemv_4bit_inference_cpu_nf4_bf16(
306
+ ct.c_int64(M),
307
+ ct.c_int64(N),
308
+ ct.c_int64(K),
309
+ get_ptr(A),
310
+ get_ptr(B),
311
+ get_ptr(absmax),
312
+ get_ptr(out),
313
+ ct.c_int64(blocksize),
314
+ ct.c_int64(x_strideM),
315
+ ct.c_int64(out_strideM),
316
+ )
317
+
318
+ if dtype != torch.bfloat16:
319
+ out = out.to(dtype)
320
+
321
+ return out.reshape(final_out_shape)
322
+
323
+
324
+ # ==================== CPU Optimizer Kernels ====================
325
+
326
+
327
+ def _compute_update_norm_and_scale(
328
+ update: torch.Tensor,
329
+ unorm_vec: Optional[torch.Tensor],
330
+ max_unorm: float,
331
+ param_norm: float,
332
+ ) -> float:
333
+ """Compute trust-ratio scaling factor for LAMB/LARS and store update norm."""
334
+ if max_unorm <= 0.0:
335
+ return 1.0
336
+ unorm = torch.norm(update).item()
337
+ if unorm_vec is not None:
338
+ unorm_vec.fill_(unorm)
339
+ if unorm > max_unorm * param_norm:
340
+ return (max_unorm * param_norm) / unorm
341
+ return 1.0
342
+
343
+
344
+ @torch.no_grad()
345
+ def _optimizer_update_32bit_cpu(
346
+ optimizer_name: str,
347
+ g: torch.Tensor,
348
+ p: torch.Tensor,
349
+ state1: torch.Tensor,
350
+ state2: Optional[torch.Tensor],
351
+ unorm_vec: Optional[torch.Tensor],
352
+ max_unorm: float,
353
+ param_norm: float,
354
+ beta1: float,
355
+ beta2: float,
356
+ beta3: float,
357
+ alpha: float,
358
+ eps: float,
359
+ weight_decay: float,
360
+ step: int,
361
+ lr: float,
362
+ gnorm_scale: float,
363
+ skip_zeros: bool = False,
364
+ ) -> None:
365
+ g_float = g.float() * gnorm_scale
366
+ p_float = p.data.float()
367
+
368
+ if optimizer_name in ("adam", "lamb"):
369
+ # Adam / LAMB (2-state): m and v
370
+ state1.mul_(beta1).add_(g_float, alpha=1.0 - beta1)
371
+ state2.mul_(beta2).addcmul_(g_float, g_float, value=1.0 - beta2)
372
+
373
+ correction1 = 1.0 - beta1**step
374
+ correction2 = math.sqrt(1.0 - beta2**step)
375
+ step_size = -lr * correction2 / correction1
376
+
377
+ if weight_decay > 0.0:
378
+ p_float.mul_(1.0 - lr * weight_decay)
379
+
380
+ update = state1 / (state2.sqrt() + eps * correction2)
381
+
382
+ update_scale = _compute_update_norm_and_scale(update, unorm_vec, max_unorm, param_norm)
383
+ p_float.add_(update, alpha=step_size * update_scale)
384
+
385
+ elif optimizer_name == "ademamix":
386
+ # AdEMAMix (2-state): state1 shape is (2, *p.shape), state1[0]=m1, state1[1]=m2
387
+ m1 = state1[0]
388
+ m2 = state1[1]
389
+ nu = state2
390
+
391
+ m1.mul_(beta1).add_(g_float, alpha=1.0 - beta1)
392
+ m2.mul_(beta3).add_(g_float, alpha=1.0 - beta3)
393
+ nu.mul_(beta2).addcmul_(g_float, g_float, value=1.0 - beta2)
394
+
395
+ correction1 = 1.0 - beta1**step
396
+ correction2 = math.sqrt(1.0 - beta2**step)
397
+
398
+ if weight_decay > 0.0:
399
+ p_float.mul_(1.0 - lr * weight_decay)
400
+
401
+ mixed_momentum = (m1 / correction1) + (alpha * m2)
402
+ adaptive_term = (nu.sqrt() / correction2) + eps
403
+ p_float.add_(mixed_momentum / adaptive_term, alpha=-lr)
404
+
405
+ elif optimizer_name in ("momentum", "lars"):
406
+ # SGD with momentum / LARS (1-state)
407
+ g_wd = g_float.add(p_float, alpha=weight_decay) if weight_decay > 0.0 else g_float
408
+
409
+ if step == 1:
410
+ state1.copy_(g_wd)
411
+ else:
412
+ state1.mul_(beta1).add_(g_wd)
413
+
414
+ update_scale = _compute_update_norm_and_scale(state1, unorm_vec, max_unorm, param_norm)
415
+ p_float.add_(state1, alpha=-lr * update_scale)
416
+
417
+ elif optimizer_name == "lion":
418
+ # Lion (2-state sign update)
419
+ if weight_decay > 0.0:
420
+ p_float.mul_(1.0 - lr * weight_decay)
421
+
422
+ update = state1.mul(beta1).add(g_float, alpha=1.0 - beta1)
423
+ p_float.add_(update.sign(), alpha=-lr)
424
+
425
+ state1.mul_(beta2).add_(g_float, alpha=1.0 - beta2)
426
+
427
+ elif optimizer_name == "rmsprop":
428
+ # RMSprop (1-state)
429
+ g_wd = g_float.add(p_float, alpha=weight_decay) if weight_decay > 0.0 else g_float
430
+ state1.mul_(beta1).addcmul_(g_wd, g_wd, value=1.0 - beta1)
431
+
432
+ update = g_wd / (state1.sqrt() + eps)
433
+ update_scale = _compute_update_norm_and_scale(update, unorm_vec, max_unorm, param_norm)
434
+ p_float.add_(update, alpha=-lr * update_scale)
435
+
436
+ elif optimizer_name == "adagrad":
437
+ # Adagrad (1-state)
438
+ g_wd = g_float.add(p_float, alpha=weight_decay) if weight_decay > 0.0 else g_float
439
+ state1.addcmul_(g_wd, g_wd, value=1.0)
440
+
441
+ update = g_wd / (state1.sqrt() + eps)
442
+ p_float.add_(update, alpha=-lr)
443
+
444
+ else:
445
+ raise ValueError(f"Unsupported optimizer for CPU: {optimizer_name}")
446
+
447
+ # Write back to original precision
448
+ p.data.copy_(p_float)
449
+
450
+
451
+ register_kernel("bitsandbytes::optimizer_update_32bit", "cpu")(_optimizer_update_32bit_cpu)
452
+
453
+
454
+ @torch.no_grad()
455
+ def _dequant_blockwise_fp32_direct(
456
+ A_uint8: torch.Tensor, absmax: torch.Tensor, code: torch.Tensor, blocksize: int
457
+ ) -> torch.Tensor:
458
+ return torch.ops.bitsandbytes.dequantize_blockwise(A_uint8, absmax, code, blocksize, torch.float32)
459
+
460
+
461
+ def _quant_blockwise_fp32_direct(
462
+ A_fp32: torch.Tensor, code: torch.Tensor, absmax_out: torch.Tensor, out_uint8: torch.Tensor, blocksize: int
463
+ ) -> None:
464
+ out, absmax = torch.ops.bitsandbytes.quantize_blockwise(A_fp32, code, blocksize)
465
+ out_uint8.copy_(out)
466
+ absmax_out.copy_(absmax)
467
+
468
+
469
+ def _optimizer_update_8bit_blockwise_cpu(
470
+ optimizer_name: str,
471
+ g: torch.Tensor,
472
+ p: torch.Tensor,
473
+ state1: torch.Tensor,
474
+ state2: Optional[torch.Tensor],
475
+ beta1: float,
476
+ beta2: float,
477
+ beta3: float,
478
+ alpha: float,
479
+ eps: float,
480
+ step: int,
481
+ lr: float,
482
+ qmap1: torch.Tensor,
483
+ qmap2: Optional[torch.Tensor],
484
+ absmax1: torch.Tensor,
485
+ absmax2: Optional[torch.Tensor],
486
+ weight_decay: float,
487
+ gnorm_scale: float,
488
+ skip_zeros: bool = False,
489
+ ) -> None:
490
+ blocksize = 256
491
+
492
+ # Dequantize states
493
+ if optimizer_name == "ademamix" and absmax1.ndim == 2:
494
+ s1_1 = _dequant_blockwise_fp32_direct(state1[0], absmax1[0], qmap1, blocksize)
495
+ s1_2 = _dequant_blockwise_fp32_direct(state1[1], absmax1[1], qmap1, blocksize)
496
+ state1_fp32 = torch.stack([s1_1, s1_2])
497
+ else:
498
+ state1_fp32 = _dequant_blockwise_fp32_direct(state1, absmax1, qmap1, blocksize)
499
+
500
+ state2_fp32 = None
501
+ if state2 is not None and qmap2 is not None and absmax2 is not None:
502
+ state2_fp32 = _dequant_blockwise_fp32_direct(state2, absmax2, qmap2, blocksize)
503
+
504
+ grad = g.float() * gnorm_scale
505
+ p_fp32 = p.data.float()
506
+
507
+ if optimizer_name in ("adam", "lamb"):
508
+ state1_fp32.mul_(beta1).add_(grad, alpha=1.0 - beta1)
509
+ state2_fp32.mul_(beta2).addcmul_(grad, grad, value=1.0 - beta2)
510
+
511
+ correction1 = 1.0 - beta1**step
512
+ correction2 = math.sqrt(1.0 - beta2**step)
513
+
514
+ denom = (state2_fp32.sqrt() / correction2).add_(eps)
515
+ if weight_decay > 0.0:
516
+ p_fp32.mul_(1.0 - lr * weight_decay)
517
+ p_fp32.addcdiv_(state1_fp32, denom, value=-lr / correction1)
518
+
519
+ elif optimizer_name == "ademamix":
520
+ m1_fp32, m2_fp32 = state1_fp32[0], state1_fp32[1]
521
+ nu_fp32 = state2_fp32
522
+
523
+ m1_fp32.mul_(beta1).add_(grad, alpha=1.0 - beta1)
524
+ m2_fp32.mul_(beta3).add_(grad, alpha=1.0 - beta3)
525
+ nu_fp32.mul_(beta2).addcmul_(grad, grad, value=1.0 - beta2)
526
+
527
+ correction1 = 1.0 - beta1**step
528
+ correction2 = math.sqrt(1.0 - beta2**step)
529
+
530
+ update = (m1_fp32 / correction1 + alpha * m2_fp32) / (nu_fp32.sqrt() / correction2 + eps)
531
+ if weight_decay > 0.0:
532
+ p_fp32.mul_(1.0 - lr * weight_decay)
533
+ p_fp32.add_(update, alpha=-lr)
534
+
535
+ state1_fp32 = torch.stack([m1_fp32, m2_fp32])
536
+
537
+ elif optimizer_name in ("momentum", "lars"):
538
+ grad.add_(p_fp32, alpha=weight_decay)
539
+ if step == 1:
540
+ state1_fp32.copy_(grad)
541
+ else:
542
+ state1_fp32.mul_(beta1).add_(grad)
543
+ p_fp32.add_(state1_fp32, alpha=-lr)
544
+
545
+ elif optimizer_name == "lion":
546
+ if weight_decay > 0.0:
547
+ p_fp32.mul_(1.0 - lr * weight_decay)
548
+
549
+ update_dir = torch.sign(state1_fp32.mul(beta1) + grad.mul(1.0 - beta1))
550
+ p_fp32.add_(update_dir, alpha=-lr)
551
+
552
+ state1_fp32.mul_(beta2).add_(grad, alpha=1.0 - beta2)
553
+
554
+ elif optimizer_name == "rmsprop":
555
+ grad.add_(p_fp32, alpha=weight_decay)
556
+ state1_fp32.mul_(beta1).addcmul_(grad, grad, value=1.0 - beta1)
557
+ p_fp32.addcdiv_(grad, state1_fp32.sqrt().add_(eps), value=-lr)
558
+
559
+ elif optimizer_name == "adagrad":
560
+ grad.add_(p_fp32, alpha=weight_decay)
561
+ state1_fp32.addcmul_(grad, grad, value=1.0)
562
+ p_fp32.addcdiv_(grad, state1_fp32.sqrt().add_(eps), value=-lr)
563
+
564
+ else:
565
+ raise ValueError(f"Unsupported optimizer for CPU 8-bit: {optimizer_name}")
566
+
567
+ p.data.copy_(p_fp32)
568
+
569
+ # Re-quantize states
570
+ if optimizer_name == "ademamix":
571
+ _quant_blockwise_fp32_direct(state1_fp32[0], qmap1, absmax1[0], state1[0], blocksize)
572
+ _quant_blockwise_fp32_direct(state1_fp32[1], qmap1, absmax1[1], state1[1], blocksize)
573
+ _quant_blockwise_fp32_direct(state2_fp32, qmap2, absmax2, state2, blocksize)
574
+ else:
575
+ _quant_blockwise_fp32_direct(state1_fp32, qmap1, absmax1, state1, blocksize)
576
+ if state2_fp32 is not None:
577
+ _quant_blockwise_fp32_direct(state2_fp32, qmap2, absmax2, state2, blocksize)
578
+
579
+
580
+ register_kernel("bitsandbytes::optimizer_update_8bit_blockwise", "cpu")(_optimizer_update_8bit_blockwise_cpu)
File without changes