Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
25d5b4b
Prototype NVFP4 with UE5M3 scales
timmoon10 Aug 7, 2026
dede7c7
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 7, 2026
343c4bd
[PyTorch] Enable e5m3 fused GEMM kernels from cuDNN (#2)
kainzhong Aug 13, 2026
8feb5e9
Merge branch 'main' into nvfp4-ue5m3-prototype
timmoon10 Aug 13, 2026
ab3a9b3
Use custom recipe for NVFP4-UE5M3 tests
timmoon10 Aug 14, 2026
92b1063
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 14, 2026
3d25d20
Add grouped MLP kernel for GGEMM+SwiGLU+RHT+quant
timmoon10 Aug 14, 2026
f030e40
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 14, 2026
535ea4f
Merge branch 'main' into nvfp4-ue5m3-prototype
timmoon10 Aug 14, 2026
e366e88
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 14, 2026
252ae94
Debug integration with GGEMM+GLU+RHT+quant
timmoon10 Aug 15, 2026
f2b9c98
Remove scale max helper functions from NVFP4 cast utils
timmoon10 Aug 18, 2026
8685556
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 18, 2026
803dd24
Fix compile error
timmoon10 Aug 18, 2026
27f29d0
Fix NVFP4 scale dtype ABI
tdophung Aug 18, 2026
c9dad3e
remove redundant output alloc
kainzhong Aug 19, 2026
2bf42eb
no need to pad N now
kainzhong Aug 19, 2026
b6913b2
fix linting errors
kainzhong Aug 19, 2026
f2c26bd
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 19, 2026
030e9af
Disable cuDNN GGEMM+GLU+RHT+quant kernel
timmoon10 Aug 20, 2026
9162e98
Guard NVFP4 alpha scaling by scaling mode
tdophung Aug 20, 2026
db0957b
Restore UE5M3 NVFP4 cast support
tdophung Aug 20, 2026
982be7b
Localize NVFP4 4over6 scale policy
tdophung Aug 21, 2026
01f4edc
Tweak arg order in C API functions
timmoon10 Aug 21, 2026
3a63623
Rename cuDNN GGEMM helper functions for general_gemm
timmoon10 Aug 21, 2026
26db2ee
Merge branch 'main' into HEAD
timmoon10 Aug 21, 2026
8e229f1
Fix compilation error in C++ test
timmoon10 Aug 21, 2026
c8e6ced
Treat nvfp4_e4m3_max=0 as unset value
timmoon10 Aug 21, 2026
6e84816
Debug torch.compile test failure
timmoon10 Aug 21, 2026
0c2de5e
Debug test failures
timmoon10 Aug 21, 2026
dd90509
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 21, 2026
d42ccbe
Revert accidental CUTLASS commit change
timmoon10 Aug 21, 2026
b47e1f8
Enable cuDNN GGEMM+GLU+RHT+quant kernel
timmoon10 Aug 21, 2026
ae3020f
Remove incorrect scale_inv_dtype arg to NVFP4Tensor constructor
timmoon10 Aug 22, 2026
1d0d56f
Fix bug when selecting cuDNN GGEMM+GLU+RHT+quant kernel
timmoon10 Aug 22, 2026
b8fa24d
Restore GGEMM+GLU+RHT+amax kernel with RHT sign mask
timmoon10 Aug 24, 2026
30b8af8
Debug minor test failures
timmoon10 Aug 24, 2026
d9b633d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 24, 2026
75bdfe4
Fix incorrect scale dtypes in grouped tensor builder method
timmoon10 Aug 25, 2026
58dfc41
Avoid redundant amax ptr check in row-scaled NVFP4 quantize
timmoon10 Aug 25, 2026
b1a4483
Clean up C++ unit tests
timmoon10 Aug 28, 2026
93cbd0a
Address some review comments from @ptrendx
timmoon10 Aug 28, 2026
d00cbeb
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 28, 2026
3a62970
Respect no-tensor-scaling in grouped tensor helper function
timmoon10 Aug 28, 2026
e404db8
Debug C++ test compilation error
timmoon10 Aug 29, 2026
adef2dc
Make sure GEMM alpha/beta scales are on GPU, even without amaxes
timmoon10 Aug 30, 2026
a971552
Create new Mcore DDP integration functions rather than breaking backw…
timmoon10 Aug 31, 2026
bb59631
Remove unrelated tests from NVFP4 dequantize C++ unit tests
timmoon10 Aug 31, 2026
85cf34f
Add reference impl and test for NVFP4-UE5M3
timmoon10 Sep 1, 2026
d73bb04
Merge branch 'main' into nvfp4-ue5m3-prototype
timmoon10 Sep 1, 2026
bdc1d97
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 1, 2026
dc40106
Make NVFP4-UE5M3 grouped GEMM CUDA-graph safe (#4)
michal2409 Sep 1, 2026
4be085f
Fix JAX NVFP4 GEMM device scalar handling
tdophung Sep 1, 2026
1bf887c
Replace NVFP4 amax heuristic with explicit alpha/beta residency flag
tdophung Sep 2, 2026
9ca3313
Document bugs with `alpha_beta_on_device` GEMM config
timmoon10 Sep 2, 2026
608ce6b
[PyTorch] Do not swizzle operand scales in place in the NVFP4-UE5M3 G…
michal2409 Sep 2, 2026
7a6c88b
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 2, 2026
e4fa0f5
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 3, 2026
2b4b054
Merge branch 'main' into nvfp4-ue5m3-prototype
timmoon10 Sep 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
447 changes: 324 additions & 123 deletions tests/cpp/operator/test_cast_nvfp4_transpose.cu

Large diffs are not rendered by default.

148 changes: 107 additions & 41 deletions tests/cpp/operator/test_dequantize_nvfp4.cu
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <cstring>
#include <memory>
#include <random>
#include <type_traits>
#include <vector>

#include <cuda_bf16.h>
Expand Down Expand Up @@ -39,23 +40,23 @@ float2 cvt_fp4x2_to_float2(fp4e2m1x2 fp4_pair) {
return {static_cast<float>(h2.x), static_cast<float>(h2.y)};
}

template <typename OType>
template <typename OType, typename ScaleType>
void compute_ref_dequantize_nvfp4(const uint8_t *packed_data,
const fp8e4m3 *scales,
const ScaleType *scales,
const std::vector<float> &amax,
OType *output,
size_t rows,
size_t cols,
size_t scale_stride,
int e4m3_max) {
const float factor_inv = 1.0f / (6.0f * static_cast<float>(e4m3_max));
float scale_max) {
const float factor_inv = 1.0f / (6.0f * scale_max);
constexpr size_t BLOCK_SIZE = 16;
const size_t Mread = cols / BLOCK_SIZE;
const size_t bytes_per_block = BLOCK_SIZE / 2;

for (size_t row = 0; row < rows; ++row) {
for (size_t block = 0; block < Mread; ++block) {
const fp8e4m3 scale = scales[row * scale_stride + block];
const ScaleType scale = scales[row * scale_stride + block];
const float final_scale =
static_cast<float>(scale) * (amax.size() == 1 ? amax[0] : amax[row]) * factor_inv;

Expand Down Expand Up @@ -87,34 +88,61 @@ float compute_amax(test::Tensor &t, size_t rows, size_t cols) {
return amax;
}

template <typename ScaleType>
constexpr float get_scale_max(int scale_max = 0) {
static_assert(std::is_same_v<ScaleType, fp8e4m3>
#if CUDA_VERSION >= 13040
|| std::is_same_v<ScaleType, fp8ue5m3>
#endif
, "Unsupported NVFP4 scale type.");
if constexpr (std::is_same_v<ScaleType, fp8e4m3>) {
if (scale_max == 0) {
scale_max = 448;
}
return static_cast<float>(scale_max);
}
#if CUDA_VERSION >= 13040
if constexpr (std::is_same_v<ScaleType, fp8ue5m3>) {
if (scale_max == 0) {
scale_max = 114688;
}
return static_cast<float>(scale_max);
}
#endif
return 0.f;
}

struct NVFP4DequantizeTestConfig {
NVTENVFP44Over6Mode mode = kNVTENVFP44Over6Disabled;
int e4m3_max = 448;
int scale_max = 0;
};

// Quantize a high-precision input to NVFP4, then dequantize and compare
// against a CPU reference computed from the quantized data.
template <typename OutputType>
template <typename OutputType, typename ScaleType = fp8e4m3>
void performTest_dequantize_nvfp4(const size_t rows, const size_t cols,
const bool row_scaled_nvfp4,
const NVTENVFP44Over6Mode mode,
const int e4m3_max) {
const int scale_max) {
using namespace test;
DType otype = TypeInfo<OutputType>::dtype;

// Tensors
Tensor input("input", std::vector<size_t>{rows, cols}, otype);
Tensor quantized("quantized", std::vector<size_t>{rows, cols},
DType::kFloat4E2M1, true, false, NVTE_NVFP4_1D_SCALING);
DType::kFloat4E2M1, true, false, NVTE_NVFP4_1D_SCALING,
TypeInfo<ScaleType>::dtype);
Tensor output("output", std::vector<size_t>{rows, cols}, otype, true, false);

// Fill input with random data
fillCase<fp32>(&input, InputsFillCase::uniform);

// Configure quantized tensor amax
size_t amax_size = 1;
quantized.set_nvfp4_e4m3_max(e4m3_max);
ASSERT_EQ(quantized.nvfp4_e4m3_max(), e4m3_max);
if (scale_max != 0) {
quantized.set_nvfp4_e4m3_max(scale_max);
ASSERT_EQ(quantized.nvfp4_e4m3_max(), scale_max);
}
if (row_scaled_nvfp4) {
quantized.set_row_scaled_nvfp4(true);
amax_size = rows;
Expand Down Expand Up @@ -149,38 +177,41 @@ void performTest_dequantize_nvfp4(const size_t rows, const size_t cols,
quantized.to_cpu();
const uint8_t *fp4_data =
reinterpret_cast<const uint8_t *>(quantized.rowwise_cpu_dptr<fp4e2m1>());
const fp8e4m3 *scales = quantized.rowwise_cpu_scale_inv_ptr<fp8e4m3>();
const ScaleType *scales = quantized.rowwise_cpu_scale_inv_ptr<ScaleType>();
const auto *amax = quantized.cpu_rowwise_amax_ptr<float>();
const std::vector<float> amax_vals(amax, amax + amax_size);
const NVTEShape scale_shape = quantized.rowwise_scale_inv_shape();
const size_t scale_stride = scale_shape.data[scale_shape.ndim - 1];
std::unique_ptr<OutputType[]> ref_output =
std::make_unique<OutputType[]>(rows * cols);
compute_ref_dequantize_nvfp4<OutputType>(
compute_ref_dequantize_nvfp4<OutputType, ScaleType>(
fp4_data, scales, amax_vals, ref_output.get(),
rows, cols, scale_stride, e4m3_max);
rows, cols, scale_stride, get_scale_max<ScaleType>(scale_max));

// Compare results from TE and reference impls
auto [atol, rtol] = getTolerances(otype);
compareResults("output_nvfp4", output, ref_output.get(), true, atol, rtol);
}

// Dequantize NVFP4 with GEMM-swizzled scales and compare against compact path.
template <typename OutputType>
template <typename OutputType, typename ScaleType = fp8e4m3>
void performTest_dequantize_nvfp4_swizzled(const size_t rows, const size_t cols,
const bool row_scaled_nvfp4,
const NVTENVFP44Over6Mode mode,
const int e4m3_max) {
const int scale_max) {
using namespace test;
DType otype = TypeInfo<OutputType>::dtype;

Tensor input("input", std::vector<size_t>{rows, cols}, otype);
fillCase<fp32>(&input, InputsFillCase::uniform);

Tensor quantized_compact("quantized_compact", std::vector<size_t>{rows, cols},
DType::kFloat4E2M1, true, false, NVTE_NVFP4_1D_SCALING);
quantized_compact.set_nvfp4_e4m3_max(e4m3_max);
ASSERT_EQ(quantized_compact.nvfp4_e4m3_max(), e4m3_max);
DType::kFloat4E2M1, true, false, NVTE_NVFP4_1D_SCALING,
TypeInfo<ScaleType>::dtype);
if (scale_max != 0) {
quantized_compact.set_nvfp4_e4m3_max(scale_max);
ASSERT_EQ(quantized_compact.nvfp4_e4m3_max(), scale_max);
}
if (row_scaled_nvfp4) {
quantized_compact.set_row_scaled_nvfp4(true);
} else if (rows > 0 && cols > 0) {
Expand All @@ -203,9 +234,12 @@ void performTest_dequantize_nvfp4_swizzled(const size_t rows, const size_t cols,

// Create tensor with same FP4 data but swizzled scales
Tensor quantized_swizzled("quantized_swizzled", std::vector<size_t>{rows, cols},
DType::kFloat4E2M1, true, false, NVTE_NVFP4_1D_SCALING);
quantized_swizzled.set_nvfp4_e4m3_max(e4m3_max);
ASSERT_EQ(quantized_swizzled.nvfp4_e4m3_max(), e4m3_max);
DType::kFloat4E2M1, true, false, NVTE_NVFP4_1D_SCALING,
TypeInfo<ScaleType>::dtype);
if (scale_max != 0) {
quantized_swizzled.set_nvfp4_e4m3_max(scale_max);
ASSERT_EQ(quantized_swizzled.nvfp4_e4m3_max(), scale_max);
}
if (row_scaled_nvfp4) {
quantized_swizzled.set_row_scaled_nvfp4(true);
} else {
Expand Down Expand Up @@ -281,7 +315,22 @@ class DequantizeNVFP4TestSuite : public ::testing::TestWithParam
<std::tuple<std::pair<size_t, size_t>,
transformer_engine::DType,
bool,
NVFP4DequantizeTestConfig>> {};
NVFP4DequantizeTestConfig,
transformer_engine::DType>> {
public:
static std::string test_name(const testing::TestParamInfo<ParamType> &info) {
const NVFP4DequantizeTestConfig config = std::get<3>(info.param);
const bool use_4over6 = config.mode != kNVTENVFP44Over6Disabled;
std::string name = std::to_string(std::get<0>(info.param).first) + "X" +
std::to_string(std::get<0>(info.param).second) + "X" +
test::typeName(std::get<1>(info.param)) + "X" +
(std::get<2>(info.param) ? "RowScaled" : "PerTensor") + "X" +
(use_4over6 ? "FourOverSix" : "Default") + "X" +
"ScaleMax" + std::to_string(config.scale_max) + "X" +
"Scale" + test::typeName(std::get<4>(info.param));
return name;
}
};

TEST_P(DequantizeNVFP4TestSuite, TestDequantizeNVFP4)
{
Expand All @@ -293,11 +342,25 @@ TEST_P(DequantizeNVFP4TestSuite, TestDequantizeNVFP4)
const DType output_type = std::get<1>(GetParam());
const bool row_scaled_nvfp4 = std::get<2>(GetParam());
const NVFP4DequantizeTestConfig config = std::get<3>(GetParam());
const DType scale_type = std::get<4>(GetParam());

TRANSFORMER_ENGINE_TYPE_SWITCH_FP16_FP32_ONLY(output_type, OutputType,
performTest_dequantize_nvfp4<OutputType>(
switch (scale_type) {
case transformer_engine::DType::kFloat8E4M3:
performTest_dequantize_nvfp4<OutputType, fp8e4m3>(
tensor_size.first, tensor_size.second, row_scaled_nvfp4, config.mode,
config.e4m3_max);
config.scale_max);
break;
#if CUDA_VERSION >= 13040
case transformer_engine::DType::kFloat8UE5M3:
performTest_dequantize_nvfp4<OutputType, fp8ue5m3>(
tensor_size.first, tensor_size.second, row_scaled_nvfp4, config.mode,
config.scale_max);
break;
#endif // CUDA_VERSION >= 13040
default:
NVTE_ERROR("Invalid scale type (", static_cast<int>(scale_type), ".");
}
);
}

Expand All @@ -310,20 +373,23 @@ INSTANTIATE_TEST_SUITE_P(
::testing::Bool(),
::testing::Values(NVFP4DequantizeTestConfig{},
NVFP4DequantizeTestConfig{kNVTENVFP44Over6MinMAE, 448},
NVFP4DequantizeTestConfig{kNVTENVFP44Over6MinMAE, 256})),
[](const testing::TestParamInfo<DequantizeNVFP4TestSuite::ParamType>& info)
{
const NVFP4DequantizeTestConfig config = std::get<3>(info.param);
const bool use_4over6 = config.mode != kNVTENVFP44Over6Disabled;
std::string name = std::to_string(std::get<0>(info.param).first) + "X" +
std::to_string(std::get<0>(info.param).second) + "X" +
test::typeName(std::get<1>(info.param)) + "X" +
(std::get<2>(info.param) ? "RowScaled" : "PerTensor") + "X" +
(use_4over6 ? "FourOverSix" : "Default") + "X" +
(config.e4m3_max == 256 ? "E4M3Max256" : "E4M3Max448");
return name;
}
);
NVFP4DequantizeTestConfig{kNVTENVFP44Over6MinMAE, 256}),
::testing::Values(DType::kFloat8E4M3)),
DequantizeNVFP4TestSuite::test_name);

#if CUDA_VERSION >= 13040

INSTANTIATE_TEST_SUITE_P(
OperatorTestUE5M3Scales,
DequantizeNVFP4TestSuite,
::testing::Combine(
::testing::ValuesIn(nvfp4_tensor_dims),
::testing::Values(DType::kFloat32, DType::kBFloat16, DType::kFloat16),
::testing::Values(false),
::testing::Values(NVFP4DequantizeTestConfig{}),
::testing::Values(DType::kFloat8UE5M3)),
DequantizeNVFP4TestSuite::test_name);
#endif

class DequantizeNVFP4SwizzledTestSuite : public ::testing::TestWithParam
<std::tuple<std::pair<size_t, size_t>,
Expand All @@ -345,7 +411,7 @@ TEST_P(DequantizeNVFP4SwizzledTestSuite, TestDequantizeNVFP4Swizzled)
TRANSFORMER_ENGINE_TYPE_SWITCH_FP16_FP32_ONLY(output_type, OutputType,
performTest_dequantize_nvfp4_swizzled<OutputType>(
tensor_size.first, tensor_size.second, row_scaled_nvfp4, config.mode,
config.e4m3_max);
config.scale_max);
);
}

Expand All @@ -368,7 +434,7 @@ INSTANTIATE_TEST_SUITE_P(
test::typeName(std::get<1>(info.param)) + "X" +
(std::get<2>(info.param) ? "RowScaled" : "PerTensor") + "X" +
(use_4over6 ? "FourOverSix" : "Default") + "X" +
(config.e4m3_max == 256 ? "E4M3Max256" : "E4M3Max448") + "X" +
"ScaleMax" + std::to_string(config.scale_max) + "X" +
"Swizzled";
return name;
}
Expand Down
21 changes: 17 additions & 4 deletions tests/cpp/operator/test_normalization.cu
Original file line number Diff line number Diff line change
Expand Up @@ -261,9 +261,9 @@ TEST_P(NormTestSuite, TestNorm) {
const bool cudnn_zero_centered_gamma_in_weight_dtype = std::get<6>(GetParam());
const bool fused_bwd_add = std::get<7>(GetParam());

TRANSFORMER_ENGINE_TYPE_SWITCH_ALL(input_type, InputType,
TRANSFORMER_ENGINE_TYPE_SWITCH_ALL(output_type, OutputType,
performTest<InputType, OutputType>(
TRANSFORMER_ENGINE_TYPE_SWITCH_FP16_FP32_ONLY(input_type, InputType,
if (output_type == DType::kFloat8E4M3) {
performTest<InputType, fp8e4m3>(
size.first,
size.second,
zero_centered_gamma,
Expand All @@ -272,7 +272,20 @@ TEST_P(NormTestSuite, TestNorm) {
cudnn_zero_centered_gamma_in_weight_dtype,
fused_bwd_add
);
);
} else {
TRANSFORMER_ENGINE_TYPE_SWITCH_FP16_FP32_ONLY(
output_type, OutputType,
performTest<InputType, OutputType>(
size.first,
size.second,
zero_centered_gamma,
norm_type,
use_cudnn,
cudnn_zero_centered_gamma_in_weight_dtype,
fused_bwd_add
);
);
}
);
}

Expand Down
Loading
Loading