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
File without changes
@@ -0,0 +1,632 @@
1
+ from collections.abc import Sequence
2
+ from functools import cache, wraps
3
+ from math import prod, sqrt
4
+ from typing import Optional
5
+
6
+ import torch
7
+
8
+ from ..._ops import register_kernel
9
+ from ..utils import _get_4bit_code
10
+
11
+
12
+ def _try_torch_compile(func=None, **compile_kwargs):
13
+ """
14
+ Wrapper around torch.compile that falls back to the original function if compilation fails.
15
+ """
16
+
17
+ def decorator(fn):
18
+ try:
19
+ compiled_fn = torch.compile(fn, **compile_kwargs)
20
+
21
+ @wraps(fn)
22
+ def wrapper(*args, **kwargs):
23
+ try:
24
+ return compiled_fn(*args, **kwargs)
25
+ except Exception:
26
+ return fn(*args, **kwargs)
27
+
28
+ return wrapper
29
+ except Exception:
30
+ return fn
31
+
32
+ if func is None:
33
+ return decorator
34
+ else:
35
+ return decorator(func)
36
+
37
+
38
+ @register_kernel("bitsandbytes::int8_mm_dequant", "default")
39
+ def _(
40
+ A: torch.Tensor,
41
+ row_stats: torch.Tensor,
42
+ col_stats: torch.Tensor,
43
+ dtype: Optional[torch.dtype] = None,
44
+ bias: Optional[torch.Tensor] = None,
45
+ ) -> torch.Tensor:
46
+ if A.dtype != torch.int32:
47
+ raise ValueError(f"A must be int32, got {A.dtype}")
48
+ if row_stats.dtype != torch.float32:
49
+ raise ValueError(f"row_stats must be float32, got {row_stats.dtype}")
50
+ if col_stats.dtype != torch.float32:
51
+ raise ValueError(f"col_stats must be float32, got {col_stats.dtype}")
52
+
53
+ A_calc = A.view(-1, A.shape[-1])
54
+ row_stats = row_stats.reshape(-1).unsqueeze(-1)
55
+ col_stats = col_stats.reshape(-1).unsqueeze(0)
56
+
57
+ out = A_calc * (row_stats * col_stats) * 6.200124e-05
58
+ if bias is not None:
59
+ out += bias
60
+
61
+ return out.to(dtype or torch.float16)
62
+
63
+
64
+ @register_kernel("bitsandbytes::int8_mixed_scaled_mm", "default")
65
+ def _(
66
+ A: torch.Tensor,
67
+ CA: torch.Tensor,
68
+ CB: torch.Tensor,
69
+ SCA: torch.Tensor,
70
+ SCB: torch.Tensor,
71
+ outlier_cols: Optional[torch.Tensor] = None,
72
+ bias: Optional[torch.Tensor] = None,
73
+ ) -> tuple[torch.Tensor, Optional[torch.Tensor]]:
74
+ subB = None
75
+
76
+ if outlier_cols is not None and outlier_cols.numel():
77
+ # Extract the inputs with outliers in original precision
78
+ subA = A[:, outlier_cols].contiguous()
79
+
80
+ # Dequantize the corresponding weight columns
81
+ subB = (
82
+ torch.ops.bitsandbytes.int8_vectorwise_dequant.default(CB[:, outlier_cols].contiguous(), SCB)
83
+ .to(A.dtype)
84
+ .t()
85
+ )
86
+
87
+ # TODO: if state.has_fp16_weights: subB = B[:, outlier_cols].t()
88
+
89
+ else:
90
+ # Needed for torch.compile when there are no outliers.
91
+ subA = torch.empty(0, device=A.device, dtype=A.dtype)
92
+
93
+ # Int8 Matmul + Dequant + Bias
94
+ output = torch.ops.bitsandbytes.int8_scaled_mm.default(CA, CB, SCA, SCB, bias=bias, dtype=A.dtype)
95
+
96
+ if subB is not None:
97
+ # Add the outlier columns back to the output
98
+ output = output.addmm(subA, subB)
99
+
100
+ return output, subA
101
+
102
+
103
+ @register_kernel("bitsandbytes::int8_scaled_mm", "default")
104
+ def _(
105
+ A: torch.Tensor,
106
+ B: torch.Tensor,
107
+ row_stats: torch.Tensor,
108
+ col_stats: torch.Tensor,
109
+ bias: Optional[torch.Tensor] = None,
110
+ dtype: Optional[torch.dtype] = None,
111
+ ) -> torch.Tensor:
112
+ out_i32 = torch.ops.bitsandbytes.int8_linear_matmul.default(A, B)
113
+ return torch.ops.bitsandbytes.int8_mm_dequant.default(
114
+ out_i32,
115
+ row_stats,
116
+ col_stats,
117
+ dtype=dtype or torch.float16,
118
+ bias=bias,
119
+ )
120
+
121
+
122
+ @register_kernel("bitsandbytes::int8_linear_matmul", "default")
123
+ def _(A: torch.Tensor, B: torch.Tensor):
124
+ return _int8_linear_matmul_impl(A, B)
125
+
126
+
127
+ @register_kernel("bitsandbytes::int8_linear_matmul.out", "default")
128
+ def _(A: torch.Tensor, B: torch.Tensor, out: torch.Tensor):
129
+ if out.dtype != torch.int32:
130
+ raise ValueError(f"out must be int32, got {out.dtype}")
131
+ _int8_linear_matmul_impl(A, B, out)
132
+
133
+
134
+ def _int8_linear_matmul_impl(A: torch.Tensor, B: torch.Tensor, out: Optional[torch.Tensor] = None):
135
+ # Naive implementation: perform matmul in fp32
136
+ result = torch.matmul(A.float(), B.float().t()).to(torch.int32)
137
+ if out is not None:
138
+ result = out.copy_(result)
139
+ return result
140
+
141
+
142
+ @register_kernel("bitsandbytes::int8_vectorwise_quant", "default")
143
+ def _(A: torch.Tensor, threshold=0.0):
144
+ rows = A.numel() // A.shape[-1]
145
+ outlier_cols = None
146
+
147
+ outlier_restore = None
148
+
149
+ if threshold > 0.0:
150
+ outliers = A.abs() >= threshold
151
+
152
+ if outliers.any():
153
+ # Determine which columns contain outliers, and zero out the
154
+ # outliers ahead of quantization. We need to keep a backup of these
155
+ # outliers to restore them after quantization.
156
+ outlier_cols = torch.argwhere(outliers.any(dim=0)).view(-1)
157
+ outlier_restore = A[outliers].clone()
158
+ A[outliers] = 0
159
+ else:
160
+ # Needed for torch.compile support.
161
+ outlier_cols = torch.empty(0, device=A.device, dtype=torch.int64)
162
+
163
+ # Get absmax for each row.
164
+ row_stats = torch.max(A.abs(), dim=1).values.float()
165
+
166
+ # Quantize row-wise to int8.
167
+ out_row = torch.round(A * (127.0 / row_stats.unsqueeze(-1))).to(torch.int8)
168
+
169
+ # Zero out values from outlier columns across all rows.
170
+ if rows > 1 and outlier_cols is not None:
171
+ out_row[:, outlier_cols] = 0
172
+
173
+ # Restore outliers.
174
+ if outlier_restore is not None:
175
+ A[outliers] = outlier_restore
176
+
177
+ return out_row, row_stats, outlier_cols
178
+
179
+
180
+ @register_kernel("bitsandbytes::quantize_blockwise", "default")
181
+ def _(A: torch.Tensor, code: torch.Tensor, blocksize: int) -> tuple[torch.Tensor, torch.Tensor]:
182
+ A_flat = A.reshape(-1).float()
183
+ n = A_flat.numel()
184
+ rem = n % blocksize
185
+ full = n - rem
186
+ blocks = full // blocksize
187
+ A_com = A_flat[:full].reshape(blocks, blocksize)
188
+ absmax = A_com.abs().max(dim=-1)[0]
189
+ scaled = torch.clamp(A_com * (1.0 / absmax.clamp(min=1e-38).view(-1, 1)), -1, 1).reshape(-1)
190
+ if rem:
191
+ am = A_flat[full:].abs().max().clamp(min=1e-38)
192
+ absmax = torch.cat([absmax, am.unsqueeze(0)])
193
+ scaled = torch.cat([scaled, torch.clamp(A_flat[full:] / am, -1, 1)])
194
+ bounds = (code[:-1] + code[1:]) / 2 # code is always sorted (same assumption as CUDA kernel)
195
+ q = torch.bucketize(scaled, bounds, out_int32=True).to(torch.uint8)
196
+ return q.reshape(A.shape), absmax
197
+
198
+
199
+ @_try_torch_compile(dynamic=False)
200
+ def _dequantize_blockwise_compute(
201
+ A_flat: torch.Tensor, absmax: torch.Tensor, code: torch.Tensor, blocksize: int, dtype: torch.dtype
202
+ ):
203
+ n = A_flat.numel()
204
+ out = code[A_flat.to(torch.int64)]
205
+ rem = n % blocksize
206
+ if rem == 0:
207
+ out = (out.reshape(-1, blocksize) * absmax.view(-1, 1)).reshape(n)
208
+ else:
209
+ full = n - rem
210
+ blocks = full // blocksize
211
+ out = torch.cat(
212
+ [
213
+ (out[:full].reshape(blocks, blocksize) * absmax[:blocks].view(-1, 1)).reshape(full),
214
+ out[full:] * absmax[blocks],
215
+ ]
216
+ )
217
+ return out.to(dtype)
218
+
219
+
220
+ @register_kernel("bitsandbytes::dequantize_blockwise", "default")
221
+ def _(A: torch.Tensor, absmax: torch.Tensor, code: torch.Tensor, blocksize: int, dtype: torch.dtype) -> torch.Tensor:
222
+ return _dequantize_blockwise_compute(A.reshape(-1), absmax, code, blocksize, dtype).reshape(A.shape)
223
+
224
+
225
+ @cache
226
+ def _get_4bit_quantize_bounds(quant_type: str, device: torch.device):
227
+ code = _get_4bit_code(quant_type, device)
228
+ order = torch.argsort(code)
229
+ midpoints = (code[order[:-1]] + code[order[1:]]) / 2
230
+ return midpoints, order # NF4 order is identity (sorted); FP4 needs remap
231
+
232
+
233
+ @register_kernel("bitsandbytes::quantize_4bit", "default")
234
+ def _(
235
+ A: torch.Tensor, blocksize: int, quant_type: str, quant_storage: torch.dtype
236
+ ) -> tuple[torch.Tensor, torch.Tensor]:
237
+ bounds, order = _get_4bit_quantize_bounds(quant_type, A.device)
238
+ A_flat = A.reshape(-1).float()
239
+ n = A_flat.numel()
240
+ rem = n % blocksize
241
+ full = n - rem
242
+ blocks = full // blocksize
243
+ A_com = A_flat[:full].reshape(blocks, blocksize)
244
+ absmax = A_com.abs().max(dim=-1)[0]
245
+ scaled = torch.clamp(A_com * (1.0 / absmax.clamp(min=1e-38).view(-1, 1)), -1, 1).reshape(-1)
246
+ if rem:
247
+ am = A_flat[full:].abs().max().clamp(min=1e-38)
248
+ absmax = torch.cat([absmax, am.unsqueeze(0)])
249
+ scaled = torch.cat([scaled, torch.clamp(A_flat[full:] / am, -1, 1)])
250
+ if scaled.numel() % 2:
251
+ scaled = torch.nn.functional.pad(scaled, (0, 1))
252
+ q = torch.bucketize(scaled, bounds, out_int32=True)
253
+ if quant_type != "nf4":
254
+ q = order[q]
255
+ q8 = q.to(torch.uint8)
256
+ packed = ((q8[::2] << 4) | q8[1::2]).unsqueeze(1)
257
+ if quant_storage != torch.uint8:
258
+ packed = packed.squeeze().view(quant_storage).unsqueeze(1)
259
+ return packed, absmax
260
+
261
+
262
+ @_try_torch_compile(dynamic=False)
263
+ def _dequantize_4bit_compute(
264
+ A_flat: torch.Tensor,
265
+ absmax: torch.Tensor,
266
+ code: torch.Tensor,
267
+ blocksize: int,
268
+ shape: Sequence[int],
269
+ dtype: torch.dtype,
270
+ ):
271
+ n = prod(shape)
272
+ out_dq = torch.empty(A_flat.size(0) * 2, dtype=torch.int32, device=A_flat.device)
273
+ out_dq[1::2] = A_flat & 0xF
274
+ out_dq[::2] = A_flat >> 4
275
+ out_dq = code[out_dq][:n] # stays fp32, matches C++ / CUDA behavior
276
+ rem = n % blocksize
277
+ if rem:
278
+ full = n - rem
279
+ blocks = full // blocksize
280
+ out = torch.empty(n, dtype=torch.float32, device=A_flat.device)
281
+ out[:full] = (out_dq[:full].view(-1, blocksize) * absmax[:blocks].view(-1, 1)).reshape(full)
282
+ out[full:] = out_dq[full:] * absmax[blocks]
283
+ else:
284
+ out = (out_dq.view(-1, blocksize) * absmax.view(-1, 1)).reshape(n)
285
+ return out.reshape(-1, *shape[1:]).to(dtype)
286
+
287
+
288
+ @register_kernel("bitsandbytes::dequantize_4bit", "default")
289
+ def _(
290
+ A: torch.Tensor,
291
+ absmax: torch.Tensor,
292
+ blocksize: int,
293
+ quant_type: str,
294
+ shape: Sequence[int],
295
+ dtype: torch.dtype,
296
+ ) -> torch.Tensor:
297
+ if A.dtype != torch.uint8:
298
+ A = A.view(torch.uint8)
299
+ code = _get_4bit_code(quant_type, A.device)
300
+ return _dequantize_4bit_compute(A.reshape(-1), absmax, code, blocksize, shape, dtype)
301
+
302
+
303
+ @register_kernel("bitsandbytes::gemv_4bit", "default")
304
+ def _(
305
+ A: torch.Tensor,
306
+ B: torch.Tensor,
307
+ shapeB: Sequence[int],
308
+ absmax: torch.Tensor,
309
+ code: torch.Tensor,
310
+ blocksize: int,
311
+ ) -> torch.Tensor:
312
+ # Applied from dequantize_4bit
313
+ quant_type = "fp4" if code[1] > 0 else "nf4"
314
+ B_dq = torch.ops.bitsandbytes.dequantize_4bit.default(B, absmax, blocksize, quant_type, shapeB, A.dtype)
315
+
316
+ return torch.nn.functional.linear(
317
+ A,
318
+ B_dq,
319
+ bias=None,
320
+ )
321
+
322
+
323
+ def _gemm_4bit_default_impl(
324
+ A: torch.Tensor,
325
+ B: torch.Tensor,
326
+ shapeB: Sequence[int],
327
+ absmax: torch.Tensor,
328
+ blocksize: int,
329
+ quant_type: str,
330
+ bias: Optional[torch.Tensor] = None,
331
+ absmax_8bit: Optional[torch.Tensor] = None,
332
+ absmax_code: Optional[torch.Tensor] = None,
333
+ absmax_offset: Optional[torch.Tensor] = None,
334
+ ) -> torch.Tensor:
335
+ # When nested, per-block scale = absmax_code[absmax_8bit[i]] * absmax[i // 256] + absmax_offset
336
+ if absmax_8bit is not None:
337
+ absmax = (
338
+ torch.ops.bitsandbytes.dequantize_blockwise.default(absmax_8bit, absmax, absmax_code, 256, torch.float32)
339
+ + absmax_offset
340
+ )
341
+ B_dq = torch.ops.bitsandbytes.dequantize_4bit.default(B, absmax, blocksize, quant_type, shapeB, A.dtype)
342
+ return torch.nn.functional.linear(A, B_dq, bias)
343
+
344
+
345
+ register_kernel("bitsandbytes::gemm_4bit", "default")(_gemm_4bit_default_impl)
346
+
347
+
348
+ MOMENTUM = 0
349
+ RMSPROP = 1
350
+ ADAGRAD = 2
351
+ ADAM = 3
352
+ # LION should be larger than MOMENTUM, RMSPROP, ADAGRAD due to comparison in kernels
353
+ LION = 4
354
+ ADEMAMIX = 5
355
+
356
+ name2optimizer_id = {
357
+ "momentum": MOMENTUM,
358
+ "lars": MOMENTUM,
359
+ "rmsprop": RMSPROP,
360
+ "adagrad": ADAGRAD,
361
+ "adam": ADAM,
362
+ "lamb": ADAM,
363
+ "lion": LION,
364
+ "ademamix": ADEMAMIX,
365
+ }
366
+
367
+
368
+ @_try_torch_compile
369
+ def _optimizer_precondition_32bit(
370
+ g: torch.Tensor,
371
+ p: torch.Tensor,
372
+ state1: torch.Tensor,
373
+ state2: Optional[torch.Tensor],
374
+ unorm_vec: torch.Tensor,
375
+ beta1: float,
376
+ beta2: float,
377
+ eps: float,
378
+ weight_decay: float,
379
+ step: int,
380
+ lr: float,
381
+ gnorm_scale: float,
382
+ optimizer_id: int,
383
+ ):
384
+ """Preprocessing optimizer, computing update norm"""
385
+
386
+ g_vals = gnorm_scale * g
387
+
388
+ if optimizer_id == 3: # ADAM
389
+ correction1 = 1.0 / (1.0 - beta1**step)
390
+ correction2 = 1.0 / (1.0 - beta2**step)
391
+
392
+ s1_vals = state1 * beta1 + (1.0 - beta1) * g_vals
393
+ s2_vals = state2 * beta2 + (1.0 - beta2) * g_vals * g_vals
394
+
395
+ s1_vals = s1_vals * correction1
396
+ s2_vals = s2_vals * correction2
397
+
398
+ update_vals = s1_vals / (torch.sqrt(s2_vals) + eps)
399
+ update_norm = update_vals * update_vals
400
+
401
+ elif optimizer_id == 5: # ADEMAMIX
402
+ update_norm = state1
403
+
404
+ elif optimizer_id == 0: # MOMENTUM
405
+ if step == 1:
406
+ s1_vals = g_vals
407
+ else:
408
+ s1_vals = state1 * beta1 + g_vals
409
+ update_norm = s1_vals * s1_vals
410
+
411
+ elif optimizer_id == 4: # LION
412
+ s1_vals = state1 * beta2 + (1.0 - beta2) * g_vals
413
+ update_norm = s1_vals
414
+
415
+ elif optimizer_id == 1: # RMSPROP
416
+ s1_vals = state1 * beta1 + (1.0 - beta1) * g_vals * g_vals
417
+ update_vals = g_vals / (torch.sqrt(s1_vals) + eps)
418
+ update_norm = update_vals * update_vals
419
+
420
+ elif optimizer_id == 2: # ADAGRAD
421
+ s1_vals = state1 + g_vals * g_vals
422
+ update_vals = g_vals / (torch.sqrt(s1_vals) + eps)
423
+ update_norm = update_vals * update_vals
424
+
425
+ total_norm = torch.sum(update_norm)
426
+ unorm_vec.add_(total_norm)
427
+
428
+
429
+ @_try_torch_compile
430
+ def _optimizer_update_32bit(
431
+ g: torch.Tensor,
432
+ p: torch.Tensor,
433
+ state1: torch.Tensor,
434
+ state2: Optional[torch.Tensor],
435
+ unorm_vec: Optional[torch.Tensor],
436
+ max_unorm: float,
437
+ param_norm: float,
438
+ beta1: float,
439
+ beta2: float,
440
+ beta3: float,
441
+ alpha: float,
442
+ eps: float,
443
+ weight_decay: float,
444
+ step: int,
445
+ lr: float,
446
+ gnorm_scale: float,
447
+ optimizer_id: int,
448
+ ):
449
+ """Unified optimizer update kernel"""
450
+
451
+ p_vals = p.float()
452
+ g_vals = (gnorm_scale * g).float()
453
+ # Coupled (L2) weight decay: fold wd into the gradient. This is correct for
454
+ # MOMENTUM/RMSPROP/ADAGRAD, but NOT for LION (id 4), which uses *decoupled*
455
+ # (AdamW-style) weight decay applied to the param directly (see the LION branch
456
+ # below and Chen et al. 2023). LION is intentionally excluded here.
457
+ if optimizer_id in [0, 1, 2] and weight_decay > 0.0:
458
+ g_vals = g_vals + p_vals * weight_decay
459
+
460
+ update_scale = 1.0
461
+ if max_unorm > 0.0:
462
+ current_unorm = torch.sqrt(unorm_vec)
463
+ if optimizer_id in [0, 1, 2, 4]: # 1-state optimizers
464
+ if current_unorm > max_unorm * param_norm + eps:
465
+ update_scale = (max_unorm * param_norm + eps) / current_unorm
466
+ else: # 2-state optimizers
467
+ if current_unorm > max_unorm * param_norm:
468
+ update_scale = (max_unorm * param_norm) / current_unorm
469
+
470
+ if optimizer_id == 3: # ADAM
471
+ s1_vals = state1 * beta1 + (1.0 - beta1) * g_vals
472
+ s2_vals = state2 * beta2 + (1.0 - beta2) * g_vals * g_vals
473
+
474
+ correction1 = 1.0 - beta1**step
475
+ correction2 = sqrt(1.0 - beta2**step)
476
+ step_size = -lr * correction2 / correction1
477
+
478
+ if weight_decay > 0.0:
479
+ p_vals = p_vals * (1.0 - lr * weight_decay)
480
+
481
+ update_val = update_scale * step_size * (s1_vals / (torch.sqrt(s2_vals) + eps * correction2))
482
+ p_vals = p_vals + update_val
483
+
484
+ state1.copy_(s1_vals)
485
+ state2.copy_(s2_vals)
486
+
487
+ elif optimizer_id == 5: # ADEMAMIX
488
+ s1_vals = state1[0]
489
+ s3_vals = state1[1]
490
+ s2_vals = state2
491
+
492
+ m1 = s1_vals * beta1 + (1.0 - beta1) * g_vals
493
+ m2 = s3_vals * beta3 + (1.0 - beta3) * g_vals
494
+ nu = s2_vals * beta2 + (1.0 - beta2) * g_vals * g_vals
495
+
496
+ correction1 = 1.0 - beta1**step
497
+ correction2 = sqrt(1.0 - beta2**step)
498
+
499
+ if weight_decay > 0.0:
500
+ p_vals = p_vals * (1.0 - lr * weight_decay)
501
+
502
+ mixed_momentum = (m1 / correction1) + (alpha * m2)
503
+ adaptive_term = (torch.sqrt(nu) / correction2) + eps
504
+ p_vals = p_vals - lr * (mixed_momentum / adaptive_term)
505
+
506
+ state1[0].copy_(m1)
507
+ state1[1].copy_(m2)
508
+ state2.copy_(nu)
509
+
510
+ elif optimizer_id == 0: # MOMENTUM
511
+ if step == 1:
512
+ s1_vals = g_vals
513
+ else:
514
+ s1_vals = state1 * beta1 + g_vals
515
+
516
+ update_val = update_scale * (-lr * s1_vals)
517
+ p_vals = p_vals + update_val
518
+
519
+ state1.copy_(s1_vals)
520
+
521
+ elif optimizer_id == 4: # LION
522
+ # Lion uses decoupled weight decay: shrink the param directly (p *= 1 - lr*wd)
523
+ # rather than folding wd into the gradient. Matches the cpu backend, the CUDA
524
+ # 8-bit blockwise kernel, and the Lion paper (Chen et al. 2023).
525
+ if weight_decay > 0.0:
526
+ p_vals = p_vals * (1.0 - lr * weight_decay)
527
+
528
+ momentum_update = state1 * beta1 + (1.0 - beta1) * g_vals
529
+ update_val = update_scale * lr * torch.sign(momentum_update)
530
+ p_vals = p_vals - update_val
531
+
532
+ s1_vals = state1 * beta2 + (1.0 - beta2) * g_vals
533
+ state1.copy_(s1_vals)
534
+
535
+ elif optimizer_id == 1: # RMSPROP
536
+ s1_vals = state1 * beta1 + (1.0 - beta1) * g_vals * g_vals
537
+ update_val = update_scale * lr * g_vals / (torch.sqrt(s1_vals) + eps)
538
+ p_vals = p_vals - update_val
539
+
540
+ state1.copy_(s1_vals)
541
+
542
+ elif optimizer_id == 2: # ADAGRAD
543
+ s1_vals = state1 + g_vals * g_vals
544
+ update_val = lr * g_vals / (torch.sqrt(s1_vals) + eps)
545
+ p_vals = p_vals - update_val
546
+
547
+ state1.copy_(s1_vals)
548
+
549
+ p.copy_(p_vals)
550
+
551
+
552
+ @register_kernel("bitsandbytes::optimizer_update_32bit", "default")
553
+ def _(
554
+ optimizer_name: str,
555
+ g: torch.Tensor,
556
+ p: torch.Tensor,
557
+ state1: torch.Tensor,
558
+ state2: Optional[torch.Tensor],
559
+ unorm_vec: Optional[torch.Tensor],
560
+ max_unorm: float,
561
+ param_norm: float,
562
+ beta1: float,
563
+ beta2: float,
564
+ beta3: float,
565
+ alpha: float,
566
+ eps: float,
567
+ weight_decay: float,
568
+ step: int,
569
+ lr: float,
570
+ gnorm_scale: float = 1.0,
571
+ skip_zeros=False,
572
+ ) -> None:
573
+ """
574
+ 32-bit optimizer implemented by PyTorch with @torch.compile
575
+ """
576
+ if skip_zeros:
577
+ raise NotImplementedError("skip_zeros is not supported yet")
578
+
579
+ optimizer_id = name2optimizer_id[optimizer_name]
580
+
581
+ if optimizer_name == "lion":
582
+ _optimizer_update_32bit(
583
+ g,
584
+ p,
585
+ state1,
586
+ state2,
587
+ unorm_vec,
588
+ max_unorm,
589
+ param_norm,
590
+ beta1,
591
+ beta2,
592
+ beta3,
593
+ alpha,
594
+ eps,
595
+ weight_decay,
596
+ step,
597
+ lr,
598
+ gnorm_scale,
599
+ optimizer_id,
600
+ )
601
+
602
+ if max_unorm > 0.0:
603
+ unorm_vec.zero_()
604
+ _optimizer_precondition_32bit(
605
+ g, p, state1, state2, unorm_vec, beta1, beta2, eps, weight_decay, step, lr, gnorm_scale, optimizer_id
606
+ )
607
+ else:
608
+ if max_unorm > 0.0:
609
+ unorm_vec.zero_()
610
+ _optimizer_precondition_32bit(
611
+ g, p, state1, state2, unorm_vec, beta1, beta2, eps, weight_decay, step, lr, gnorm_scale, optimizer_id
612
+ )
613
+
614
+ _optimizer_update_32bit(
615
+ g,
616
+ p,
617
+ state1,
618
+ state2,
619
+ unorm_vec,
620
+ max_unorm,
621
+ param_norm,
622
+ beta1,
623
+ beta2,
624
+ beta3,
625
+ alpha,
626
+ eps,
627
+ weight_decay,
628
+ step,
629
+ lr,
630
+ gnorm_scale,
631
+ optimizer_id,
632
+ )
File without changes
@@ -0,0 +1,53 @@
1
+ from collections.abc import Sequence
2
+ import math
3
+
4
+ import torch
5
+
6
+ from ..._ops import register_kernel
7
+ from ..utils import GAUDI_SW_VER
8
+
9
+
10
+ # convert btw standard 4-bit compression format and ipex compression format
11
+ # needed for backward compatibility with older versions of gaudi sw
12
+ def _reverse_4bit_compress_format(weight: torch.Tensor):
13
+ out_1 = (weight & 0xF0) >> 4
14
+ out_2 = (weight & 0xF) << 4
15
+ out = out_1 | out_2
16
+ return out
17
+
18
+
19
+ @register_kernel("bitsandbytes::dequantize_4bit", "hpu")
20
+ def _(
21
+ A: torch.Tensor,
22
+ absmax: torch.Tensor,
23
+ blocksize: int,
24
+ quant_type: str,
25
+ shape: Sequence[int],
26
+ dtype: torch.dtype,
27
+ ) -> torch.Tensor:
28
+ if quant_type != "nf4":
29
+ raise ValueError(f"HPU backend only supports quant_type 'nf4', got {quant_type!r}")
30
+ if A.dtype not in (torch.bfloat16, torch.uint8):
31
+ raise ValueError(f"HPU backend only supports uint8 or bfloat16 storage, got {A.dtype}")
32
+
33
+ # Enable non uint8 dtype
34
+ if A.dtype != torch.uint8:
35
+ A = A.view(torch.uint8)
36
+
37
+ A = A.reshape(-1)
38
+
39
+ if GAUDI_SW_VER and (GAUDI_SW_VER.major < 1 or GAUDI_SW_VER.minor < 22):
40
+ A = _reverse_4bit_compress_format(A)
41
+
42
+ # HPU dequantization function for NF4 quantized tensors.
43
+ out_dq = torch.ops.hpu.dequantize_nf4(
44
+ A,
45
+ absmax.to(dtype),
46
+ blocksize,
47
+ out_shape=(math.prod(shape),),
48
+ out_dtype=dtype,
49
+ )
50
+
51
+ output = out_dq.reshape(shape)
52
+
53
+ return output
File without changes