Quantize input tensor to FP4 and return quantized tensor and scale. This function quantizes the last dimension of the given tensor `input`. For every 16 consecutive elements, a single dynamically computed scaling factor is shared. This scaling factor is quantized using the `input_g
(input: torch.Tensor, input_global_scale: torch.Tensor)
| 9 | |
| 10 | |
| 11 | def scaled_nvfp4_quant(input: torch.Tensor, input_global_scale: torch.Tensor): |
| 12 | """ |
| 13 | Quantize input tensor to FP4 and return quantized tensor and scale. |
| 14 | |
| 15 | This function quantizes the last dimension of the given tensor `input`. For |
| 16 | every 16 consecutive elements, a single dynamically computed scaling factor |
| 17 | is shared. This scaling factor is quantized using the `input_global_scale` |
| 18 | and is stored in a swizzled layout (see |
| 19 | https://docs.nvidia.com/cuda/parallel-thread-execution/#tcgen05-mma-scale-factor-b-layout-4x). |
| 20 | |
| 21 | Args: |
| 22 | input: The input tensor to be quantized to FP4 |
| 23 | input_global_scale: A scalar scaling factor for the entire tensor. |
| 24 | |
| 25 | Returns: |
| 26 | Tuple[torch.Tensor, torch.Tensor]: The output tensor in FP4 but every |
| 27 | two values are packed into a uint8 and float8_e4m3 scaling factors |
| 28 | in a sizzled layout. |
| 29 | """ |
| 30 | # assert input.ndim >= 1, f"input.ndim needs to be >= 1, but got {input.ndim}." |
| 31 | # other_dims = 1 if input.ndim == 1 else -1 |
| 32 | # input = input.reshape(other_dims, input.shape[-1]) |
| 33 | m, n = input.shape |
| 34 | block_size = 16 |
| 35 | device = input.device |
| 36 | |
| 37 | # assert n % block_size == 0, f"last dim has to be multiple of 16, but got {n}." |
| 38 | # assert input.dtype in ( |
| 39 | # torch.float16, |
| 40 | # torch.bfloat16, |
| 41 | # ), f"input.dtype needs to be fp16 or bf16 but got {input.dtype}." |
| 42 | |
| 43 | # Two fp4 values will be packed into an uint8. |
| 44 | output = torch.empty((m, n // 2), device=device, dtype=torch.uint8) |
| 45 | |
| 46 | # We use the rounded values to store the swizzled values. Then, the scaling |
| 47 | # factors in float8_e4m3fn are packed into an int32 for every 4 values. |
| 48 | # rounded_m = ((m + 128 - 1) // 128) * 128 |
| 49 | # scale_n = n // block_size |
| 50 | # rounded_n = ((scale_n + 4 - 1) // 4) * 4 |
| 51 | output_scale = torch.zeros((((m + 128 - 1) // 128) * 128, (n // block_size + 4 - 1) // 4), device=device, dtype=torch.int32) |
| 52 | |
| 53 | torch.ops.lightx2v_kernel.scaled_nvfp4_quant_sm120.default(output, input, output_scale, input_global_scale) |
| 54 | output_scale = output_scale.view(torch.float8_e4m3fn) |
| 55 | return output, output_scale |
| 56 | |
| 57 | |
| 58 | def scaled_mxfp4_quant(input: torch.Tensor): |