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,277 @@
1
+ """MPS backend for bitsandbytes quantization ops.
2
+
3
+ Hub kernels (kernels-community/bitsandbytes-mps) are attempted lazily on
4
+ macOS 26+. On older macOS the hub kernel path is skipped entirely and
5
+ default fallbacks or MPS-specific pure PyTorch fallbacks are used for all ops.
6
+
7
+ Note: not all ops have implementations on the Hub kernels. Those that do not are also
8
+ implemented using pure PyTorch fallbacks.
9
+ """
10
+
11
+ from collections.abc import Sequence
12
+ from math import prod
13
+ import platform
14
+ from typing import Optional
15
+
16
+ import torch
17
+
18
+ from ..._ops import register_kernel
19
+ from ..default.ops import (
20
+ _dequantize_4bit_compute,
21
+ _get_4bit_quantize_bounds,
22
+ _try_torch_compile,
23
+ )
24
+ from ..utils import _get_4bit_code
25
+
26
+ _QUANT_MAP = {"fp4": 1, "nf4": 2}
27
+
28
+ _kernel = None
29
+
30
+ _macos_major = int(platform.mac_ver()[0].split(".")[0]) if platform.mac_ver()[0] else 0
31
+
32
+ # Pre-set to True on macOS < 26 so _get_kernel() never attempts the import.
33
+ _kernel_load_failed = _macos_major < 26
34
+
35
+
36
+ def _get_kernel():
37
+ global _kernel, _kernel_load_failed
38
+ if _kernel_load_failed:
39
+ return None
40
+ if _kernel is not None:
41
+ return _kernel
42
+ try:
43
+ from kernels import get_kernel
44
+
45
+ _kernel = get_kernel("kernels-community/bitsandbytes-mps", version=1)
46
+ except Exception:
47
+ _kernel_load_failed = True
48
+ return None
49
+ return _kernel
50
+
51
+
52
+ @_try_torch_compile(dynamic=True)
53
+ def _quantize_blockwise_compute(
54
+ A_flat: torch.Tensor, code: torch.Tensor, blocksize: int
55
+ ) -> tuple[torch.Tensor, torch.Tensor]:
56
+ """
57
+ On torch <= 2.12, torch.bucketize does not perform well.
58
+ Implements blockwise quantization using a binary search instead of using the default.
59
+ """
60
+ n = A_flat.numel()
61
+ rem = n % blocksize
62
+ full = n - rem
63
+ blocks = full // blocksize
64
+ A_com = A_flat[:full].reshape(blocks, blocksize)
65
+ absmax = A_com.abs().max(dim=-1)[0]
66
+ scaled = torch.clamp(A_com * (1.0 / absmax.clamp(min=1e-38).view(-1, 1)), -1, 1).reshape(-1)
67
+ if rem:
68
+ am = A_flat[full:].abs().max().clamp(min=1e-38)
69
+ absmax = torch.cat([absmax, am.unsqueeze(0)])
70
+ scaled = torch.cat([scaled, torch.clamp(A_flat[full:] / am, -1, 1)])
71
+ bounds = (code[:-1] + code[1:]) / 2
72
+ n_bounds = bounds.shape[0]
73
+ n_iters = n_bounds.bit_length()
74
+ lo = torch.zeros(scaled.shape, dtype=torch.int16, device=scaled.device)
75
+ hi = torch.full(scaled.shape, n_bounds, dtype=torch.int16, device=scaled.device)
76
+ for _ in range(n_iters):
77
+ mid = (lo + hi) >> 1
78
+ val = bounds[mid.to(torch.int64)]
79
+ lo = torch.where(val < scaled, (mid + 1).to(torch.int16), lo)
80
+ hi = torch.where(val >= scaled, mid, hi)
81
+ return lo.to(torch.uint8), absmax
82
+
83
+
84
+ @register_kernel("bitsandbytes::quantize_blockwise", "mps")
85
+ def _(A: torch.Tensor, code: torch.Tensor, blocksize: int) -> tuple[torch.Tensor, torch.Tensor]:
86
+ q, absmax = _quantize_blockwise_compute(A.reshape(-1).float(), code.float(), blocksize)
87
+ return q.reshape(A.shape), absmax
88
+
89
+
90
+ @_try_torch_compile(dynamic=True)
91
+ def _quantize_4bit_compute(
92
+ A_flat: torch.Tensor,
93
+ blocksize: int,
94
+ bounds: torch.Tensor,
95
+ order: torch.Tensor,
96
+ nf4: bool,
97
+ ) -> tuple[torch.Tensor, torch.Tensor]:
98
+ n = A_flat.numel()
99
+ rem = n % blocksize
100
+ full = n - rem
101
+ blocks = full // blocksize
102
+ A_com = A_flat[:full].reshape(blocks, blocksize)
103
+ absmax = A_com.abs().max(dim=-1)[0]
104
+ scaled = torch.clamp(A_com * (1.0 / absmax.clamp(min=1e-38).view(-1, 1)), -1, 1).reshape(-1)
105
+ if rem:
106
+ am = A_flat[full:].abs().max().clamp(min=1e-38)
107
+ absmax = torch.cat([absmax, am.unsqueeze(0)])
108
+ scaled = torch.cat([scaled, torch.clamp(A_flat[full:] / am, -1, 1)])
109
+ if scaled.numel() % 2:
110
+ scaled = torch.nn.functional.pad(scaled, (0, 1))
111
+ idx = torch.zeros(scaled.shape, dtype=torch.int8, device=scaled.device)
112
+ for b in bounds:
113
+ idx = idx + (scaled > b).to(torch.int8)
114
+ if not nf4:
115
+ idx = order[idx.to(torch.int32)]
116
+ q8 = idx.to(torch.uint8)
117
+ return (q8[::2] << 4) | q8[1::2], absmax
118
+
119
+
120
+ def _quantize_4bit_fallback(
121
+ A: torch.Tensor, blocksize: int, quant_type: str, quant_storage: torch.dtype
122
+ ) -> tuple[torch.Tensor, torch.Tensor]:
123
+ bounds, order = _get_4bit_quantize_bounds(quant_type, A.device)
124
+ packed, absmax = _quantize_4bit_compute(A.reshape(-1).float(), blocksize, bounds, order, quant_type == "nf4")
125
+ packed = packed.unsqueeze(1)
126
+ if quant_storage != torch.uint8:
127
+ packed = packed.squeeze().view(quant_storage).unsqueeze(1)
128
+ return packed, absmax
129
+
130
+
131
+ @register_kernel("bitsandbytes::quantize_4bit", "mps")
132
+ def _(
133
+ A: torch.Tensor,
134
+ blocksize: int,
135
+ quant_type: str,
136
+ quant_storage: torch.dtype,
137
+ ) -> tuple[torch.Tensor, torch.Tensor]:
138
+ if blocksize in (64, 128, 256, 512) and (k := _get_kernel()) is not None:
139
+ packed, absmax = k.quantize_4bit(A.contiguous(), blocksize, _QUANT_MAP[quant_type])
140
+ packed = packed.view(quant_storage).unsqueeze(1)
141
+ return packed, absmax
142
+ return _quantize_4bit_fallback(A, blocksize, quant_type, quant_storage)
143
+
144
+
145
+ def _dequantize_4bit_impl(
146
+ A: torch.Tensor,
147
+ absmax: torch.Tensor,
148
+ blocksize: int,
149
+ quant_type: str,
150
+ shape: Sequence[int],
151
+ dtype: torch.dtype,
152
+ ) -> torch.Tensor:
153
+ if A.dtype != torch.uint8:
154
+ A = A.view(torch.uint8)
155
+
156
+ # Use HF Hub kernel when supported.
157
+ if blocksize in (64, 128, 256, 512) and (k := _get_kernel()) is not None:
158
+ numel = prod(shape)
159
+ out = k.dequantize_4bit(A, absmax, blocksize, _QUANT_MAP[quant_type], numel, dtype)
160
+ return out.reshape(shape)
161
+
162
+ # Fallback to implementation from default backend.
163
+ code = _get_4bit_code(quant_type, A.device)
164
+ return _dequantize_4bit_compute(A.reshape(-1), absmax, code, blocksize, shape, dtype)
165
+
166
+
167
+ @register_kernel("bitsandbytes::dequantize_4bit", "mps")
168
+ def _(
169
+ A: torch.Tensor,
170
+ absmax: torch.Tensor,
171
+ blocksize: int,
172
+ quant_type: str,
173
+ shape: Sequence[int],
174
+ dtype: torch.dtype,
175
+ ) -> torch.Tensor:
176
+ return _dequantize_4bit_impl(A, absmax, blocksize, quant_type, shape, dtype)
177
+
178
+
179
+ @register_kernel("bitsandbytes::dequantize_4bit.out", "mps")
180
+ def _(
181
+ A: torch.Tensor,
182
+ absmax: torch.Tensor,
183
+ blocksize: int,
184
+ quant_type: str,
185
+ shape: Sequence[int],
186
+ dtype: torch.dtype,
187
+ out: torch.Tensor,
188
+ ) -> None:
189
+ result = _dequantize_4bit_impl(A, absmax, blocksize, quant_type, shape, dtype)
190
+ out.copy_(result)
191
+
192
+
193
+ def _gemv_4bit_impl(
194
+ A: torch.Tensor,
195
+ B: torch.Tensor,
196
+ shapeB: Sequence[int],
197
+ absmax: torch.Tensor,
198
+ code: torch.Tensor,
199
+ blocksize: int,
200
+ ) -> torch.Tensor:
201
+ if blocksize in (64, 128, 256) and (k := _get_kernel()) is not None:
202
+ if B.dtype != torch.uint8:
203
+ B = B.view(torch.uint8)
204
+
205
+ output_features = shapeB[0]
206
+ quant_type_int = _QUANT_MAP["fp4"] if code[1] > 0 else _QUANT_MAP["nf4"]
207
+
208
+ return k.gemv_4bit(A, B, absmax, output_features, blocksize, quant_type_int)
209
+
210
+ quant_type = "fp4" if code[1] > 0 else "nf4"
211
+ B_dq = _dequantize_4bit_impl(B, absmax, blocksize, quant_type, shapeB, A.dtype)
212
+ return torch.nn.functional.linear(A, B_dq)
213
+
214
+
215
+ @register_kernel("bitsandbytes::gemv_4bit", "mps")
216
+ def _(
217
+ A: torch.Tensor,
218
+ B: torch.Tensor,
219
+ shapeB: Sequence[int],
220
+ absmax: torch.Tensor,
221
+ code: torch.Tensor,
222
+ blocksize: int,
223
+ ) -> torch.Tensor:
224
+ return _gemv_4bit_impl(A, B, shapeB, absmax, code, blocksize)
225
+
226
+
227
+ @register_kernel("bitsandbytes::gemv_4bit.out", "mps")
228
+ def _(
229
+ A: torch.Tensor,
230
+ B: torch.Tensor,
231
+ shapeB: Sequence[int],
232
+ absmax: torch.Tensor,
233
+ code: torch.Tensor,
234
+ blocksize: int,
235
+ out: torch.Tensor,
236
+ ) -> None:
237
+ result = _gemv_4bit_impl(A, B, shapeB, absmax, code, blocksize)
238
+ out.copy_(result)
239
+
240
+
241
+ @register_kernel("bitsandbytes::gemm_4bit", "mps")
242
+ def _(
243
+ A: torch.Tensor,
244
+ B: torch.Tensor,
245
+ shapeB: Sequence[int],
246
+ absmax: torch.Tensor,
247
+ blocksize: int,
248
+ quant_type: str,
249
+ bias: Optional[torch.Tensor] = None,
250
+ absmax_8bit: Optional[torch.Tensor] = None,
251
+ absmax_code: Optional[torch.Tensor] = None,
252
+ absmax_offset: Optional[torch.Tensor] = None,
253
+ ) -> torch.Tensor:
254
+ K = A.shape[-1]
255
+ M = A.numel() // K
256
+ N = shapeB[0]
257
+
258
+ # For nested absmax, we don't have a fused implementation yet.
259
+ # Dequantize the absmax values first.
260
+ if absmax_8bit is not None:
261
+ absmax = (
262
+ torch.ops.bitsandbytes.dequantize_blockwise.default(absmax_8bit, absmax, absmax_code, 256, torch.float32)
263
+ + absmax_offset
264
+ )
265
+
266
+ # Use HF Hub kernel when supported for GEMV.
267
+ if M == 1 and blocksize in (64, 128, 256) and (k := _get_kernel()) is not None:
268
+ if B.dtype != torch.uint8:
269
+ B = B.view(torch.uint8)
270
+ result = k.gemv_4bit(A, B, absmax.view(N, -1), N, blocksize, _QUANT_MAP[quant_type])
271
+ if bias is not None:
272
+ result = result + bias
273
+ return result
274
+
275
+ # Fallback: dequantize + linear.
276
+ B_dq = _dequantize_4bit_impl(B, absmax, blocksize, quant_type, shapeB, A.dtype)
277
+ return torch.nn.functional.linear(A, B_dq, bias)
File without changes