# SPDX-License-Identifier: Apache-2.0 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project """Tests for Cutlass W4A16 (Machete) kernel on Hopper. Verifies that W4A16 quantized models loaded through vllm select the MacheteLinearKernel on sm_90 GPUs, that weights are correctly repacked, and that inference produces valid output. Run `pytest tests/quantization/test_cutlass_w4a16.py`. """ import pytest import torch from vllm.platforms import current_platform if not current_platform.has_device_capability(90) or current_platform.is_rocm(): pytest.skip( "Machete W4A16 requires Hopper (sm_90).", allow_module_level=True, ) from vllm.model_executor.kernels.linear import ( MPLinearLayerConfig, choose_mp_linear_kernel, ) from vllm.model_executor.kernels.linear.mixed_precision import ( MacheteLinearKernel, ) from vllm.model_executor.layers.quantization.compressed_tensors.compressed_tensors import ( # noqa: E501 CompressedTensorsLinearMethod, CompressedTensorsWNA16, ) from vllm.scalar_type import scalar_types @pytest.fixture(scope="function", autouse=True) def enable_pickle(monkeypatch): """`LLM.apply_model` requires pickling a function.""" monkeypatch.setenv("VLLM_ALLOW_INSECURE_SERIALIZATION", "1") @pytest.mark.parametrize( "act_type,weight_type,group_size,zero_points", [ (torch.float16, scalar_types.uint4b8, 128, False), (torch.bfloat16, scalar_types.uint4b8, 128, False), (torch.float16, scalar_types.uint4, 128, True), (torch.float16, scalar_types.uint4b8, -1, False), ], ids=[ "fp16-gptq-g128", "bf16-gptq-g128", "fp16-awq-g128", "fp16-channelwise", ], ) def test_machete_kernel_selected(act_type, weight_type, group_size, zero_points): """Verify choose_mp_linear_kernel picks MacheteLinearKernel.""" config = MPLinearLayerConfig( full_weight_shape=(4096, 4096), partition_weight_shape=(4096, 4096), act_type=act_type, weight_type=weight_type, group_size=group_size, zero_points=zero_points, has_g_idx=False, ) kernel = choose_mp_linear_kernel(config) assert kernel is MacheteLinearKernel, ( f"Expected MacheteLinearKernel, got {kernel.__name__}" ) @pytest.mark.parametrize( "full_shape,part_shape,weight_type,group_size,has_g_idx,expected_reason", [ ((4096, 4096), (2048, 4096), scalar_types.uint4b8, 128, True, "Act reordering"), ( (4096, 4096), (4096, 4096), scalar_types.float6_e3m2f, 128, False, "Quant type", ), ((4096, 4096), (4096, 4096), scalar_types.uint4b8, 32, False, "Group size"), ], ids=["partitioned-g_idx", "unsupported-quant-type", "unsupported-group-size"], ) def test_machete_rejects_invalid_config( full_shape, part_shape, weight_type, group_size, has_g_idx, expected_reason ): """Verify Machete rejects unsupported configurations.""" config = MPLinearLayerConfig( full_weight_shape=full_shape, partition_weight_shape=part_shape, act_type=torch.float16, weight_type=weight_type, group_size=group_size, zero_points=False, has_g_idx=has_g_idx, ) can_impl, reason = MacheteLinearKernel.can_implement(config) assert not can_impl assert expected_reason in reason def test_kernel_selection_with_disabled_machete(monkeypatch): """Verify kernel selection falls back when Machete is disabled.""" monkeypatch.setattr("vllm.envs.VLLM_DISABLED_KERNELS", ["MacheteLinearKernel"]) config = MPLinearLayerConfig( full_weight_shape=(4096, 4096), partition_weight_shape=(4096, 4096), act_type=torch.float16, weight_type=scalar_types.uint4b8, group_size=128, zero_points=False, has_g_idx=False, ) kernel = choose_mp_linear_kernel(config) assert kernel is not MacheteLinearKernel, "MacheteLinearKernel should be disabled" @pytest.mark.parametrize( "model_name", [ "nm-testing/tinyllama-oneshot-w4a16-channel-v2", "nm-testing/TinyLlama-1.1B-Chat-v1.0-W4A16-G128-Asym-Updated-ActOrder", ], ) def test_w4a16_machete_e2e(vllm_runner, model_name): """Load a W4A16 model, verify Machete kernel is used, and generate.""" with vllm_runner(model_name, enforce_eager=True, gpu_memory_utilization=0.5) as llm: def check_model(model): layer = model.model.layers[0] qkv_proj = layer.self_attn.qkv_proj assert isinstance(qkv_proj.quant_method, CompressedTensorsLinearMethod) assert isinstance(qkv_proj.scheme, CompressedTensorsWNA16) assert isinstance(qkv_proj.scheme.kernel, MacheteLinearKernel), ( f"Expected MacheteLinearKernel on Hopper, " f"got {type(qkv_proj.scheme.kernel).__name__}" ) assert hasattr(qkv_proj, "weight_packed") assert hasattr(qkv_proj, "weight_scale") assert qkv_proj.weight_packed.dtype == torch.int32 llm.apply_model(check_model) output = llm.generate_greedy("Hello my name is", max_tokens=10) assert output assert len(output[0][1]) > 0 def test_w4a16_machete_bfloat16_deterministic(vllm_runner): """Verify Machete works with bf16 activations and is deterministic.""" model_name = "nm-testing/tinyllama-oneshot-w4a16-channel-v2" prompt = "The capital of France is" with vllm_runner( model_name, enforce_eager=True, dtype="bfloat16", gpu_memory_utilization=0.5, ) as llm: def check_kernel_type(model): layer = model.model.layers[0] scheme = layer.self_attn.qkv_proj.scheme assert isinstance(scheme.kernel, MacheteLinearKernel), ( f"Expected MacheteLinearKernel with bf16, " f"got {type(scheme.kernel).__name__}" ) llm.apply_model(check_kernel_type) out1 = llm.generate_greedy(prompt, max_tokens=10) out2 = llm.generate_greedy(prompt, max_tokens=10) assert out1[0][1] == out2[0][1], ( f"Non-deterministic: '{out1[0][1]}' vs '{out2[0][1]}'" )