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,1177 @@
1
+ import math
2
+ from typing import Optional
3
+
4
+ import torch
5
+
6
+ import triton
7
+ import triton.language as tl
8
+
9
+ # from triton.language.extra import libdevice
10
+ from .kernels_8bit_quant import (
11
+ dequant_8bit_blockwise,
12
+ dequant_8bit_blockwise_kernel_util,
13
+ quantize_8bit_blockwise_kernel_util,
14
+ quantize_blockwise_triton,
15
+ )
16
+
17
+ MOMENTUM = 0
18
+ RMSPROP = 1
19
+ ADAGRAD = 2
20
+ ADAM = 3
21
+ # LION should be larger than MOMENTUM, RMSPROP, ADAGRAD due to comparison in kernels
22
+ LION = 4
23
+ ADEMAMIX = 5
24
+
25
+ name2optimizer_id = {
26
+ "momentum": MOMENTUM,
27
+ "lars": MOMENTUM,
28
+ "rmsprop": RMSPROP,
29
+ "adagrad": ADAGRAD,
30
+ "adam": ADAM,
31
+ "lamb": ADAM,
32
+ "lion": LION,
33
+ "ademamix": ADEMAMIX,
34
+ }
35
+
36
+
37
+ @triton.jit
38
+ def _optimizer_precondition_2state_32bit(
39
+ g_ptr,
40
+ p_ptr,
41
+ state1_ptr,
42
+ state2_ptr,
43
+ unorm_ptr,
44
+ beta1: tl.constexpr,
45
+ beta2: tl.constexpr,
46
+ eps: tl.constexpr,
47
+ weight_decay: tl.constexpr,
48
+ step,
49
+ beta1_step,
50
+ beta2_step,
51
+ lr,
52
+ gnorm_scale: tl.constexpr,
53
+ n_elements,
54
+ OPTIMIZER_ID: tl.constexpr,
55
+ BLOCK_SIZE: tl.constexpr,
56
+ N_PER_TH: tl.constexpr,
57
+ ):
58
+ """Preprocessing optimizer, computing update norm (2-state optimizer)"""
59
+ pid = tl.program_id(axis=0)
60
+ block_start_idx = pid * N_PER_TH
61
+ offsets = block_start_idx * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE * N_PER_TH)
62
+ mask = offsets < n_elements
63
+
64
+ g_vals = tl.load(g_ptr + offsets, mask=mask, other=0.0)
65
+ s1_vals = tl.load(state1_ptr + offsets, mask=mask, other=0.0)
66
+ s2_vals = tl.load(state2_ptr + offsets, mask=mask, other=0.0)
67
+
68
+ g_vals = gnorm_scale * g_vals
69
+
70
+ correction1 = 1.0 / (1.0 - beta1_step)
71
+ correction2 = 1.0 / (1.0 - beta2_step)
72
+
73
+ if OPTIMIZER_ID == 3: # ADAM
74
+ s1_vals = s1_vals * beta1 + (1.0 - beta1) * g_vals
75
+ s2_vals = s2_vals * beta2 + (1.0 - beta2) * g_vals * g_vals
76
+
77
+ s1_vals = s1_vals * correction1
78
+ s2_vals = s2_vals * correction2
79
+
80
+ update_vals = s1_vals / (tl.sqrt(s2_vals) + eps)
81
+
82
+ update_norm = update_vals * update_vals
83
+
84
+ elif OPTIMIZER_ID == 5: # ADEMAMIX
85
+ update_norm = s1_vals
86
+
87
+ total_norm = tl.sum(tl.where(mask, update_norm, 0.0))
88
+
89
+ tl.atomic_add(unorm_ptr, total_norm)
90
+
91
+
92
+ @triton.jit
93
+ def _optimizer_precondition_1state_32bit(
94
+ g_ptr,
95
+ p_ptr,
96
+ state1_ptr,
97
+ state2_ptr,
98
+ unorm_ptr,
99
+ beta1: tl.constexpr,
100
+ beta2: tl.constexpr,
101
+ eps: tl.constexpr,
102
+ weight_decay,
103
+ step,
104
+ beta1_step,
105
+ beta2_step,
106
+ lr,
107
+ gnorm_scale: tl.constexpr,
108
+ n_elements,
109
+ OPTIMIZER_ID: tl.constexpr,
110
+ BLOCK_SIZE: tl.constexpr,
111
+ N_PER_TH: tl.constexpr,
112
+ ):
113
+ """Preprocessing optimizer, computing update norm (1-state optimizer)"""
114
+ pid = tl.program_id(axis=0)
115
+ block_start_idx = pid * N_PER_TH
116
+ offsets = block_start_idx * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE * N_PER_TH)
117
+ mask = offsets < n_elements
118
+
119
+ g_vals = tl.load(g_ptr + offsets, mask=mask, other=0.0)
120
+ s1_vals = tl.load(state1_ptr + offsets, mask=mask, other=0.0)
121
+
122
+ g_vals = gnorm_scale * g_vals
123
+
124
+ if OPTIMIZER_ID == 0: # MOMENTUM
125
+ if step == 1:
126
+ # Cast to fp32 to avoid type mismatch: s1_vals is fp32 but g_vals may be fp16.
127
+ s1_vals = g_vals.to(tl.float32)
128
+ else:
129
+ s1_vals = s1_vals * beta1 + g_vals
130
+ update_norm = s1_vals * s1_vals
131
+
132
+ elif OPTIMIZER_ID == 4: # LION
133
+ s1_vals = s1_vals * beta2 + (1.0 - beta2) * g_vals
134
+ update_norm = s1_vals
135
+
136
+ elif OPTIMIZER_ID == 1: # RMSPROP
137
+ s1_vals = s1_vals * beta1 + (1.0 - beta1) * g_vals * g_vals
138
+ update_vals = g_vals / (tl.sqrt(s1_vals) + eps)
139
+ update_norm = update_vals * update_vals
140
+
141
+ elif OPTIMIZER_ID == 2: # ADAGRAD
142
+ s1_vals = s1_vals + g_vals * g_vals
143
+ update_vals = g_vals / (tl.sqrt(s1_vals) + eps)
144
+ update_norm = update_vals * update_vals
145
+
146
+ total_norm = tl.sum(tl.where(mask, update_norm, 0.0))
147
+
148
+ tl.atomic_add(unorm_ptr, total_norm)
149
+
150
+
151
+ @triton.jit
152
+ def _optimizer_update_2state_32bit_triton_kernel(
153
+ g_ptr,
154
+ p_ptr,
155
+ state1_ptr,
156
+ state2_ptr,
157
+ unorm_ptr,
158
+ max_unorm: tl.constexpr,
159
+ param_norm,
160
+ beta1: tl.constexpr,
161
+ beta2: tl.constexpr,
162
+ beta3,
163
+ alpha,
164
+ eps: tl.constexpr,
165
+ weight_decay: tl.constexpr,
166
+ step,
167
+ beta1_step,
168
+ beta2_step,
169
+ lr,
170
+ gnorm_scale: tl.constexpr,
171
+ skip_zeros,
172
+ n_elements,
173
+ OPTIMIZER_ID: tl.constexpr,
174
+ BLOCK_SIZE: tl.constexpr,
175
+ N_PER_TH: tl.constexpr,
176
+ ):
177
+ """2-state optimizer kernel"""
178
+ pid = tl.program_id(axis=0)
179
+ block_start_idx = pid * N_PER_TH
180
+ offsets = block_start_idx * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE * N_PER_TH)
181
+ mask = offsets < n_elements
182
+
183
+ g_vals = tl.load(g_ptr + offsets, mask=mask, other=0.0).to(tl.float32)
184
+ p_vals = tl.load(p_ptr + offsets, mask=mask, other=0.0).to(tl.float32)
185
+ s1_vals = tl.load(state1_ptr + offsets, mask=mask, other=0.0)
186
+ s2_vals = tl.load(state2_ptr + offsets, mask=mask, other=0.0)
187
+
188
+ if OPTIMIZER_ID == 5: # ADEMAMIX
189
+ s3_vals = tl.load(state1_ptr + n_elements + offsets, mask=mask, other=0.0)
190
+
191
+ g_vals = gnorm_scale * g_vals
192
+
193
+ update_scale = 1.0
194
+ if max_unorm > 0.0:
195
+ current_unorm = tl.sqrt(tl.load(unorm_ptr))
196
+ if current_unorm > max_unorm * param_norm:
197
+ update_scale = (max_unorm * param_norm) / current_unorm
198
+
199
+ if OPTIMIZER_ID == 3: # ADAM
200
+ s1_vals = s1_vals * beta1 + (1.0 - beta1) * g_vals
201
+ s2_vals = s2_vals * beta2 + (1.0 - beta2) * g_vals * g_vals
202
+
203
+ correction1 = 1.0 - beta1_step
204
+ correction2 = tl.sqrt(1.0 - beta2_step)
205
+ step_size = -lr * correction2 / correction1
206
+
207
+ if weight_decay > 0.0:
208
+ p_vals = p_vals * (1.0 - lr * weight_decay)
209
+
210
+ update_val = update_scale * step_size * (s1_vals / (tl.sqrt(s2_vals) + eps * correction2))
211
+ p_vals = p_vals + update_val
212
+
213
+ elif OPTIMIZER_ID == 5: # ADEMAMIX
214
+ s1_vals = s1_vals * beta1 + (1.0 - beta1) * g_vals # m1
215
+ s3_vals = s3_vals * beta3 + (1.0 - beta3) * g_vals # m2
216
+ s2_vals = s2_vals * beta2 + (1.0 - beta2) * g_vals * g_vals # nu
217
+
218
+ correction1 = 1.0 - beta1_step
219
+ correction2 = tl.sqrt(1.0 - beta2_step)
220
+
221
+ if weight_decay > 0.0:
222
+ p_vals = p_vals * (1.0 - lr * weight_decay)
223
+
224
+ mixed_momentum = (s1_vals / correction1) + (alpha * s3_vals)
225
+ adaptive_term = (tl.sqrt(s2_vals) / correction2) + eps
226
+ p_vals = p_vals - lr * (mixed_momentum / adaptive_term)
227
+
228
+ tl.store(p_ptr + offsets, p_vals, mask=mask)
229
+ tl.store(state1_ptr + offsets, s1_vals, mask=mask)
230
+ tl.store(state2_ptr + offsets, s2_vals, mask=mask)
231
+
232
+ if OPTIMIZER_ID == 5: # ADEMAMIX
233
+ tl.store(state1_ptr + n_elements + offsets, s3_vals, mask=mask)
234
+
235
+
236
+ @triton.jit
237
+ def _optimizer_update_1state_32bit_triton_kernel(
238
+ g_ptr,
239
+ p_ptr,
240
+ state1_ptr,
241
+ state2_ptr,
242
+ unorm_ptr,
243
+ max_unorm: tl.constexpr,
244
+ param_norm,
245
+ beta1: tl.constexpr,
246
+ beta2: tl.constexpr,
247
+ beta3,
248
+ alpha,
249
+ eps: tl.constexpr,
250
+ weight_decay: tl.constexpr,
251
+ step,
252
+ beta1_step,
253
+ beta2_step,
254
+ lr,
255
+ gnorm_scale: tl.constexpr,
256
+ skip_zeros,
257
+ n_elements,
258
+ OPTIMIZER_ID: tl.constexpr,
259
+ BLOCK_SIZE: tl.constexpr,
260
+ N_PER_TH: tl.constexpr,
261
+ ):
262
+ """1-state optimizer kernel"""
263
+ pid = tl.program_id(axis=0)
264
+ block_start_idx = pid * N_PER_TH
265
+ offsets = block_start_idx * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE * N_PER_TH)
266
+ mask = offsets < n_elements
267
+
268
+ g_vals = tl.load(g_ptr + offsets, mask=mask, other=0.0).to(tl.float32)
269
+ p_vals = tl.load(p_ptr + offsets, mask=mask, other=0.0).to(tl.float32)
270
+ s1_vals = tl.load(state1_ptr + offsets, mask=mask, other=0.0)
271
+
272
+ g_vals = gnorm_scale * g_vals
273
+ # Coupled (L2) weight decay: fold wd into the gradient. This is correct for
274
+ # MOMENTUM/RMSPROP/ADAGRAD, but NOT for LION (id 4), which uses *decoupled*
275
+ # (AdamW-style) weight decay applied to the param directly (see the LION branch
276
+ # below and Chen et al. 2023). LION is intentionally excluded here.
277
+ if OPTIMIZER_ID != 4 and weight_decay > 0.0:
278
+ g_vals = g_vals + p_vals * weight_decay
279
+
280
+ update_scale = 1.0
281
+ if max_unorm > 0.0:
282
+ current_unorm = tl.sqrt(tl.load(unorm_ptr))
283
+ if current_unorm > max_unorm * param_norm + eps:
284
+ update_scale = (max_unorm * param_norm + eps) / current_unorm
285
+
286
+ if OPTIMIZER_ID == 0: # MOMENTUM
287
+ if step == 1:
288
+ s1_vals = g_vals
289
+ else:
290
+ s1_vals = s1_vals * beta1 + g_vals
291
+
292
+ update_val = update_scale * (-lr * s1_vals)
293
+ p_vals = p_vals + update_val
294
+
295
+ elif OPTIMIZER_ID == 4: # LION
296
+ # Lion uses decoupled weight decay: shrink the param directly (p *= 1 - lr*wd)
297
+ # rather than folding wd into the gradient. Matches the 8-bit blockwise kernel,
298
+ # the default/cpu backends, and the Lion paper (Chen et al. 2023).
299
+ if weight_decay > 0.0:
300
+ p_vals = p_vals * (1.0 - lr * weight_decay)
301
+
302
+ momentum_update = s1_vals * beta1 + (1.0 - beta1) * g_vals
303
+ update_val = update_scale * lr * tl.where(momentum_update > 0, 1.0, tl.where(momentum_update < 0, -1.0, 0.0))
304
+ p_vals = p_vals - update_val
305
+
306
+ s1_vals = s1_vals * beta2 + (1.0 - beta2) * g_vals
307
+
308
+ elif OPTIMIZER_ID == 1: # RMSPROP
309
+ s1_vals = s1_vals * beta1 + (1.0 - beta1) * g_vals * g_vals
310
+
311
+ update_val = update_scale * lr * g_vals / (tl.sqrt(s1_vals) + eps)
312
+ p_vals = p_vals - update_val
313
+
314
+ elif OPTIMIZER_ID == 2: # ADAGRAD
315
+ s1_vals = s1_vals + g_vals * g_vals
316
+
317
+ update_val = lr * g_vals / (tl.sqrt(s1_vals) + eps)
318
+ p_vals = p_vals - update_val
319
+
320
+ tl.store(p_ptr + offsets, p_vals, mask=mask)
321
+ tl.store(state1_ptr + offsets, s1_vals, mask=mask)
322
+
323
+
324
+ name2optimizer_32bit_fn = {
325
+ "adam": {
326
+ "preprocess": _optimizer_precondition_2state_32bit,
327
+ "update": _optimizer_update_2state_32bit_triton_kernel,
328
+ },
329
+ "lamb": {
330
+ "preprocess": _optimizer_precondition_2state_32bit,
331
+ "update": _optimizer_update_2state_32bit_triton_kernel,
332
+ },
333
+ "ademamix": {
334
+ "preprocess": _optimizer_precondition_2state_32bit,
335
+ "update": _optimizer_update_2state_32bit_triton_kernel,
336
+ },
337
+ "momentum": {
338
+ "preprocess": _optimizer_precondition_1state_32bit,
339
+ "update": _optimizer_update_1state_32bit_triton_kernel,
340
+ },
341
+ "lars": {
342
+ "preprocess": _optimizer_precondition_1state_32bit,
343
+ "update": _optimizer_update_1state_32bit_triton_kernel,
344
+ },
345
+ "rmsprop": {
346
+ "preprocess": _optimizer_precondition_1state_32bit,
347
+ "update": _optimizer_update_1state_32bit_triton_kernel,
348
+ },
349
+ "adagrad": {
350
+ "preprocess": _optimizer_precondition_1state_32bit,
351
+ "update": _optimizer_update_1state_32bit_triton_kernel,
352
+ },
353
+ "lion": {
354
+ "preprocess": _optimizer_precondition_1state_32bit,
355
+ "update": _optimizer_update_1state_32bit_triton_kernel,
356
+ },
357
+ }
358
+
359
+
360
+ def optimizer_update_32bit_impl(
361
+ optimizer_name: str,
362
+ g: torch.Tensor,
363
+ p: torch.Tensor,
364
+ state1: torch.Tensor,
365
+ state2: Optional[torch.Tensor],
366
+ unorm_vec: Optional[torch.Tensor],
367
+ max_unorm: float,
368
+ param_norm: float,
369
+ beta1: float,
370
+ beta2: float,
371
+ beta3: float,
372
+ alpha: float,
373
+ eps: float,
374
+ weight_decay: float,
375
+ step: int,
376
+ lr: float,
377
+ gnorm_scale: float = 1.0,
378
+ skip_zeros=False,
379
+ ) -> None:
380
+ """
381
+ 32-bit optimizer implemented by Triton
382
+ """
383
+ if skip_zeros:
384
+ raise NotImplementedError("skip_zeros is not supported on XPU yet")
385
+
386
+ BLOCK_SIZE = 256
387
+ N_PER_TH = 1 # Number of blocks processed per thread.
388
+ grid = (triton.cdiv(p.numel(), BLOCK_SIZE * N_PER_TH),)
389
+ optimizer_id = name2optimizer_id[optimizer_name]
390
+ fn_preprocess = name2optimizer_32bit_fn[optimizer_name]["preprocess"]
391
+ fn_update = name2optimizer_32bit_fn[optimizer_name]["update"]
392
+
393
+ # In torch=2.7 on XPU there is an issue with libdevice.pow, leading to an error.
394
+ # For backwards compatibility we precompute the bias correction factors.
395
+ beta1_step = beta1**step
396
+ beta2_step = beta2**step
397
+
398
+ if optimizer_name == "lion":
399
+ fn_update[grid](
400
+ g,
401
+ p,
402
+ state1,
403
+ state2,
404
+ unorm_vec,
405
+ max_unorm,
406
+ param_norm,
407
+ beta1,
408
+ beta2,
409
+ beta3,
410
+ alpha,
411
+ eps,
412
+ weight_decay,
413
+ step,
414
+ beta1_step,
415
+ beta2_step,
416
+ lr,
417
+ gnorm_scale,
418
+ skip_zeros,
419
+ p.numel(),
420
+ optimizer_id,
421
+ BLOCK_SIZE,
422
+ N_PER_TH,
423
+ num_warps=2,
424
+ )
425
+
426
+ if max_unorm > 0.0:
427
+ unorm_vec.zero_()
428
+ fn_preprocess[grid](
429
+ g,
430
+ p,
431
+ state1,
432
+ state2,
433
+ unorm_vec,
434
+ beta1,
435
+ beta2,
436
+ eps,
437
+ weight_decay,
438
+ step,
439
+ beta1_step,
440
+ beta2_step,
441
+ lr,
442
+ gnorm_scale,
443
+ p.numel(),
444
+ optimizer_id,
445
+ BLOCK_SIZE,
446
+ N_PER_TH,
447
+ num_warps=2,
448
+ )
449
+
450
+ else:
451
+ if max_unorm > 0.0:
452
+ unorm_vec.zero_()
453
+ fn_preprocess[grid](
454
+ g,
455
+ p,
456
+ state1,
457
+ state2,
458
+ unorm_vec,
459
+ beta1,
460
+ beta2,
461
+ eps,
462
+ weight_decay,
463
+ step,
464
+ beta1_step,
465
+ beta2_step,
466
+ lr,
467
+ gnorm_scale,
468
+ p.numel(),
469
+ optimizer_id,
470
+ BLOCK_SIZE,
471
+ N_PER_TH,
472
+ num_warps=2,
473
+ )
474
+
475
+ fn_update[grid](
476
+ g,
477
+ p,
478
+ state1,
479
+ state2,
480
+ unorm_vec,
481
+ max_unorm,
482
+ param_norm,
483
+ beta1,
484
+ beta2,
485
+ beta3,
486
+ alpha,
487
+ eps,
488
+ weight_decay,
489
+ step,
490
+ beta1_step,
491
+ beta2_step,
492
+ lr,
493
+ gnorm_scale,
494
+ skip_zeros,
495
+ p.numel(),
496
+ optimizer_id,
497
+ BLOCK_SIZE,
498
+ N_PER_TH,
499
+ num_warps=2,
500
+ )
501
+
502
+
503
+ ###########################################
504
+ # Pure torch implementation for reference #
505
+ ###########################################
506
+
507
+
508
+ @torch.compile
509
+ def _dequantize_blockwise_pytorch(
510
+ A: torch.Tensor,
511
+ absmax: torch.Tensor,
512
+ code: torch.Tensor,
513
+ blocksize: int,
514
+ dtype: torch.dtype,
515
+ ) -> torch.Tensor:
516
+ """
517
+ Pure PyTorch reference implementation for block-wise dequantization.
518
+ """
519
+ if A.numel() == 0:
520
+ return torch.empty_like(A, dtype=dtype)
521
+
522
+ A_flat = A.flatten()
523
+ num_elements = A_flat.numel()
524
+
525
+ dequantized_flat = code.to(A.device)[A_flat.long()].to(dtype)
526
+
527
+ num_blocks = math.ceil(num_elements / blocksize)
528
+ pad_len = num_blocks * blocksize - num_elements
529
+ if pad_len > 0:
530
+ dequantized_flat = torch.nn.functional.pad(dequantized_flat, (0, pad_len))
531
+
532
+ dequantized_blocks = dequantized_flat.reshape(num_blocks, blocksize)
533
+
534
+ rescaled_blocks = dequantized_blocks * absmax.unsqueeze(1).to(dtype)
535
+
536
+ rescaled_flat = rescaled_blocks.flatten()
537
+ if pad_len > 0:
538
+ rescaled_flat = rescaled_flat[:-pad_len]
539
+
540
+ return rescaled_flat.reshape(A.shape)
541
+
542
+
543
+ @torch.compile
544
+ def _quantize_blockwise_pytorch(
545
+ A: torch.Tensor,
546
+ code: torch.Tensor,
547
+ blocksize: int,
548
+ ) -> tuple[torch.Tensor, torch.Tensor]:
549
+ """
550
+ Pure PyTorch reference implementation for block-wise quantization.
551
+ """
552
+ if A.numel() == 0:
553
+ return torch.empty_like(A, dtype=torch.uint8), torch.empty(0, dtype=torch.float32, device=A.device)
554
+
555
+ A_flat = A.flatten()
556
+ num_elements = A_flat.numel()
557
+
558
+ num_blocks = math.ceil(num_elements / blocksize)
559
+
560
+ pad_len = num_blocks * blocksize - num_elements
561
+ if pad_len > 0:
562
+ A_flat = torch.nn.functional.pad(A_flat, (0, pad_len))
563
+
564
+ A_blocks = A_flat.reshape(num_blocks, blocksize)
565
+
566
+ absmax = torch.max(torch.abs(A_blocks), dim=1, keepdim=True)[0]
567
+ absmax[absmax == 0] = 1.0
568
+
569
+ scaled_blocks = A_blocks / absmax
570
+
571
+ # Inefficient but straightforward quantization, takes a lot of memory
572
+ diff = torch.abs(scaled_blocks.unsqueeze(2) - code.to(A.device))
573
+ quantized_indices = torch.argmin(diff, dim=2).to(torch.uint8)
574
+
575
+ quantized_flat = quantized_indices.flatten()
576
+ if pad_len > 0:
577
+ quantized_flat = quantized_flat[:-pad_len]
578
+
579
+ return quantized_flat.reshape(A.shape), absmax.flatten()
580
+
581
+
582
+ # Main updated function
583
+ def optimizer_update_8bit_blockwise_pytorch(
584
+ p: torch.Tensor,
585
+ g: torch.Tensor,
586
+ state1: torch.Tensor,
587
+ state2: Optional[torch.Tensor],
588
+ beta1: float,
589
+ beta2: float,
590
+ beta3: float, # ADEMIX
591
+ alpha: float, # ADEMIX
592
+ eps: float,
593
+ step: int,
594
+ lr: float,
595
+ qmap1: torch.Tensor,
596
+ qmap2: Optional[torch.Tensor],
597
+ absmax1: torch.Tensor,
598
+ absmax2: Optional[torch.Tensor],
599
+ weight_decay: float,
600
+ gnorm_scale: float,
601
+ skip_zeros: bool,
602
+ # ADEMIX
603
+ *,
604
+ optimizer_name: str,
605
+ ) -> None:
606
+ """
607
+ Pure PyTorch implementation of the 8-bit block-wise optimizer update step.
608
+ This version ensures high-precision updates for float16 parameters.
609
+ """
610
+ if skip_zeros:
611
+ raise ValueError("skip_zeros is not supported on XPU yet.")
612
+
613
+ blocksize = 256
614
+
615
+ with torch.no_grad():
616
+ # Dequantize states to perform updates in 32-bit precision
617
+ if optimizer_name == "ademamix" and absmax1.ndim == 2:
618
+ # For AdEMAMix, state1 holds two EMAs, so absmax1 is stacked.
619
+ s1_1_fp32 = _dequantize_blockwise_pytorch(state1[0], absmax1[0], qmap1, blocksize, torch.float32)
620
+ s1_2_fp32 = _dequantize_blockwise_pytorch(state1[1], absmax1[1], qmap1, blocksize, torch.float32)
621
+ state1_fp32 = torch.stack([s1_1_fp32, s1_2_fp32])
622
+ else:
623
+ state1_fp32 = _dequantize_blockwise_pytorch(state1, absmax1, qmap1, blocksize, torch.float32)
624
+
625
+ state2_fp32 = None
626
+ if state2 is not None:
627
+ state2_fp32 = _dequantize_blockwise_pytorch(state2, absmax2, qmap2, blocksize, torch.float32)
628
+
629
+ grad = g.float() * gnorm_scale
630
+
631
+ # Create a 32-bit copy of the parameter for high-precision updates
632
+ p_fp32 = p.data.float()
633
+
634
+ if optimizer_name == "adam":
635
+ state1_fp32.mul_(beta1).add_(grad, alpha=1.0 - beta1)
636
+ state2_fp32.mul_(beta2).addcmul_(grad, grad, value=1.0 - beta2)
637
+
638
+ bias_correction1 = 1.0 - beta1**step
639
+ bias_correction2 = 1.0 - beta2**step
640
+
641
+ denom = (state2_fp32.sqrt() / math.sqrt(bias_correction2)).add_(eps)
642
+
643
+ if weight_decay > 0.0:
644
+ p_fp32.mul_(1.0 - lr * weight_decay)
645
+ p_fp32.addcdiv_(state1_fp32, denom, value=-lr / bias_correction1)
646
+
647
+ elif optimizer_name == "ademamix":
648
+ m1_fp32, m2_fp32 = state1_fp32[0], state1_fp32[1]
649
+ nu_fp32 = state2_fp32
650
+
651
+ m1_fp32.mul_(beta1).add_(grad, alpha=1.0 - beta1)
652
+ m2_fp32.mul_(beta3).add_(grad, alpha=1.0 - beta3)
653
+ nu_fp32.mul_(beta2).addcmul_(grad, grad, value=1.0 - beta2)
654
+
655
+ bias_correction1 = 1.0 - beta1**step
656
+ bias_correction2 = math.sqrt(1.0 - beta2**step)
657
+
658
+ update = (m1_fp32 / bias_correction1 + alpha * m2_fp32) / (nu_fp32.sqrt() / bias_correction2 + eps)
659
+
660
+ if weight_decay > 0.0:
661
+ p_fp32.mul_(1.0 - lr * weight_decay)
662
+
663
+ p_fp32.add_(update, alpha=-lr)
664
+ state1_fp32 = torch.stack([m1_fp32, m2_fp32])
665
+
666
+ elif optimizer_name == "momentum":
667
+ grad.add_(p_fp32, alpha=weight_decay)
668
+ if step == 1:
669
+ state1_fp32.copy_(grad)
670
+ else:
671
+ state1_fp32.mul_(beta1).add_(grad)
672
+ p_fp32.add_(state1_fp32, alpha=-lr)
673
+
674
+ elif optimizer_name == "rmsprop":
675
+ grad.add_(p_fp32, alpha=weight_decay)
676
+ state1_fp32.mul_(beta1).addcmul_(grad, grad, value=1.0 - beta1)
677
+ p_fp32.addcdiv_(grad, state1_fp32.sqrt().add_(eps), value=-lr)
678
+
679
+ elif optimizer_name == "lion":
680
+ if weight_decay > 0.0:
681
+ p_fp32.mul_(1.0 - lr * weight_decay)
682
+
683
+ update_dir = torch.sign(state1_fp32.mul(beta1) + grad.mul(1.0 - beta1))
684
+ p_fp32.add_(update_dir, alpha=-lr)
685
+
686
+ state1_fp32.mul_(beta2).add_(grad, alpha=1.0 - beta2)
687
+
688
+ elif optimizer_name == "adagrad":
689
+ grad.add_(p_fp32, alpha=weight_decay)
690
+ state1_fp32.addcmul_(grad, grad, value=1.0)
691
+ p_fp32.addcdiv_(grad, state1_fp32.sqrt().add_(eps), value=-lr)
692
+
693
+ else:
694
+ raise NotImplementedError(
695
+ f"Pure PyTorch implementation for optimizer '{optimizer_name}' is not available."
696
+ )
697
+
698
+ # Copy the updated 32-bit parameter back to the original tensor
699
+ p.data.copy_(p_fp32)
700
+
701
+ # Re-quantize states and update state tensors in-place
702
+ if optimizer_name == "ademamix":
703
+ new_m1_8bit, new_absmax_m1 = _quantize_blockwise_pytorch(state1_fp32[0], qmap1, blocksize)
704
+ new_m2_8bit, new_absmax_m2 = _quantize_blockwise_pytorch(state1_fp32[1], qmap1, blocksize)
705
+ state1[0].copy_(new_m1_8bit)
706
+ state1[1].copy_(new_m2_8bit)
707
+ absmax1[0].copy_(new_absmax_m1)
708
+ absmax1[1].copy_(new_absmax_m2)
709
+
710
+ new_state2_8bit, new_absmax2 = _quantize_blockwise_pytorch(state2_fp32, qmap2, blocksize)
711
+ state2.copy_(new_state2_8bit)
712
+ absmax2.copy_(new_absmax2)
713
+ else:
714
+ new_state1_8bit, new_absmax1 = _quantize_blockwise_pytorch(state1_fp32, qmap1, blocksize)
715
+ state1.copy_(new_state1_8bit)
716
+ absmax1.copy_(new_absmax1)
717
+
718
+ if state2_fp32 is not None:
719
+ new_state2_8bit, new_absmax2 = _quantize_blockwise_pytorch(state2_fp32, qmap2, blocksize)
720
+ state2.copy_(new_state2_8bit)
721
+ absmax2.copy_(new_absmax2)
722
+
723
+
724
+ #######################################
725
+ # Mixed torch + triton implementation #
726
+ #######################################
727
+
728
+
729
+ # Much more memory efficient due to using triton for quantization/dequantization
730
+ def optimizer_update_8bit_blockwise_triton_quant(
731
+ p: torch.Tensor,
732
+ g: torch.Tensor,
733
+ state1: torch.Tensor,
734
+ state2: Optional[torch.Tensor],
735
+ beta1: float,
736
+ beta2: float,
737
+ beta3: float, # ADEMIX
738
+ alpha: float, # ADEMIX
739
+ eps: float,
740
+ step: int,
741
+ lr: float,
742
+ qmap1: torch.Tensor,
743
+ qmap2: Optional[torch.Tensor],
744
+ absmax1: torch.Tensor,
745
+ absmax2: Optional[torch.Tensor],
746
+ weight_decay: float,
747
+ gnorm_scale: float,
748
+ skip_zeros: bool,
749
+ # ADEMIX
750
+ *,
751
+ optimizer_name: str,
752
+ ) -> None:
753
+ """
754
+ Pure PyTorch implementation of the 8-bit block-wise optimizer update step.
755
+ This version ensures high-precision updates for float16 parameters.
756
+ """
757
+ if skip_zeros and not torch.any(g):
758
+ return
759
+
760
+ blocksize = 256
761
+ grad = g.float() * gnorm_scale
762
+
763
+ with torch.no_grad():
764
+ # Create a 32-bit copy of the parameter for high-precision updates
765
+ p_fp32 = p.data.float()
766
+
767
+ # Dequantize states to perform updates in 32-bit precision
768
+ if optimizer_name == "ademamix" and absmax1.ndim == 2:
769
+ # For AdEMAMix, state1 holds two EMAs, so absmax1 is stacked.
770
+ s1_1_fp32 = dequant_8bit_blockwise(state1[0], absmax1[0], qmap1, blocksize, dtype=torch.float32)
771
+ s1_2_fp32 = dequant_8bit_blockwise(state1[1], absmax1[1], qmap1, blocksize, dtype=torch.float32)
772
+ state1_fp32 = torch.stack([s1_1_fp32, s1_2_fp32])
773
+ else:
774
+ state1_fp32 = dequant_8bit_blockwise(state1, absmax1, qmap1, blocksize, dtype=torch.float32)
775
+
776
+ state2_fp32 = None
777
+ if state2 is not None:
778
+ state2_fp32 = dequant_8bit_blockwise(state2, absmax2, qmap2, blocksize, dtype=torch.float32)
779
+
780
+ # Apply optimizer-specific update logic
781
+ if optimizer_name == "adam":
782
+ if weight_decay > 0.0:
783
+ p_fp32.mul_(1.0 - lr * weight_decay)
784
+
785
+ state1_fp32.mul_(beta1).add_(grad, alpha=1.0 - beta1)
786
+ state2_fp32.mul_(beta2).addcmul_(grad, grad, value=1.0 - beta2)
787
+
788
+ bias_correction1 = 1.0 - beta1**step
789
+ bias_correction2 = 1.0 - beta2**step
790
+
791
+ denom = (state2_fp32.sqrt() / math.sqrt(bias_correction2)).add_(eps)
792
+ p_fp32.addcdiv_(state1_fp32, denom, value=-lr / bias_correction1)
793
+
794
+ elif optimizer_name == "ademamix":
795
+ m1_fp32, m2_fp32 = state1_fp32[0], state1_fp32[1]
796
+ nu_fp32 = state2_fp32
797
+
798
+ m1_fp32.mul_(beta1).add_(grad, alpha=1.0 - beta1)
799
+ m2_fp32.mul_(beta3).add_(grad, alpha=1.0 - beta3)
800
+ nu_fp32.mul_(beta2).addcmul_(grad, grad, value=1.0 - beta2)
801
+
802
+ bias_correction1 = 1.0 - beta1**step
803
+ bias_correction2 = math.sqrt(1.0 - beta2**step)
804
+
805
+ update = (m1_fp32 / bias_correction1 + alpha * m2_fp32) / (nu_fp32.sqrt() / bias_correction2 + eps)
806
+
807
+ if weight_decay > 0.0:
808
+ p_fp32.mul_(1.0 - lr * weight_decay)
809
+
810
+ p_fp32.add_(update, alpha=-lr)
811
+ state1_fp32 = torch.stack([m1_fp32, m2_fp32])
812
+
813
+ elif optimizer_name == "momentum":
814
+ grad.add_(p_fp32, alpha=weight_decay)
815
+ if step == 1:
816
+ state1_fp32.copy_(grad)
817
+ else:
818
+ state1_fp32.mul_(beta1).add_(grad)
819
+ p_fp32.add_(state1_fp32, alpha=-lr)
820
+
821
+ elif optimizer_name == "rmsprop":
822
+ grad.add_(p_fp32, alpha=weight_decay)
823
+ state1_fp32.mul_(beta1).addcmul_(grad, grad, value=1.0 - beta1)
824
+ p_fp32.addcdiv_(grad, state1_fp32.sqrt().add_(eps), value=-lr)
825
+
826
+ elif optimizer_name == "lion":
827
+ if weight_decay > 0.0:
828
+ p_fp32.mul_(1.0 - lr * weight_decay)
829
+
830
+ update_dir = torch.sign(state1_fp32.mul(beta1) + grad.mul(1.0 - beta1))
831
+ p_fp32.add_(update_dir, alpha=-lr)
832
+
833
+ state1_fp32.mul_(beta2).add_(grad, alpha=1.0 - beta2)
834
+
835
+ elif optimizer_name == "adagrad":
836
+ grad.add_(p_fp32, alpha=weight_decay)
837
+ state1_fp32.addcmul_(grad, grad, value=1.0)
838
+ p_fp32.addcdiv_(grad, state1_fp32.sqrt().add_(eps), value=-lr)
839
+
840
+ else:
841
+ raise NotImplementedError(
842
+ f"Pure PyTorch implementation for optimizer '{optimizer_name}' is not available."
843
+ )
844
+
845
+ # Copy the updated 32-bit parameter back to the original tensor
846
+ p.data.copy_(p_fp32)
847
+
848
+ # Re-quantize states and update state tensors in-place
849
+ if optimizer_name == "ademamix":
850
+ new_m1_8bit, new_absmax_m1 = quantize_blockwise_triton(state1_fp32[0], qmap1, blocksize)
851
+ new_m2_8bit, new_absmax_m2 = quantize_blockwise_triton(state1_fp32[1], qmap1, blocksize)
852
+ state1[0].copy_(new_m1_8bit)
853
+ state1[1].copy_(new_m2_8bit)
854
+ absmax1[0].copy_(new_absmax_m1)
855
+ absmax1[1].copy_(new_absmax_m2)
856
+
857
+ new_state2_8bit, new_absmax2 = quantize_blockwise_triton(state2_fp32, qmap2, blocksize)
858
+ state2.copy_(new_state2_8bit)
859
+ absmax2.copy_(new_absmax2)
860
+ else:
861
+ new_state1_8bit, new_absmax1 = quantize_blockwise_triton(state1_fp32, qmap1, blocksize)
862
+ state1.copy_(new_state1_8bit)
863
+ absmax1.copy_(new_absmax1)
864
+
865
+ if state2_fp32 is not None:
866
+ new_state2_8bit, new_absmax2 = quantize_blockwise_triton(state2_fp32, qmap2, blocksize)
867
+ state2.copy_(new_state2_8bit)
868
+ absmax2.copy_(new_absmax2)
869
+
870
+
871
+ #########################
872
+ # Triton implementation #
873
+ #########################
874
+
875
+
876
+ @triton.jit
877
+ def _optimizer_update_1state_8bit_blockwise_triton_kernel(
878
+ # Tensors
879
+ p_ptr,
880
+ g_ptr,
881
+ state1_ptr,
882
+ state2_ptr,
883
+ beta1: tl.constexpr,
884
+ beta2: tl.constexpr,
885
+ beta3,
886
+ alpha,
887
+ eps: tl.constexpr,
888
+ step,
889
+ beta1_step,
890
+ beta2_step,
891
+ lr,
892
+ qmap1_ptr,
893
+ qmap2_ptr,
894
+ absmax1_ptr,
895
+ absmax2_ptr,
896
+ weight_decay,
897
+ gnorm_scale,
898
+ # Meta-parameters
899
+ n_elements,
900
+ BLOCK_SIZE_N: tl.constexpr,
901
+ N_PER_TH: tl.constexpr,
902
+ OPTIMIZER_ID: tl.constexpr,
903
+ ):
904
+ """
905
+ Triton kernel for 8-bit optimizers that use one momentum state.
906
+ Supports: Momentum, RMSprop, Adagrad, Lion.
907
+ """
908
+ # 1. Boilerplate: pid, offsets, mask
909
+ pid = tl.program_id(axis=0)
910
+ block_start_idx = pid * N_PER_TH
911
+ offsets = block_start_idx * BLOCK_SIZE_N + tl.arange(0, BLOCK_SIZE_N * N_PER_TH)
912
+ mask = offsets < n_elements
913
+
914
+ # 2. Load and dequantize tensors
915
+ g = tl.load(g_ptr + offsets, mask=mask, other=0.0).to(tl.float32) * gnorm_scale
916
+ p = tl.load(p_ptr + offsets, mask=mask, other=0.0).to(tl.float32)
917
+ s1 = dequant_8bit_blockwise_kernel_util(state1_ptr, offsets, qmap1_ptr, absmax1_ptr, mask, BLOCK_SIZE_N)
918
+
919
+ # 3. Optimizer-specific updates
920
+ # LION
921
+ if weight_decay > 0.0 and OPTIMIZER_ID == 2:
922
+ p *= 1.0 - lr * weight_decay
923
+ # Apply weight decay for momentum, rmsprop, adagrad
924
+ elif weight_decay > 0.0:
925
+ g += p * weight_decay
926
+
927
+ # Momentum update
928
+ if OPTIMIZER_ID == 0: # MOMENTUM
929
+ if step == 1:
930
+ s1 = g
931
+ else:
932
+ s1 = s1 * beta1 + g
933
+ p -= lr * s1
934
+
935
+ # RMSprop update
936
+ elif OPTIMIZER_ID == 1: # RMSPROP
937
+ s1 = s1 * beta1 + (1.0 - beta1) * g * g
938
+ p -= lr * (g / (tl.sqrt(s1) + eps))
939
+
940
+ # Adagrad update
941
+ elif OPTIMIZER_ID == 2: # ADAGRAD
942
+ s1 += g * g
943
+ p -= lr * (g / (tl.sqrt(s1) + eps))
944
+
945
+ # Lion update
946
+ elif OPTIMIZER_ID == 4: # LION
947
+ val = s1 * beta1 + (1.0 - beta1) * g
948
+ update = tl.where(val > 0.0, 1.0, tl.where(val < 0.0, -1.0, 0.0))
949
+ p -= lr * update
950
+ s1 = s1 * beta2 + (1.0 - beta2) * g
951
+
952
+ # 4. Store updated parameter and requantized state
953
+ tl.store(p_ptr + offsets, p.to(p_ptr.dtype.element_ty), mask=mask)
954
+ s1_codes, new_absmax1 = quantize_8bit_blockwise_kernel_util(s1, qmap1_ptr, 256, BLOCK_SIZE_N, N_PER_TH)
955
+ tl.store(state1_ptr + offsets, s1_codes, mask=mask)
956
+ tl.store(absmax1_ptr + block_start_idx + tl.arange(0, N_PER_TH), new_absmax1)
957
+
958
+
959
+ @triton.jit
960
+ def _optimizer_update_2state_8bit_blockwise_triton_kernel(
961
+ # Tensors
962
+ p_ptr,
963
+ g_ptr,
964
+ state1_ptr,
965
+ state2_ptr,
966
+ beta1: tl.constexpr,
967
+ beta2: tl.constexpr,
968
+ # ademamix changes alpha and beta3
969
+ beta3,
970
+ # ademamix changes alpha and beta3
971
+ alpha,
972
+ eps: tl.constexpr,
973
+ step,
974
+ beta1_step,
975
+ beta2_step,
976
+ lr,
977
+ qmap1_ptr,
978
+ qmap2_ptr,
979
+ absmax1_ptr,
980
+ absmax2_ptr,
981
+ weight_decay: tl.constexpr,
982
+ gnorm_scale: tl.constexpr,
983
+ # Meta-parameters
984
+ n_elements,
985
+ BLOCK_SIZE_N: tl.constexpr,
986
+ N_PER_TH: tl.constexpr,
987
+ OPTIMIZER_ID: tl.constexpr,
988
+ ):
989
+ """
990
+ Triton kernel for 8-bit optimizers that use two momentum states.
991
+ Supports: Adam, AdEMAMix.
992
+ """
993
+ # 1. Boilerplate: pid, offsets, mask
994
+ pid = tl.program_id(axis=0)
995
+ block_start_idx = pid * N_PER_TH
996
+ offsets = block_start_idx * BLOCK_SIZE_N + tl.arange(0, BLOCK_SIZE_N * N_PER_TH)
997
+ mask = offsets < n_elements
998
+
999
+ # 2. Load and dequantize tensors
1000
+ g = tl.load(g_ptr + offsets, mask=mask, other=0.0).to(tl.float32) * gnorm_scale
1001
+ p = tl.load(p_ptr + offsets, mask=mask, other=0.0).to(tl.float32)
1002
+
1003
+ # 3. Optimizer-specific updates
1004
+ if OPTIMIZER_ID == 3: # ADAM
1005
+ s1 = dequant_8bit_blockwise_kernel_util(state1_ptr, offsets, qmap1_ptr, absmax1_ptr, mask, BLOCK_SIZE_N)
1006
+ s2 = dequant_8bit_blockwise_kernel_util(state2_ptr, offsets, qmap2_ptr, absmax2_ptr, mask, BLOCK_SIZE_N)
1007
+
1008
+ s1 = s1 * beta1 + (1.0 - beta1) * g
1009
+ s2 = s2 * beta2 + (1.0 - beta2) * g * g
1010
+
1011
+ # In torch=2.7 on XPU there is an issue with libdevice.pow, leading to an error.
1012
+ # For backwards compatibility we precompute the bias correction factors.
1013
+ # bias_correction1 = 1.0 - libdevice.pow(beta1, step)
1014
+ # bias_correction2 = 1.0 - libdevice.pow(beta2, step)
1015
+ bias_correction1 = 1.0 - beta1_step
1016
+ bias_correction2 = 1.0 - beta2_step
1017
+
1018
+ if weight_decay > 0.0:
1019
+ p *= 1.0 - lr * weight_decay
1020
+
1021
+ denom = tl.sqrt(s2) / tl.sqrt(bias_correction2) + eps
1022
+ p -= (lr / bias_correction1) * (s1 / denom)
1023
+
1024
+ # Store updated parameter
1025
+ tl.store(p_ptr + offsets, p.to(p_ptr.dtype.element_ty), mask=mask)
1026
+
1027
+ # Requantize and store states
1028
+ s1_codes, new_absmax1 = quantize_8bit_blockwise_kernel_util(s1, qmap1_ptr, 256, BLOCK_SIZE_N, N_PER_TH)
1029
+ tl.store(state1_ptr + offsets, s1_codes, mask=mask)
1030
+ tl.store(absmax1_ptr + block_start_idx + tl.arange(0, N_PER_TH), new_absmax1)
1031
+
1032
+ s2_codes, new_absmax2 = quantize_8bit_blockwise_kernel_util(s2, qmap2_ptr, 256, BLOCK_SIZE_N, N_PER_TH)
1033
+ tl.store(state2_ptr + offsets, s2_codes, mask=mask)
1034
+ tl.store(absmax2_ptr + block_start_idx + tl.arange(0, N_PER_TH), new_absmax2)
1035
+
1036
+ elif OPTIMIZER_ID == 5: # ADEMAMIX
1037
+ # AdEMAMix has a stacked state1 (m1, m2) and state2 (nu)
1038
+ m1 = dequant_8bit_blockwise_kernel_util(state1_ptr, offsets, qmap1_ptr, absmax1_ptr, mask, BLOCK_SIZE_N)
1039
+ m2 = dequant_8bit_blockwise_kernel_util(
1040
+ state1_ptr + n_elements,
1041
+ offsets,
1042
+ qmap1_ptr,
1043
+ absmax1_ptr + n_elements // BLOCK_SIZE_N,
1044
+ mask,
1045
+ BLOCK_SIZE_N,
1046
+ )
1047
+ nu = dequant_8bit_blockwise_kernel_util(state2_ptr, offsets, qmap2_ptr, absmax2_ptr, mask, BLOCK_SIZE_N)
1048
+
1049
+ m1 = m1 * beta1 + (1.0 - beta1) * g
1050
+ m2 = m2 * beta3 + (1.0 - beta3) * g
1051
+ nu = nu * beta2 + (1.0 - beta2) * g * g
1052
+
1053
+ # In torch=2.7 on XPU there is an issue with libdevice.pow, leading to an error.
1054
+ # For backwards compatibility we precompute the bias correction factors.
1055
+ # bias_correction1 = 1.0 - libdevice.pow(beta1, step)
1056
+ # bias_correction2 = tl.sqrt(1.0 - libdevice.pow(beta2, step))
1057
+ bias_correction1 = 1.0 - beta1_step
1058
+ bias_correction2 = tl.sqrt(1.0 - beta2_step)
1059
+
1060
+ update = (m1 / bias_correction1 + alpha * m2) / (tl.sqrt(nu) / bias_correction2 + eps)
1061
+
1062
+ if weight_decay > 0.0:
1063
+ p *= 1.0 - lr * weight_decay
1064
+
1065
+ p -= lr * update
1066
+
1067
+ # Store updated parameter
1068
+ tl.store(p_ptr + offsets, p.to(p_ptr.dtype.element_ty), mask=mask)
1069
+
1070
+ # Requantize and store all three states
1071
+ m1_codes, new_absmax_m1 = quantize_8bit_blockwise_kernel_util(m1, qmap1_ptr, 256, BLOCK_SIZE_N, N_PER_TH)
1072
+ tl.store(state1_ptr + offsets, m1_codes, mask=mask)
1073
+ tl.store(absmax1_ptr + block_start_idx + tl.arange(0, N_PER_TH), new_absmax_m1)
1074
+
1075
+ m2_codes, new_absmax_m2 = quantize_8bit_blockwise_kernel_util(m2, qmap1_ptr, 256, BLOCK_SIZE_N, N_PER_TH)
1076
+ tl.store(state1_ptr + n_elements + offsets, m2_codes, mask=mask)
1077
+ tl.store(
1078
+ absmax1_ptr + block_start_idx + tl.arange(0, N_PER_TH) + n_elements // BLOCK_SIZE_N,
1079
+ new_absmax_m2,
1080
+ )
1081
+
1082
+ nu_codes, new_absmax_nu = quantize_8bit_blockwise_kernel_util(nu, qmap2_ptr, 256, BLOCK_SIZE_N, N_PER_TH)
1083
+ tl.store(state2_ptr + offsets, nu_codes, mask=mask)
1084
+ tl.store(absmax2_ptr + block_start_idx + tl.arange(0, N_PER_TH), new_absmax_nu)
1085
+
1086
+
1087
+ name2optimizer_fn = {
1088
+ "momentum": _optimizer_update_1state_8bit_blockwise_triton_kernel,
1089
+ "lars": _optimizer_update_1state_8bit_blockwise_triton_kernel,
1090
+ "rmsprop": _optimizer_update_1state_8bit_blockwise_triton_kernel,
1091
+ "adagrad": _optimizer_update_1state_8bit_blockwise_triton_kernel,
1092
+ "adam": _optimizer_update_2state_8bit_blockwise_triton_kernel,
1093
+ "lamb": _optimizer_update_2state_8bit_blockwise_triton_kernel,
1094
+ "lion": _optimizer_update_1state_8bit_blockwise_triton_kernel,
1095
+ "ademamix": _optimizer_update_2state_8bit_blockwise_triton_kernel,
1096
+ }
1097
+
1098
+
1099
+ def optimizer_update_8bit_blockwise_impl(
1100
+ optimizer_name: str,
1101
+ g: torch.Tensor,
1102
+ p: torch.Tensor,
1103
+ state1: torch.Tensor,
1104
+ state2: Optional[torch.Tensor],
1105
+ beta1: float,
1106
+ beta2: float,
1107
+ beta3: float,
1108
+ alpha: float,
1109
+ eps: float,
1110
+ step: int,
1111
+ lr: float,
1112
+ qmap1: torch.Tensor,
1113
+ qmap2: Optional[torch.Tensor],
1114
+ absmax1: torch.Tensor,
1115
+ absmax2: Optional[torch.Tensor],
1116
+ weight_decay: float = 0.0,
1117
+ gnorm_scale: float = 1.0,
1118
+ skip_zeros=False,
1119
+ ) -> None:
1120
+ if skip_zeros:
1121
+ raise NotImplementedError("skip_zeros is not supported on XPU yet")
1122
+
1123
+ if optimizer_name == "ademamix":
1124
+ # Handle AdEMAMIX's stacked state tensors
1125
+ if state1.dim() < 2 or state1.shape[0] != 2:
1126
+ raise ValueError(
1127
+ f"For ademamix, state1 must be a stacked tensor of shape (2, ...), but got {state1.shape}"
1128
+ )
1129
+ if absmax1.dim() < 2 or absmax1.shape[0] != 2:
1130
+ raise ValueError(
1131
+ f"For ademamix, absmax1 must be a stacked tensor of shape (2, ...), but got {absmax1.shape}"
1132
+ )
1133
+
1134
+ BLOCK_SIZE = 256
1135
+ N_PER_TH = 1 # Number of blocks processed per thread.
1136
+ grid = (triton.cdiv(p.numel(), BLOCK_SIZE * N_PER_TH),)
1137
+ fn = name2optimizer_fn[optimizer_name]
1138
+ optimizer_id = name2optimizer_id[optimizer_name]
1139
+
1140
+ # In torch=2.7 on XPU there is an issue with libdevice.pow, leading to an error.
1141
+ # For backwards compatibility we precompute the bias correction factors.
1142
+ beta1_step = beta1**step
1143
+ beta2_step = beta2**step
1144
+
1145
+ fn[grid](
1146
+ p,
1147
+ g,
1148
+ state1,
1149
+ state2,
1150
+ beta1,
1151
+ beta2,
1152
+ beta3,
1153
+ alpha,
1154
+ eps,
1155
+ step,
1156
+ beta1_step,
1157
+ beta2_step,
1158
+ lr,
1159
+ qmap1,
1160
+ qmap2,
1161
+ absmax1,
1162
+ absmax2,
1163
+ weight_decay,
1164
+ gnorm_scale,
1165
+ p.numel(),
1166
+ BLOCK_SIZE_N=BLOCK_SIZE,
1167
+ N_PER_TH=N_PER_TH,
1168
+ OPTIMIZER_ID=optimizer_id,
1169
+ num_warps=2,
1170
+ )
1171
+
1172
+
1173
+ # optimizer_update_8bit_blockwise_impl = optimizer_update_8bit_blockwise_pytorch
1174
+ # optimizer_update_8bit_blockwise_impl = torch.compile(optimizer_update_8bit_blockwise_pytorch_impl)
1175
+ # optimizer_update_8bit_blockwise_impl = optimizer_update_8bit_blockwise_triton_quant
1176
+ # optimizer_update_8bit_blockwise_impl = torch.compile(optimizer_update_8bit_blockwise_triton_quant)
1177
+ optimizer_update_8bit_blockwise_impl = optimizer_update_8bit_blockwise_impl