Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
6c43f15
start draft
kainzhong Jun 12, 2026
a4f9bab
remove benchmark scripts
kainzhong Jul 8, 2026
96136c2
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 8, 2026
a9e8df4
add license
kainzhong Jul 8, 2026
e036abf
fix
kainzhong Jul 8, 2026
15de391
make cutlass dsl required
kainzhong Jul 8, 2026
0d4a2ba
fix linting errors
kainzhong Jul 8, 2026
01b28b3
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 8, 2026
94501c6
warn failed compilation
kainzhong Jul 9, 2026
a95ccf7
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 9, 2026
5cd2d47
make tvm-ffi common dependency
kainzhong Jul 9, 2026
bf0cd8d
use a higher version for cutlass-dsl
kainzhong Jul 9, 2026
1650c05
less comment
kainzhong Jul 9, 2026
b332fb7
skip zeroing buffer if noop flag set
kainzhong Jul 9, 2026
631bf05
refactor activations
kainzhong Jul 9, 2026
81c52ef
fix
kainzhong Jul 9, 2026
3fc36ff
lint
kainzhong Jul 9, 2026
f41e448
fix
kainzhong Jul 9, 2026
2af3d4e
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 9, 2026
43c7b5f
nit
kainzhong Jul 10, 2026
84cfcc3
maybe it's better to prepare tvm-ffi & cutedsl kernels in common/__in…
kainzhong Jul 14, 2026
815ec0f
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 14, 2026
c9c1c4f
nit
kainzhong Jul 17, 2026
2334ca7
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 17, 2026
c4291ad
fix
kainzhong Jul 17, 2026
26a2847
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 17, 2026
00784d5
fix
kainzhong Jul 17, 2026
752e962
fix
kainzhong Jul 17, 2026
aaf6518
fix
kainzhong Jul 17, 2026
3d300ce
also flush colwise scale in SMEM
kainzhong Jul 17, 2026
b3b0a7f
use explicit fp8 types
kainzhong Jul 17, 2026
5e16681
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 17, 2026
855ba5b
nit
kainzhong Jul 20, 2026
a6010ca
add checks
kainzhong Jul 20, 2026
a2dcda7
refactor
kainzhong Jul 20, 2026
13bc7ff
nit
kainzhong Jul 20, 2026
812b81e
nit
kainzhong Jul 21, 2026
428bc5a
nit
kainzhong Jul 21, 2026
5044d27
fi
kainzhong Jul 24, 2026
bc804b6
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 24, 2026
5172938
nit
kainzhong Jul 24, 2026
19b61ff
nit
kainzhong Jul 24, 2026
5f5099b
skip comparision test if cutedsl disabled
kainzhong Jul 24, 2026
1f0a686
fix
kainzhong Jul 24, 2026
cb2cdcb
Merge pull request #3 from janekb04/cutedsl_mxfp8_common
janekb04 Jul 27, 2026
2ea3eb5
add test to the qa script
kainzhong Jul 27, 2026
4815598
warn only once if dlopen fails
kainzhong Jul 27, 2026
3c93710
fix
kainzhong Jul 28, 2026
7a10bb9
let tests cover swizzling
kainzhong Jul 29, 2026
a26638a
warn not chosen cutedsl kernel during testing
kainzhong Jul 29, 2026
e054993
improve lazyload so now the cache is uint32
kainzhong Jul 29, 2026
207b043
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 29, 2026
eebf0e7
let tests be verbose about warnings so we know if cutedsl kernel is n…
kainzhong Jul 29, 2026
09a70a2
support swizzled scale for specialized kernel but disable it
kainzhong Jul 29, 2026
1c18fd7
fix
kainzhong Jul 29, 2026
7942da1
nit
kainzhong Jul 31, 2026
0c83fb3
fix inline ptx
kainzhong Aug 4, 2026
b111740
more fix
kainzhong Aug 4, 2026
547b84a
revert to mul
kainzhong Aug 4, 2026
53a8ab5
nit
kainzhong Aug 5, 2026
fd9b1d8
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 5, 2026
6e02798
add jax tests
kainzhong Aug 6, 2026
ce9ef37
also ignore noop when dbias
kainzhong Aug 6, 2026
36f8129
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 6, 2026
12b478d
Merge branch 'main' into cutedsl_mxfp8_common
kainzhong Aug 14, 2026
d9b6733
relax dim check for the kernels
kainzhong Aug 18, 2026
2e4bf7c
reject 2D quant
kainzhong Aug 19, 2026
c078b31
run more tests with CuTeDSL path
kainzhong Aug 19, 2026
92de056
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 19, 2026
428913e
make rowwise only handle divisible cases
kainzhong Aug 19, 2026
9c328d9
fallback to CUDA if not TMA aligned
kainzhong Aug 19, 2026
0ad3831
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 19, 2026
c1aef2f
specialize skip masking version
kainzhong Aug 19, 2026
208ddea
nit
kainzhong Aug 19, 2026
775ec31
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 19, 2026
39d69c9
add type annotations
kainzhong Aug 19, 2026
13c3fa1
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 19, 2026
1170a0e
optimize masking
kainzhong Aug 20, 2026
b291b0f
skip masking for some acts
kainzhong Aug 20, 2026
05ea26a
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 20, 2026
6e56adf
Merge branch 'main' into cutedsl_mxfp8_common
kainzhong Aug 20, 2026
1318db9
allow user to opt out from building with CuTeDSL
kainzhong Aug 20, 2026
996ad08
better warning
kainzhong Aug 20, 2026
5e39296
doc
kainzhong Aug 20, 2026
1214762
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 20, 2026
53f7f4d
add jax test coverage for cutedsl
kainzhong Aug 20, 2026
5e42c35
fix device sm query
kainzhong Aug 20, 2026
ae4b4e8
catch more python error
kainzhong Aug 20, 2026
ab73b64
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 20, 2026
f67c7d9
nit
kainzhong Aug 20, 2026
308702c
nit
kainzhong Aug 20, 2026
d9636c1
nit
kainzhong Aug 21, 2026
8fa0b71
fix
kainzhong Aug 21, 2026
4797fa9
make tvm-ffi required for python users
kainzhong Aug 21, 2026
d02acc2
optimize dbias
kainzhong Aug 22, 2026
3ef8dcd
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 22, 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
24 changes: 24 additions & 0 deletions docs/envvars.rst
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,14 @@ Optional Dependencies
:Default: ``0``
:Description: Enable NVSHMEM support. When set to ``1``, requires ``NVSHMEM_HOME`` to be set to the NVSHMEM installation directory.

.. envvar:: NVTE_WITH_CUTEDSL

:Type: CMake option
:Default: ``ON``
:Description: Build CuTeDSL kernel bindings in C++. Only applies when configuring the C++ library with CMake directly; pass
``-DNVTE_WITH_CUTEDSL=OFF`` to build without it. This is intended for C++ users who do not want to introduce
a python environment which is required to generate CuTeDSL kernels at runtime.

.. envvar:: NVTE_BUILD_ACTIVATION_WITH_FAST_MATH

:Type: CMake option
Expand Down Expand Up @@ -130,6 +138,22 @@ General
value if an application legitimately creates more tensor handles than
the default pool can hold.

.. envvar:: NVTE_ENABLE_CUTEDSL_QUANT_BACKEND

:Type: ``int`` (0 or 1)
:Default: ``0``
:Description: Use CuTeDSL kernels when it's available, otherwise fallback to the
CUDA C++ kernels. CuTeDSL kernels will be registered when
``import transformer_engine`` so this env var should be set before that.

.. envvar:: NVTE_WARN_IF_CUTEDSL_BACKEND_NOT_CHOSEN

:Type: ``int`` (0 or 1)
:Default: ``0``
:Description: Warn TE falls back to the CUDA C++ kernels instead of dispatching to
available CuTeDSL kernels. Useful to check if CuTeDSL path is taken,
since a silent fallback is otherwise indistinguishable from success.

.. envvar:: NVTE_GROUPED_TENSOR_HANDLE_POOL_SIZE_MB

:Type: ``int`` (positive integer)
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# See LICENSE for license information.

[build-system]
requires = ["setuptools>=61.0", "cmake>=3.21", "wheel", "pybind11[global]", "ninja", "pip", "torch>=2.1", "jax>=0.5.0", "flax>=0.7.1", "nvidia-cudnn-frontend>=1.25.0"]
requires = ["setuptools>=61.0", "cmake>=3.21", "wheel", "pybind11[global]", "ninja", "pip", "torch>=2.1", "jax>=0.5.0", "flax>=0.7.1", "nvidia-cudnn-frontend>=1.25.0", "apache-tvm-ffi>=0.1.12"]
Comment thread
greptile-apps[bot] marked this conversation as resolved.

# Use legacy backend to import local packages in setup.py
build-backend = "setuptools.build_meta:__legacy__"
12 changes: 12 additions & 0 deletions qa/L0_jax_unittest/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,18 @@ NVTE_JAX_CUSTOM_CALLS="false" python3 -m pytest -c $TE_PATH/tests/jax/pytest.ini
# single-GPU runners.
CUDA_VISIBLE_DEVICES=0 python3 -m pytest -c $TE_PATH/tests/jax/pytest.ini -v --junitxml=$XML_LOG_DIR/pytest_docs_examples_jax.xml $TE_PATH/docs/examples/jax/ || test_fail "docs/examples/jax"

CUTEDSL_BACKEND_TESTS=(
tests/jax/test_mxfp8_cutedsl_backend.py
tests/jax/test_custom_call_compute.py
tests/jax/test_recipe_characteristics.py
tests/jax/test_layer.py
)
for cutedsl_test in "${CUTEDSL_BACKEND_TESTS[@]}"; do
cutedsl_tag=$(echo "$cutedsl_test" | tr '/.' '__')
NVTE_ENABLE_CUTEDSL_QUANT_BACKEND=1 NVTE_WARN_IF_CUTEDSL_BACKEND_NOT_CHOSEN=1 python3 -m pytest -c $TE_PATH/tests/jax/pytest.ini -s -v --junitxml=$XML_LOG_DIR/pytest_jax_cutedsl_${cutedsl_tag}.xml $TE_PATH/$cutedsl_test -k 'not distributed' || test_fail "cutedsl backend: $cutedsl_test"
done


if [ $RET -ne 0 ]; then
echo "Error: some sub-tests failed: $FAILED_CASES"
exit 1
Expand Down
16 changes: 16 additions & 0 deletions qa/L0_pytorch_unittest/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,22 @@ PYTORCH_JIT=0 NVTE_TORCH_COMPILE=0 python3 -m pytest --tb=auto --junitxml=$XML_L
PYTORCH_JIT=0 NVTE_TORCH_COMPILE=0 python3 -m pytest --tb=auto --junitxml=$XML_LOG_DIR/pytest_test_ops_grouped_linear_distributed_weight.xml $TE_PATH/tests/pytorch/test_ops_grouped_linear_distributed_weight.py || test_fail "test_ops_grouped_linear_distributed_weight.py"
NVTE_GROUPED_LINEAR_SINGLE_PARAM=1 NVTE_CUTEDSL_FUSED_GROUPED_MLP=1 python3 -m pytest --tb=auto --junitxml=$XML_LOG_DIR/pytest_test_grouped_mlp.xml $TE_PATH/tests/pytorch/test_grouped_mlp.py || test_fail "test_grouped_mlp.py"

CUTEDSL_BACKEND_TESTS=(
tests/pytorch/mxfp8
tests/pytorch/test_quantized_tensor.py
tests/pytorch/test_grouped_tensor.py
tests/pytorch/test_cuda_graphs.py
tests/pytorch/test_fusible_ops.py
tests/pytorch/test_numerics.py
tests/pytorch/test_hybrid_quantization.py
tests/pytorch/attention/test_linear_mxfp8_attention.py
tests/pytorch/test_mxfp8_2d_quantize.py
)
for cutedsl_test in "${CUTEDSL_BACKEND_TESTS[@]}"; do
cutedsl_tag=$(echo "$cutedsl_test" | tr '/.' '__')
NVTE_ENABLE_CUTEDSL_QUANT_BACKEND=1 NVTE_WARN_IF_CUTEDSL_BACKEND_NOT_CHOSEN=1 python3 -m pytest -s --tb=auto --junitxml=$XML_LOG_DIR/pytest_cutedsl_${cutedsl_tag}.xml $TE_PATH/$cutedsl_test || test_fail "cutedsl backend: $cutedsl_test"
done

if [ "$RET" -ne 0 ]; then
echo "Error in the following test cases:$FAILED_CASES"
exit 1
Expand Down
2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ def setup_requirements() -> Tuple[List[str], List[str]]:
"pydantic",
"importlib-metadata>=1.0",
"packaging",
"apache-tvm-ffi>=0.1.12",
"nvidia-cutlass-dsl>=4.5.0",
]
test_reqs: List[str] = ["pytest>=8.2.1"]

Expand Down
257 changes: 257 additions & 0 deletions tests/jax/test_mxfp8_cutedsl_backend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
# Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#
# See LICENSE for license information.

"""Cross-backend bit-exactness tests for the CuTeDSL MXFP8 quantize kernels, driven from JAX.

JAX companion to tests/pytorch/mxfp8/test_mxfp8_cutedsl_backend.py: the CuTeDSL dispatch
lives in TE/common, so this checks that the JAX FFI path reaches it and produces the same
bytes as the CUDA kernels.
"""

import ctypes
import os

import jax
import jax.numpy as jnp
import numpy as np
import pytest

from utils import assert_allclose

from transformer_engine.common import _get_shared_object_file
from transformer_engine.jax import cpp_extensions as tex
from transformer_engine.jax.quantize import (
QuantizerFactory,
QuantizeLayout,
ScaledTensor1x,
ScalingMode,
helper,
)

tvm_ffi = pytest.importorskip("tvm_ffi")

recipe_available, reason_for_no_recipe = helper.is_scaling_mode_supported(
ScalingMode.MXFP8_1D_SCALING
)

# The already-loaded core lib (dlopen refcounts: this returns the same handle,
# so the call mutates the same dispatcher singleton the quantize ops read).
CORE_LIB = ctypes.CDLL(str(_get_shared_object_file("core")))
# We need this API to manually enable & disable the CuTeDSL backend for the tests
if not hasattr(CORE_LIB, "nvte_set_cutedsl_quant_backend"):
raise RuntimeError(
"libtransformer_engine.so lacks nvte_set_cutedsl_quant_backend -- rebuild the "
"Transformer Engine core library."
)

# The CuTeDSL entrypoint is registered only when NVTE_ENABLE_CUTEDSL_QUANT_BACKEND
# is set (see common/__init__.py); without it there is nothing to compare against
# the CUDA path, so skip these runs.
cutedsl_enabled = os.environ.get("NVTE_ENABLE_CUTEDSL_QUANT_BACKEND", "0") != "0"
pytestmark = pytest.mark.skipif(
not (recipe_available and cutedsl_enabled),
reason=reason_for_no_recipe or "NVTE_ENABLE_CUTEDSL_QUANT_BACKEND is not set",
)

# CuTeDSL's divisibility assumption strictly requires 32x32 alignment, and the JAX
# MXFP8 scale shapes additionally require 128-alignment for the fused dact paths.
MATRIX_SIZES = [
(128, 128),
(256, 1024),
(512, 512),
(8192, 7168),
]
# QuantizeLayout.COLWISE is absent on purpose: every quantize wrapper diverts colwise-only to a
# pure-JAX implementation before reaching the FFI (_quantize_dbias_impl, act_lu,
# quantize_dact_dbias), so it cannot exercise the kernels. The colwise-only kernels themselves are
# covered by tests/pytorch/mxfp8/test_mxfp8_cutedsl_backend.py.
Q_LAYOUTS = [QuantizeLayout.ROWWISE, QuantizeLayout.ROWWISE_COLWISE]

# Only GeLU activation tests are used (SiLU/ReLU/QGeLU/SReLU commented out
# in the C++ test as well). Gated variants go to a separate TE/common kernel
# that the CuTeDSL backend does not cover.
ACT_TYPE = ("gelu",)
METHODS = ["CAST_ONLY", "CAST_DBIAS", "CAST_ACT", "CAST_DACT", "CAST_DBIAS_DACT"]

IN_DTYPES = [jnp.float32, jnp.bfloat16, jnp.float16]
FP8_DTYPES = [jnp.float8_e4m3fn, jnp.float8_e5m2]
FP8_TO_KEY = {
jnp.float8_e4m3fn: "fp8_e4m3fn",
jnp.float8_e5m2: "fp8_e5m2",
}

get_shape_id = lambda s: f"{s[0]}x{s[1]}"
get_layout_id = lambda l: "rowwise" if l == QuantizeLayout.ROWWISE else "bidim"
DTYPE_TO_STR = {jnp.float32: "fp32", jnp.bfloat16: "bf16", jnp.float16: "fp16"}
get_dtype_id = DTYPE_TO_STR.get
FP8_TO_STR = {jnp.float8_e4m3fn: "e4m3", jnp.float8_e5m2: "e5m2"}
get_fp8_id = FP8_TO_STR.get


def set_cutedsl_backend(enabled):
CORE_LIB.nvte_set_cutedsl_quant_backend(1 if enabled else 0)


@pytest.fixture(scope="module", autouse=True)
def _restore_backend_choice_from_env():
"""Restore the flag that decides the CuTeDSL / CUDA backend choice when this pytest module is done."""
yield
flag = os.getenv("NVTE_ENABLE_CUTEDSL_QUANT_BACKEND")
set_cutedsl_backend(flag is not None and not flag.startswith("0"))


def generate_inputs(M, N, in_dtype, seed=0):
keys = jax.random.split(jax.random.PRNGKey(seed), 4)

def fill(k_value, k_sign):
# Mirrors InputsFillCase::uniform in fillCase_special (tests/cpp/test_common.cu) where the
# uniform range is [-2, 1] and we apply a random sign flip
v = jax.random.uniform(k_value, (M, N), jnp.float32, -2.0, 1.0)
negate = jax.random.uniform(k_sign, (M, N), jnp.float32, -1.0, 1.0) < 0.0
return jnp.where(negate, -v, v).astype(in_dtype)

x = fill(keys[0], keys[1])
# The activation input is replicated along the -2 axis, one entry per activation
# in the (possibly gated) activation type.
act_input = jnp.expand_dims(fill(keys[2], keys[3]), axis=-2)
return x, act_input


def run_quantize(method, x, act_input, q_layout, fp8_dtype):
"""Quantize via the public dispatch; returns (scaled_tensor, dbias_or_None)."""
quantizer = QuantizerFactory.create(
scaling_mode=ScalingMode.MXFP8_1D_SCALING, q_dtype=fp8_dtype, q_layout=q_layout
)
if method == "CAST_ONLY":
return tex.quantize(x, quantizer=quantizer), None
if method == "CAST_DBIAS":
return tex.quantize_dbias(x, quantizer=quantizer)
if method == "CAST_ACT":
return tex.act_lu(act_input, ACT_TYPE, quantizer=quantizer), None
if method == "CAST_DACT":
out, _ = tex.quantize_dact_dbias(
x, act_input, ACT_TYPE, is_dbias=False, quantizer=quantizer
)
return out, None
if method == "CAST_DBIAS_DACT":
return tex.quantize_dact_dbias(x, act_input, ACT_TYPE, is_dbias=True, quantizer=quantizer)
raise ValueError(f"unknown method {method!r}")


def get_cfg_key(method, in_dtype, fp8_dtype, q_layout):
"""Mirror of MXFP8QuantConfig::to_key (quantize_mxfp8_cutedsl.cuh): the name the CuTeDSL backend
registers its compiled kernel under for this config.
Used to check if the CuTeDSL implementation is registered
"""
with_dbias = method in ("CAST_DBIAS", "CAST_DBIAS_DACT")
with_dact = method in ("CAST_DACT", "CAST_DBIAS_DACT")
with_act = method == "CAST_ACT"
desc = "none"
if with_act:
desc = "gelu"
elif with_dact:
desc = "dgelu"
# MXFP8 never asks TE/common for an amax, and JAX quantize emits scales in the linear
# (non-swizzled) layout -- the GEMM swizzle happens later, in JAX (see gemm.swizzled_scale).
# trailing False is use_2d_quantization; JAX never requests 2D block scaling
flags = (True, q_layout.has_colwise, False, False, with_dbias, with_dact, with_act, False)
return (
"cutedsl_mxfp8_"
+ DTYPE_TO_STR[in_dtype]
+ "_"
+ FP8_TO_KEY[fp8_dtype]
+ "_"
+ "_".join("1" if f else "0" for f in flags)
+ "_"
+ desc
)


def extract_quantized_output(out, dbias):
"""Pull the values to compare between backends onto the host.

Materializing here is what makes the backend toggle safe: JAX dispatch is
asynchronous, so the FFI handler that reads the toggle may not have run yet when the
Python call returns.

ScaledTensor1x carries scale_inv already trimmed to the unpadded shape (see its
__post_init__), so there is no uninitialized scale padding to exclude.
"""
tensors = [out] if isinstance(out, ScaledTensor1x) else [out.rowwise_tensor, out.colwise_tensor]
parts = {}
for t in tensors:
name = "colwise" if t.is_colwise else "rowwise"
parts[f"{name} data"] = np.asarray(t.data.view(jnp.uint8))
parts[f"{name} scales"] = np.asarray(t.scale_inv.view(jnp.uint8))
return parts, None if dbias is None else np.asarray(dbias)


def run_test_case(method, shape, q_layout, in_dtype, fp8_dtype):
"""Assert the CuTeDSL and CUDA backends produce bit-identical outputs for the
same input and config.
"""
M, N = shape
x, act_input = generate_inputs(M, N, in_dtype)

set_cutedsl_backend(False)
cuda_output, dbias_cuda = extract_quantized_output(
*run_quantize(method, x, act_input, q_layout, fp8_dtype)
)

set_cutedsl_backend(True)
try:
cutedsl_output, dbias_cutedsl = extract_quantized_output(
*run_quantize(method, x, act_input, q_layout, fp8_dtype)
)
finally:
set_cutedsl_backend(False)

# Guard against a silent CUDA fallback: every config in the matrix is one the
# CuTeDSL backend supports, so its kernel must have been registered under the
# config key. If not, the backend rejected or missed the config and the
# comparison above was CUDA vs CUDA.
key = get_cfg_key(method, in_dtype, fp8_dtype, q_layout)
assert tvm_ffi.get_global_func(key, allow_missing=True) is not None, (
f"CuTeDSL kernel not registered for {key}; the CuTeDSL backend fell back "
"to CUDA and this case compared CUDA against itself"
)

tag = (
f"{method}/{get_layout_id(q_layout)}/{M}x{N}/"
f"{DTYPE_TO_STR[in_dtype]}/{FP8_TO_STR[fp8_dtype]}"
)
for name, cuda_bytes in cuda_output.items():
assert np.array_equal(
cutedsl_output[name], cuda_bytes
), f"{tag}: {name} differ between backends"
if dbias_cuda is not None:
# CuTeDSL kernel does dbias reduction in a slightly different order than the CUDA kernel,
# due to the non-associativity of floating-point addition, this will not be bit-identical.
assert_allclose(dbias_cutedsl, dbias_cuda, err_msg=f"{tag}: dbias differs between backends")


# Test cases with only cast kernels (mirrors C++ test's OperatorTest_FusedCastMXFP8_CastOnly).
@pytest.mark.parametrize("shape", MATRIX_SIZES, ids=get_shape_id)
@pytest.mark.parametrize("q_layout", Q_LAYOUTS, ids=get_layout_id)
@pytest.mark.parametrize("in_dtype", IN_DTYPES, ids=get_dtype_id)
@pytest.mark.parametrize("fp8_dtype", FP8_DTYPES, ids=get_fp8_id)
def test_cast_only(fp8_dtype, in_dtype, q_layout, shape):
run_test_case("CAST_ONLY", shape, q_layout, in_dtype, fp8_dtype)


# Test cases with varying matrix shapes and quantize layouts
# (OperatorTest_FusedCastMXFP8_Sizes).
@pytest.mark.parametrize("shape", MATRIX_SIZES, ids=get_shape_id)
@pytest.mark.parametrize("q_layout", Q_LAYOUTS, ids=get_layout_id)
@pytest.mark.parametrize("method", METHODS)
def test_sizes(method, q_layout, shape):
run_test_case(method, shape, q_layout, jnp.bfloat16, jnp.float8_e4m3fn)


# Test cases with varying dtypes (OperatorTest_FusedCastMXFP8_Dtypes).
@pytest.mark.parametrize("in_dtype", IN_DTYPES, ids=get_dtype_id)
@pytest.mark.parametrize("fp8_dtype", FP8_DTYPES, ids=get_fp8_id)
@pytest.mark.parametrize("method", METHODS)
def test_dtypes(method, fp8_dtype, in_dtype):
run_test_case(method, (256, 384), QuantizeLayout.ROWWISE_COLWISE, in_dtype, fp8_dtype)
Loading
Loading