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.
- bitsandbytes/__init__.py +78 -0
- bitsandbytes/__main__.py +4 -0
- bitsandbytes/_ops.py +510 -0
- bitsandbytes/autograd/__init__.py +0 -0
- bitsandbytes/autograd/_functions.py +491 -0
- bitsandbytes/backends/__init__.py +0 -0
- bitsandbytes/backends/cpu/__init__.py +0 -0
- bitsandbytes/backends/cpu/ops.py +580 -0
- bitsandbytes/backends/cuda/__init__.py +0 -0
- bitsandbytes/backends/cuda/ops.py +1199 -0
- bitsandbytes/backends/default/__init__.py +0 -0
- bitsandbytes/backends/default/ops.py +632 -0
- bitsandbytes/backends/hpu/__init__.py +0 -0
- bitsandbytes/backends/hpu/ops.py +53 -0
- bitsandbytes/backends/mps/__init__.py +0 -0
- bitsandbytes/backends/mps/ops.py +277 -0
- bitsandbytes/backends/triton/__init__.py +0 -0
- bitsandbytes/backends/triton/kernels_4bit.py +577 -0
- bitsandbytes/backends/triton/kernels_8bit_quant.py +195 -0
- bitsandbytes/backends/triton/kernels_optim.py +1177 -0
- bitsandbytes/backends/triton/ops.py +304 -0
- bitsandbytes/backends/utils.py +94 -0
- bitsandbytes/backends/xpu/__init__.py +0 -0
- bitsandbytes/backends/xpu/ops.py +305 -0
- bitsandbytes/cextension.py +405 -0
- bitsandbytes/consts.py +12 -0
- bitsandbytes/cuda_specs.py +111 -0
- bitsandbytes/diagnostics/__init__.py +0 -0
- bitsandbytes/diagnostics/cuda.py +193 -0
- bitsandbytes/diagnostics/main.py +134 -0
- bitsandbytes/diagnostics/utils.py +12 -0
- bitsandbytes/functional.py +1810 -0
- bitsandbytes/libbitsandbytes_cpu.dll +0 -0
- bitsandbytes/nn/__init__.py +19 -0
- bitsandbytes/nn/modules.py +1220 -0
- bitsandbytes/nn/parametrize.py +206 -0
- bitsandbytes/optim/__init__.py +22 -0
- bitsandbytes/optim/adagrad.py +187 -0
- bitsandbytes/optim/adam.py +346 -0
- bitsandbytes/optim/adamw.py +337 -0
- bitsandbytes/optim/ademamix.py +410 -0
- bitsandbytes/optim/lamb.py +190 -0
- bitsandbytes/optim/lars.py +259 -0
- bitsandbytes/optim/lion.py +266 -0
- bitsandbytes/optim/optimizer.py +756 -0
- bitsandbytes/optim/rmsprop.py +170 -0
- bitsandbytes/optim/sgd.py +152 -0
- bitsandbytes/py.typed +0 -0
- bitsandbytes/utils.py +208 -0
- bitsandbytes-0.50.0.dist-info/METADATA +288 -0
- bitsandbytes-0.50.0.dist-info/RECORD +54 -0
- bitsandbytes-0.50.0.dist-info/WHEEL +5 -0
- bitsandbytes-0.50.0.dist-info/licenses/LICENSE +21 -0
- 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
|