compressed-tensors 0.10.3a20250716__py3-none-any.whl → 0.10.3a20250721__py3-none-any.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.
@@ -111,11 +111,18 @@ def dequantize(
111
111
  elif scale.ndim == 2:
112
112
  if scale.shape[1] == 1:
113
113
  args = QuantizationArgs(strategy=QuantizationStrategy.CHANNEL)
114
- else:
114
+ # Scale height matches input or is 1 -> group quantization across columns
115
+ #
116
+ # Example 1: scale.shape[0] == 1
117
+ # x_q: (4, 8), scale: (1, 4) -> 2 columns per group
118
+ #
119
+ # Example 2: scale.shape[0] == x_q.shape[0]
120
+ # x_q: (4, 8), scale: (4, 4) -> 2 elements per group (per row)
121
+ elif (scale.shape[0] == 1) or (scale.shape[0] == x_q.shape[0]):
115
122
  group_size = int(x_q.shape[1] / scale.shape[1])
116
- args = QuantizationArgs(
117
- strategy=QuantizationStrategy.GROUP, group_size=group_size
118
- )
123
+ args = QuantizationArgs(strategy=QuantizationStrategy.GROUP, group_size=group_size)
124
+ else:
125
+ args = QuantizationArgs(strategy=QuantizationStrategy.BLOCK, block_structure=scale.shape)
119
126
  else:
120
127
  raise ValueError(
121
128
  f"Could not infer a quantization strategy from scale with {scale.ndim} "
@@ -189,7 +196,63 @@ def _process_quantization(
189
196
  q_min, q_max = calculate_range(args, x.device)
190
197
  group_size = args.group_size
191
198
 
192
- if args.strategy in (QuantizationStrategy.GROUP, QuantizationStrategy.TENSOR_GROUP):
199
+ # blockwise FP8: quantize per 2D block, supports block_structure for static block quant
200
+ if args.strategy == QuantizationStrategy.BLOCK:
201
+ original_shape = x.shape
202
+ rows, cols = x.shape[-2], x.shape[-1]
203
+ block_height, block_width = args.block_structure
204
+
205
+ # Ensure exact division (tensor dimensions must be divisible by block size)
206
+ if rows % block_height != 0:
207
+ raise ValueError(
208
+ f"Tensor height {rows} is not divisible by block_height {block_height}. "
209
+ f"Block quantization requires exact division."
210
+ )
211
+ if cols % block_width != 0:
212
+ raise ValueError(
213
+ f"Tensor width {cols} is not divisible by block_width {block_width}. "
214
+ f"Block quantization requires exact division."
215
+ )
216
+
217
+ # reshape into blocks and transpose to make each block contiguous
218
+ num_rows_blocks = rows // block_height
219
+ num_cols_blocks = cols // block_width
220
+ x_blocks = x.reshape(
221
+ num_rows_blocks,
222
+ block_height,
223
+ num_cols_blocks,
224
+ block_width,
225
+ ).transpose(1, 2)
226
+
227
+ # expand scale/zero_point for blocks
228
+ sb = scale.unsqueeze(-1).unsqueeze(-1)
229
+ zb = zero_point.unsqueeze(-1).unsqueeze(-1) if zero_point is not None else None
230
+ if do_quantize:
231
+ # quantize blocks
232
+ x_blocks = _quantize(
233
+ x=x_blocks,
234
+ scale=sb,
235
+ zero_point=zb,
236
+ q_min=q_min,
237
+ q_max=q_max,
238
+ args=args,
239
+ dtype=dtype,
240
+ global_scale=global_scale,
241
+ )
242
+ if do_dequantize:
243
+ # dequantize blocks
244
+ x_blocks = _dequantize(
245
+ x_q=x_blocks,
246
+ scale=sb,
247
+ zero_point=zb,
248
+ global_scale=global_scale,
249
+ )
250
+ # restore original shape
251
+ output = x_blocks.transpose(1, 2).reshape(original_shape)
252
+ elif args.strategy in (
253
+ QuantizationStrategy.GROUP,
254
+ QuantizationStrategy.TENSOR_GROUP,
255
+ ):
193
256
  n_dims = x.shape
194
257
  if len(n_dims) > 2:
195
258
  x = x.squeeze(0)
@@ -14,7 +14,7 @@
14
14
 
15
15
  import warnings
16
16
  from enum import Enum
17
- from typing import Any, Dict, Optional, Union
17
+ from typing import Any, Dict, List, Optional, Union
18
18
 
19
19
  import torch
20
20
  from compressed_tensors.utils import Aliasable
@@ -153,8 +153,8 @@ class QuantizationArgs(BaseModel, use_enum_values=True):
153
153
  :param symmetric: whether or not quantization scale is symmetric about zero-point
154
154
  :param strategy: string id determining the scope of scale/zero-point to apply
155
155
  :param group_size: group length to use for the group strategy
156
- :param block_structure: 2d block structure to use for the block strategy, must be
157
- of the format "2x4", "8x16", etc.
156
+ :param block_structure: 2d block structure to use for the block strategy; must be
157
+ a list of two ints [rows, cols] like [128, 128].
158
158
  :param dynamic: set True to perform dynamic quantization - values will not be
159
159
  calibrated during calibration phase, instead during inference new quantization
160
160
  ranges will be observed with every sample. Defaults to False for static
@@ -169,7 +169,7 @@ class QuantizationArgs(BaseModel, use_enum_values=True):
169
169
  symmetric: bool = True
170
170
  group_size: Optional[int] = None
171
171
  strategy: Optional[QuantizationStrategy] = None
172
- block_structure: Optional[str] = None
172
+ block_structure: Optional[List[int]] = None
173
173
  dynamic: Union[DynamicType, bool] = False
174
174
  actorder: Union[ActivationOrdering, bool, None] = None
175
175
  observer: Optional[str] = Field(
@@ -207,6 +207,28 @@ class QuantizationArgs(BaseModel, use_enum_values=True):
207
207
 
208
208
  return value
209
209
 
210
+ @field_validator("block_structure", mode="before")
211
+ def validate_block_structure(cls, value) -> Optional[List[int]]:
212
+ if value is None:
213
+ return value
214
+ # For backward compatibility, allow string format "2x4", "8x16", etc.
215
+ if isinstance(value, str):
216
+ try:
217
+ return [int(x) for x in value.split("x")]
218
+ except Exception:
219
+ raise ValueError(
220
+ f"Invalid block_structure '{value}'. Must be a list of two ints [rows, cols]."
221
+ )
222
+ if isinstance(value, (list, tuple)):
223
+ if len(value) != 2 or not all(isinstance(v, int) for v in value):
224
+ raise ValueError(
225
+ f"Invalid block_structure '{value}'. Must be a list of two ints [rows, cols]."
226
+ )
227
+ return list(value)
228
+ raise ValueError(
229
+ f"Invalid block_structure '{value}'. Must be a list of two ints [rows, cols]."
230
+ )
231
+
210
232
  @field_validator("strategy", mode="before")
211
233
  def validate_strategy(cls, value) -> Union[QuantizationStrategy, None]:
212
234
  if isinstance(value, str):
@@ -277,14 +299,15 @@ class QuantizationArgs(BaseModel, use_enum_values=True):
277
299
 
278
300
  # infer observer w.r.t. dynamic
279
301
  if dynamic:
280
- if strategy not in (
302
+ supported_strategies = (
281
303
  QuantizationStrategy.TOKEN,
282
304
  QuantizationStrategy.TENSOR,
283
305
  QuantizationStrategy.TENSOR_GROUP,
284
- ):
306
+ QuantizationStrategy.GROUP,
307
+ )
308
+ if strategy not in supported_strategies:
285
309
  raise ValueError(
286
- f"One of {(QuantizationStrategy.TOKEN, QuantizationStrategy.TENSOR, QuantizationStrategy.TENSOR_GROUP)} "
287
- "must be used for dynamic quantization",
310
+ f"One of {supported_strategies} must be used for dynamic quantization"
288
311
  )
289
312
 
290
313
  if (
@@ -12,6 +12,7 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ import warnings
15
16
  from copy import deepcopy
16
17
  from typing import Any, Dict, List, Optional
17
18
 
@@ -52,6 +53,7 @@ class QuantizationScheme(BaseModel):
52
53
  def validate_model_after(model: "QuantizationScheme") -> "QuantizationScheme":
53
54
  inputs = model.input_activations
54
55
  outputs = model.output_activations
56
+ weights = model.weights
55
57
 
56
58
  if inputs is not None:
57
59
  if inputs.actorder is not None:
@@ -61,6 +63,21 @@ class QuantizationScheme(BaseModel):
61
63
  if outputs.actorder is not None:
62
64
  raise ValueError("Cannot apply actorder to output activations")
63
65
 
66
+ if (
67
+ inputs and weights
68
+ and weights.strategy == QuantizationStrategy.GROUP
69
+ and inputs.strategy == QuantizationStrategy.GROUP
70
+ and weights.group_size != inputs.group_size
71
+ ):
72
+ warnings.warn(
73
+ "Using GROUP strategy for both weights and input_activations "
74
+ f"with different group sizes ({weights.group_size} vs {inputs.group_size}) "
75
+ "may complicate fused kernel implementations. Consider using "
76
+ "TENSOR_GROUP strategy for both or matching group sizes.",
77
+ UserWarning,
78
+ stacklevel=2
79
+ )
80
+
64
81
  return model
65
82
 
66
83
 
@@ -243,6 +260,29 @@ FP8_DYNAMIC = dict(
243
260
  ),
244
261
  )
245
262
 
263
+ # Block‐wise FP8 (deepseekv3-style quantization):
264
+ # static 128x128 per‐block weights and
265
+ # dynamic per‐token‐group activations
266
+ FP8_BLOCK = dict(
267
+ weights=QuantizationArgs(
268
+ num_bits=8,
269
+ type=QuantizationType.FLOAT,
270
+ strategy=QuantizationStrategy.BLOCK,
271
+ symmetric=True,
272
+ dynamic=False,
273
+ block_structure=[128, 128],
274
+ ),
275
+ input_activations=QuantizationArgs(
276
+ num_bits=8,
277
+ type=QuantizationType.FLOAT,
278
+ strategy=QuantizationStrategy.GROUP,
279
+ symmetric=True,
280
+ dynamic=True,
281
+ observer=None,
282
+ group_size=128,
283
+ ),
284
+ )
285
+
246
286
  PRESET_SCHEMES = {
247
287
  # Unquantized (no-op)
248
288
  "UNQUANTIZED": UNQUANTIZED,
@@ -257,6 +297,7 @@ PRESET_SCHEMES = {
257
297
  # Float weight and activation schemes
258
298
  "FP8": FP8,
259
299
  "FP8_DYNAMIC": FP8_DYNAMIC,
300
+ "FP8_BLOCK": FP8_BLOCK,
260
301
  "NVFP4A16": NVFP4A16,
261
302
  "NVFP4": NVFP4,
262
303
  }
@@ -171,7 +171,10 @@ def compute_dynamic_scales_and_zp(
171
171
  reduce_dims = tuple(idx for idx in range(value.ndim) if idx not in dim)
172
172
  elif args.strategy == QuantizationStrategy.TENSOR:
173
173
  reduce_dims = None
174
- elif args.strategy == QuantizationStrategy.TENSOR_GROUP:
174
+ elif args.strategy in (
175
+ QuantizationStrategy.TENSOR_GROUP,
176
+ QuantizationStrategy.GROUP,
177
+ ):
175
178
  if len(value.shape) > 2:
176
179
  value = value.squeeze(0)
177
180
 
@@ -187,9 +190,15 @@ def compute_dynamic_scales_and_zp(
187
190
  ),
188
191
  )
189
192
  else:
193
+ supported_strategies = (
194
+ QuantizationStrategy.TOKEN,
195
+ QuantizationStrategy.TENSOR,
196
+ QuantizationStrategy.TENSOR_GROUP,
197
+ QuantizationStrategy.GROUP,
198
+ )
190
199
  raise ValueError(
191
200
  "Dynamic quantization is only supported for ",
192
- f"{QuantizationStrategy.TOKEN, QuantizationStrategy.TENSOR, QuantizationStrategy.TENSOR_GROUP}",
201
+ f"{supported_strategies}",
193
202
  )
194
203
 
195
204
  if not reduce_dims:
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.10.3.a20250716'
20
+ __version__ = version = '0.10.3.a20250721'
21
21
  __version_tuple__ = version_tuple = (0, 10, 3)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: compressed-tensors
3
- Version: 0.10.3a20250716
3
+ Version: 0.10.3a20250721
4
4
  Summary: Library for utilization of compressed safetensors of neural network models
5
5
  Home-page: https://github.com/neuralmagic/compressed-tensors
6
6
  Author: Neuralmagic, Inc.
@@ -1,6 +1,6 @@
1
1
  compressed_tensors/__init__.py,sha256=UtKmifNeBCSE2TZSAfduVNNzHY-3V7bLjZ7n7RuXLOE,812
2
2
  compressed_tensors/base.py,sha256=73HYH7HY7O2roC89yG_piPFnZwrBfn_i7HmKl90SKc0,875
3
- compressed_tensors/version.py,sha256=DSQFEQZQHt-pmBpCtXPX8Vc2dUeG5ueZMmYLIwONR1c,523
3
+ compressed_tensors/version.py,sha256=d45NnXPlUzem78cnmH1dUdhwbxzgKieO5KjFyfBwSZA,523
4
4
  compressed_tensors/compressors/__init__.py,sha256=smSygTSfcfuujRrAXDc6uZm4L_ccV1tWZewqVnOb4lM,825
5
5
  compressed_tensors/compressors/base.py,sha256=nvWsv4xEw1Tkxkxth6TmHplDYXfBeP22xWxOsZERyDY,7204
6
6
  compressed_tensors/compressors/helpers.py,sha256=OK6qxX9j3bHwF9JfIYSGMgBJe2PWjlTA3byXKCJaTIQ,5431
@@ -26,17 +26,17 @@ compressed_tensors/config/sparse_bitmask.py,sha256=pZUboRNZTu6NajGOQEFExoPknak5y
26
26
  compressed_tensors/linear/__init__.py,sha256=fH6rjBYAxuwrTzBTlTjTgCYNyh6TCvCqajCz4Im4YrA,617
27
27
  compressed_tensors/linear/compressed_linear.py,sha256=1yo9RyjA0aQ--iuIknFfcSorJn43Mn4CoV-q4JlTJ_o,4052
28
28
  compressed_tensors/quantization/__init__.py,sha256=83J5bPB7PavN2TfCoW7_vEDhfYpm4TDrqYO9vdSQ5bk,760
29
- compressed_tensors/quantization/quant_args.py,sha256=2OpiiSdl4KidzNmjx7J8UlQoAYmt5k5GdXv_73ELw0A,11823
29
+ compressed_tensors/quantization/quant_args.py,sha256=yKTj_4lAy_pnXeTCyUADpyz2qAzJXYJU2P03NF_TP68,12835
30
30
  compressed_tensors/quantization/quant_config.py,sha256=w6sEEZGVGIF0Ub2r_cqRfZwbkBT8WzfY3ug52olmjGY,10049
31
- compressed_tensors/quantization/quant_scheme.py,sha256=IDWa1GWUbUdWCo8j78Jz6svYF5hLz89J2PVYWBBnXRc,7102
31
+ compressed_tensors/quantization/quant_scheme.py,sha256=qApRLsPxELe5S2qFv8OVyAZ5TpRL7gT35i4U3c9PAwI,8461
32
32
  compressed_tensors/quantization/lifecycle/__init__.py,sha256=_uItzFWusyV74Zco_pHLOTdE9a83cL-R-ZdyQrBkIyw,772
33
33
  compressed_tensors/quantization/lifecycle/apply.py,sha256=wM8mVcbKvZjBo18pSXMp28i30YWwUXJPSS7_HCakH9U,17892
34
34
  compressed_tensors/quantization/lifecycle/compressed.py,sha256=Fj9n66IN0EWsOAkBHg3O0GlOQpxstqjCcs0ttzMXrJ0,2296
35
- compressed_tensors/quantization/lifecycle/forward.py,sha256=JWOQ-03bsgh9_nnOLAjmLZ0S8bFQA-GjwDK6YUBwcrU,14883
35
+ compressed_tensors/quantization/lifecycle/forward.py,sha256=jT70Mbbu9pH10vu5ALVD7VWGoFdMEUpxmihGrf4frjM,17432
36
36
  compressed_tensors/quantization/lifecycle/helpers.py,sha256=C0mhy2vJ0fCjVeN4kFNhw8Eq1wkteBGHiZ36RVLThRY,944
37
37
  compressed_tensors/quantization/lifecycle/initialize.py,sha256=D7yxua1zELmsBYlQiJUTiClBOMIe2J0-IrN2d-jLFPk,8653
38
38
  compressed_tensors/quantization/utils/__init__.py,sha256=VdtEmP0bvuND_IGQnyqUPc5lnFp-1_yD7StKSX4x80w,656
39
- compressed_tensors/quantization/utils/helpers.py,sha256=24MJ6-Az_LpZQziFOUCroM3povOAtKAfYLrdfiBmLO4,17018
39
+ compressed_tensors/quantization/utils/helpers.py,sha256=Je96Wai9SOizbdE5ph0nsJ86zS96lE4fkf_9q9o2tpA,17212
40
40
  compressed_tensors/registry/__init__.py,sha256=FwLSNYqfIrb5JD_6OK_MT4_svvKTN_nEhpgQlQvGbjI,658
41
41
  compressed_tensors/registry/registry.py,sha256=0s15BxdGgzBv8RL4kUJCYcuDOFUh_KZYvNvLEeRqWTc,11956
42
42
  compressed_tensors/transform/__init__.py,sha256=v2wfl4CMfA6KbD7Hxx_MbRev63y_6QLDlccZq-WTtdw,907
@@ -61,8 +61,8 @@ compressed_tensors/utils/permutations_24.py,sha256=kx6fsfDHebx94zsSzhXGyCyuC9sVy
61
61
  compressed_tensors/utils/permute.py,sha256=V6tJLKo3Syccj-viv4F7ZKZgJeCB-hl-dK8RKI_kBwI,2355
62
62
  compressed_tensors/utils/safetensors_load.py,sha256=DMfZBuUbA6qp_BG_zIWT3ckiEE33K9ob34s-OgzReO4,12057
63
63
  compressed_tensors/utils/semi_structured_conversions.py,sha256=XKNffPum54kPASgqKzgKvyeqWPAkair2XEQXjkp7ho8,13489
64
- compressed_tensors-0.10.3a20250716.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
65
- compressed_tensors-0.10.3a20250716.dist-info/METADATA,sha256=zWpNTNT2bbSxFEQ58pvjEqdAE8MzBBZhvvtVVh6AQ14,7031
66
- compressed_tensors-0.10.3a20250716.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
67
- compressed_tensors-0.10.3a20250716.dist-info/top_level.txt,sha256=w2i-GyPs2s1UwVxvutSvN_lM22SXC2hQFBmoMcPnV7Y,19
68
- compressed_tensors-0.10.3a20250716.dist-info/RECORD,,
64
+ compressed_tensors-0.10.3a20250721.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
65
+ compressed_tensors-0.10.3a20250721.dist-info/METADATA,sha256=SZk9s5bqtGXwCSxZgsD1tg5GrTH4M8x0d8A3WQkrtlw,7031
66
+ compressed_tensors-0.10.3a20250721.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
67
+ compressed_tensors-0.10.3a20250721.dist-info/top_level.txt,sha256=w2i-GyPs2s1UwVxvutSvN_lM22SXC2hQFBmoMcPnV7Y,19
68
+ compressed_tensors-0.10.3a20250721.dist-info/RECORD,,