| from __future__ import annotations |
|
|
| import torch |
|
|
| from ._ops import ops |
|
|
| __all__ = [ |
| "matmul_packed_w4a4_int8", |
| "matmul_packed_adaln_int4_cpu", |
| "matmul_packed_weight", |
| "quantize_activations_cpu", |
| "quantize_activations_int8", |
| "quantize_activations_packed_w4", |
| "supports_cpu_activation", |
| "supports_cpu_adaln", |
| "supports_device", |
| ] |
|
|
|
|
| def supports_device(device_type: str) -> bool: |
| dispatch_keys = { |
| "cpu": "CPU", |
| "cuda": "CUDA", |
| "mps": "MPS", |
| } |
| try: |
| dispatch_key = dispatch_keys[device_type] |
| except KeyError as exc: |
| raise ValueError(f"unknown device type {device_type!r}") from exc |
| return bool( |
| torch._C._dispatch_has_kernel_for_dispatch_key( |
| ops.matmul_packed_weight._qualified_op_name, |
| dispatch_key, |
| ) |
| ) |
|
|
|
|
| def supports_cpu_activation() -> bool: |
| try: |
| operation = ops.quantize_activations_cpu |
| qualified_name = operation._qualified_op_name |
| except (AttributeError, RuntimeError): |
| return False |
| return bool( |
| torch._C._dispatch_has_kernel_for_dispatch_key( |
| qualified_name, |
| "CPU", |
| ) |
| ) |
|
|
|
|
| def supports_cpu_adaln() -> bool: |
| try: |
| operation = ops.matmul_packed_adaln_int4_cpu |
| qualified_name = operation._qualified_op_name |
| except (AttributeError, RuntimeError): |
| return False |
| return bool( |
| torch._C._dispatch_has_kernel_for_dispatch_key( |
| qualified_name, |
| "CPU", |
| ) |
| ) |
|
|
|
|
| def quantize_activations_cpu( |
| x: torch.Tensor, |
| permutation: torch.Tensor, |
| signs: torch.Tensor, |
| centroids: torch.Tensor, |
| boundaries: torch.Tensor, |
| *, |
| eps: float, |
| inv_sqrt_block: float, |
| block_size: int, |
| ) -> torch.Tensor: |
| if x.device.type != "cpu": |
| raise RuntimeError("native CPU activation quantization requires CPU tensors") |
| if x.dtype not in {torch.float32, torch.float16, torch.bfloat16}: |
| raise ValueError("x must be float32, float16, or bfloat16") |
| if block_size <= 0 or block_size & (block_size - 1): |
| raise ValueError("block_size must be a positive power of two") |
| dim = x.shape[-1] |
| if dim % block_size != 0: |
| raise ValueError("block_size must divide the input dimension") |
|
|
| original_shape = x.shape |
| values = x.contiguous().reshape(-1, dim) |
| out = torch.empty_like(values) |
| permutation_dtype = torch.int32 if permutation.dtype == torch.int32 else torch.int64 |
| permutation_values = permutation.to(device="cpu", dtype=permutation_dtype).contiguous() |
| sign_values = signs.to(device="cpu", dtype=torch.int8).contiguous() |
| centroid_values = centroids.to(device="cpu", dtype=torch.float32).contiguous() |
| boundary_values = boundaries.to(device="cpu", dtype=torch.float32).contiguous() |
| ops.quantize_activations_cpu( |
| out, |
| values, |
| permutation_values, |
| sign_values, |
| centroid_values, |
| boundary_values, |
| eps, |
| inv_sqrt_block, |
| block_size, |
| ) |
| return out.reshape(original_shape) |
|
|
|
|
| def matmul_packed_weight( |
| x: torch.Tensor, |
| packed_weight_indices: torch.Tensor, |
| row_norms: torch.Tensor, |
| centroids: torch.Tensor, |
| *, |
| bits: int, |
| out_features: int, |
| in_features: int, |
| bias: torch.Tensor | None = None, |
| block_m: int = 64, |
| block_n: int = 64, |
| block_k: int = 128, |
| ) -> torch.Tensor: |
| if bits <= 0 or bits > 8: |
| raise ValueError("bits must be in [1, 8]") |
| if x.shape[-1] != in_features: |
| raise ValueError(f"expected input last dimension {in_features}, got {x.shape[-1]}") |
| if block_m <= 0 or block_n <= 0 or block_k <= 0: |
| raise ValueError("packed matmul tile sizes must be positive") |
|
|
| original_shape = x.shape |
| x_2d = x.contiguous().reshape(-1, in_features) |
| out = torch.empty((x_2d.shape[0], out_features), device=x.device, dtype=x.dtype) |
| packed = packed_weight_indices.to(device=x.device, dtype=torch.uint8).contiguous() |
| auxiliary_dtype = torch.bfloat16 if x.device.type == "cuda" else torch.float32 |
| norms = row_norms.to(device=x.device, dtype=auxiliary_dtype).contiguous() |
| centroid_values = centroids.to(device=x.device, dtype=torch.float32).contiguous() |
| if bias is None: |
| bias_values = torch.empty((1,), device=x.device, dtype=torch.float32) |
| has_bias = False |
| else: |
| bias_dtype = x.dtype if x.device.type == "cuda" else torch.float32 |
| bias_values = bias.to(device=x.device, dtype=bias_dtype).contiguous() |
| has_bias = True |
|
|
| ops.matmul_packed_weight( |
| out, |
| x_2d, |
| packed, |
| norms, |
| centroid_values, |
| bias_values, |
| has_bias, |
| bits, |
| out_features, |
| in_features, |
| block_m, |
| block_n, |
| block_k, |
| ) |
| return out.reshape(*original_shape[:-1], out_features) |
|
|
|
|
| def matmul_packed_adaln_int4_cpu( |
| x: torch.Tensor, |
| packed_weight: torch.Tensor, |
| scales: torch.Tensor, |
| *, |
| out_features: int, |
| in_features: int, |
| group_size: int, |
| bias: torch.Tensor | None = None, |
| ) -> torch.Tensor: |
| if x.device.type != "cpu": |
| raise RuntimeError("native packed AdaLN requires CPU tensors") |
| if x.shape[-1] != in_features: |
| raise ValueError(f"expected input last dimension {in_features}, got {x.shape[-1]}") |
| if group_size <= 0: |
| raise ValueError("group_size must be positive") |
|
|
| original_shape = x.shape |
| x_2d = x.to(dtype=torch.bfloat16).contiguous().reshape(-1, in_features) |
| out = torch.empty((x_2d.shape[0], out_features), dtype=torch.bfloat16) |
| packed = packed_weight.to(device="cpu", dtype=torch.uint8).contiguous() |
| scale_values = scales.to(device="cpu", dtype=torch.float32).contiguous() |
| if bias is None: |
| bias_values = torch.empty((1,), dtype=torch.float32) |
| has_bias = False |
| else: |
| bias_values = ( |
| bias.to(device="cpu", dtype=torch.bfloat16).to(dtype=torch.float32).contiguous() |
| ) |
| has_bias = True |
| ops.matmul_packed_adaln_int4_cpu( |
| out, |
| x_2d, |
| packed, |
| scale_values, |
| bias_values, |
| has_bias, |
| out_features, |
| in_features, |
| group_size, |
| ) |
| return out.reshape(*original_shape[:-1], out_features) |
|
|
|
|
| def matmul_packed_w4a4_int8( |
| packed_activations: torch.Tensor, |
| packed_weight_indices: torch.Tensor, |
| token_norms: torch.Tensor, |
| row_norms: torch.Tensor, |
| activation_codes: torch.Tensor, |
| weight_codes: torch.Tensor, |
| *, |
| activation_scale: float, |
| weight_scale: float, |
| out_features: int, |
| in_features: int, |
| bias: torch.Tensor | None = None, |
| output_dtype: torch.dtype = torch.bfloat16, |
| tile_m: int = 128, |
| tile_n: int = 128, |
| async_packed: bool = False, |
| weight_k_major: bool = False, |
| ) -> torch.Tensor: |
| if not packed_activations.is_cuda: |
| raise RuntimeError("packed W4A4 INT8 matmul requires CUDA tensors") |
| if in_features <= 0 or in_features % 64 != 0: |
| raise ValueError("in_features must be positive and divisible by 64") |
| if packed_activations.shape[-1] != in_features // 2: |
| raise ValueError( |
| f"expected packed activation last dimension {in_features // 2}, " |
| f"got {packed_activations.shape[-1]}" |
| ) |
| if output_dtype not in {torch.float16, torch.bfloat16}: |
| raise ValueError("output_dtype must be float16 or bfloat16") |
| if (tile_m, tile_n) not in {(128, 128), (256, 128), (128, 256)}: |
| raise ValueError("tile must be 128x128, 256x128, or 128x256") |
|
|
| original_shape = packed_activations.shape |
| activations = ( |
| packed_activations.to(dtype=torch.uint8).contiguous().reshape(-1, in_features // 2) |
| ) |
| weights = packed_weight_indices.to(device=activations.device, dtype=torch.uint8).contiguous() |
| norms = token_norms.to(device=activations.device, dtype=torch.float32).contiguous() |
| weight_norms = row_norms.to(device=activations.device, dtype=torch.bfloat16).contiguous() |
| activation_code_values = activation_codes.to( |
| device=activations.device, dtype=torch.int8 |
| ).contiguous() |
| weight_code_values = weight_codes.to(device=activations.device, dtype=torch.int8).contiguous() |
| out = torch.empty( |
| (activations.shape[0], out_features), |
| device=activations.device, |
| dtype=output_dtype, |
| ) |
| if bias is None: |
| bias_values = out |
| has_bias = False |
| else: |
| bias_values = bias.to(device=activations.device, dtype=output_dtype).contiguous() |
| has_bias = True |
|
|
| ops.matmul_packed_w4a4_int8( |
| out, |
| activations, |
| weights, |
| norms, |
| weight_norms, |
| activation_code_values, |
| weight_code_values, |
| bias_values, |
| has_bias, |
| activation_scale, |
| weight_scale, |
| out_features, |
| in_features, |
| tile_m, |
| tile_n, |
| async_packed, |
| weight_k_major, |
| ) |
| return out.reshape(*original_shape[:-1], out_features) |
|
|
|
|
| def quantize_activations_packed_w4( |
| x: torch.Tensor, |
| permutation: torch.Tensor, |
| signs: torch.Tensor, |
| boundaries: torch.Tensor, |
| *, |
| eps: float = 1e-12, |
| inv_sqrt_block: float, |
| threads: int = 256, |
| ) -> tuple[torch.Tensor, torch.Tensor]: |
| if not x.is_cuda: |
| raise RuntimeError("native packed W4 activation quantization requires CUDA tensors") |
| if x.dtype not in {torch.float16, torch.bfloat16}: |
| raise ValueError("x must be float16 or bfloat16") |
| dim = x.shape[-1] |
| if dim not in {512, 1024, 2048, 4096, 8192, 16384}: |
| raise ValueError( |
| "native packed W4 activation quantization supports dimensions " |
| "512, 1024, 2048, 4096, 8192, and 16384" |
| ) |
| if threads not in {128, 256, 512}: |
| raise ValueError("threads must be 128, 256, or 512") |
| if permutation.numel() != dim or signs.numel() != dim: |
| raise ValueError("permutation and signs must match the input dimension") |
| if boundaries.numel() != 15: |
| raise ValueError("boundaries must contain 15 values") |
|
|
| original_shape = x.shape |
| values = x.contiguous().reshape(-1, dim) |
| packed = torch.empty((values.shape[0], dim // 2), device=x.device, dtype=torch.uint8) |
| norms = torch.empty(values.shape[0], device=x.device, dtype=torch.float32) |
| permutation_dtype = torch.int32 if permutation.dtype == torch.int32 else torch.int64 |
| permutation_values = permutation.to(device=x.device, dtype=permutation_dtype).contiguous() |
| sign_values = signs.to(device=x.device, dtype=torch.int8).contiguous() |
| boundary_values = boundaries.to(device=x.device, dtype=torch.float32).contiguous() |
| ops.quantize_activations_packed_w4( |
| packed, |
| norms, |
| values, |
| permutation_values, |
| sign_values, |
| boundary_values, |
| eps, |
| inv_sqrt_block, |
| threads, |
| ) |
| return ( |
| packed.reshape(*original_shape[:-1], dim // 2), |
| norms.reshape(original_shape[:-1]), |
| ) |
|
|
|
|
| def quantize_activations_int8( |
| x: torch.Tensor, |
| permutation: torch.Tensor, |
| signs: torch.Tensor, |
| boundaries: torch.Tensor, |
| codes: torch.Tensor, |
| *, |
| eps: float = 1e-12, |
| inv_sqrt_block: float, |
| threads: int = 256, |
| ) -> tuple[torch.Tensor, torch.Tensor]: |
| if not x.is_cuda: |
| raise RuntimeError("native INT8 activation quantization requires CUDA tensors") |
| if x.dtype not in {torch.float16, torch.bfloat16}: |
| raise ValueError("x must be float16 or bfloat16") |
| dim = x.shape[-1] |
| if dim not in {512, 1024, 2048, 4096, 8192, 12288, 16384}: |
| raise ValueError( |
| "native INT8 activation quantization supports dimensions " |
| "512, 1024, 2048, 4096, 8192, 12288, and 16384" |
| ) |
| if threads not in {128, 256, 512}: |
| raise ValueError("threads must be 128, 256, or 512") |
| if permutation.numel() != dim or signs.numel() != dim: |
| raise ValueError("permutation and signs must match the input dimension") |
| if boundaries.numel() != 15: |
| raise ValueError("boundaries must contain 15 values") |
| if codes.numel() != 16: |
| raise ValueError("codes must contain 16 values") |
|
|
| original_shape = x.shape |
| values = x.contiguous().reshape(-1, dim) |
| quantized = torch.empty(values.shape, device=x.device, dtype=torch.int8) |
| norms = torch.empty(values.shape[0], device=x.device, dtype=torch.float32) |
| permutation_dtype = torch.int32 if permutation.dtype == torch.int32 else torch.int64 |
| permutation_values = permutation.to(device=x.device, dtype=permutation_dtype).contiguous() |
| sign_values = signs.to(device=x.device, dtype=torch.int8).contiguous() |
| boundary_values = boundaries.to(device=x.device, dtype=torch.float32).contiguous() |
| code_values = codes.to(device=x.device, dtype=torch.int8).contiguous() |
| ops.quantize_activations_int8( |
| quantized, |
| norms, |
| values, |
| permutation_values, |
| sign_values, |
| boundary_values, |
| code_values, |
| eps, |
| inv_sqrt_block, |
| threads, |
| ) |
| return ( |
| quantized.reshape(*original_shape[:-1], dim), |
| norms.reshape(original_shape[:-1]), |
| ) |
|
|