@@ -1182,16 +1182,25 @@ def test_fused_marlin_moe_non_gated(
11821182 torch .testing .assert_close (marlin_output , torch_output , atol = 1e-1 , rtol = 0 )
11831183
11841184
1185- def _make_humming_indexed_experts (activation : MoEActivation ):
1185+ def _make_humming_indexed_experts (
1186+ activation : MoEActivation ,
1187+ * ,
1188+ weight_schema : Any = None ,
1189+ weight_schemas : dict [str , Any ] | None = None ,
1190+ tensor_factory : Callable [..., torch .Tensor ] | None = None ,
1191+ top_k : int = 6 ,
1192+ num_experts : int = 12 ,
1193+ hidden_size : int = 2688 ,
1194+ intermediate_size : int = 1856 ,
1195+ swiglu_limit : float | None = None ,
1196+ ):
11861197 pytest .importorskip ("humming" )
11871198 from vllm .model_executor .layers .fused_moe .experts .fused_humming_moe import (
11881199 HummingIndexedExperts ,
11891200 )
11901201 from vllm .model_executor .layers .quantization .utils import humming_utils
11911202 from vllm .utils import humming
11921203
1193- top_k , num_experts = 6 , 12
1194- hidden_size , intermediate_size = 2688 , 1856
11951204 gate_up_size = intermediate_size * 2 if activation .is_gated else intermediate_size
11961205 num_w13_stacks = 2 if activation .is_gated else 1
11971206 moe_config = make_dummy_moe_config (
@@ -1205,35 +1214,51 @@ def _make_humming_indexed_experts(activation: MoEActivation):
12051214 layer = torch .nn .Module ()
12061215 layer .moe_config = moe_config
12071216 layer .params_dtype = torch .bfloat16
1217+ layer .swiglu_limit = swiglu_limit
12081218
1209- weight_schema = humming .ModeloptNvfp4WeightSchema ()
1219+ weight_schema = weight_schema or humming .ModeloptNvfp4WeightSchema ()
12101220 for sublayer_name , shape_n , shape_k , stack_size in (
12111221 ("w13" , gate_up_size , hidden_size , num_w13_stacks ),
12121222 ("w2" , hidden_size , intermediate_size , 1 ),
12131223 ):
1214- tensor_attrs = weight_schema .get_tensors_attrs (
1224+ sublayer_weight_schema = (
1225+ weight_schemas .get (sublayer_name , weight_schema )
1226+ if weight_schemas is not None
1227+ else weight_schema
1228+ )
1229+ tensor_attrs = sublayer_weight_schema .get_tensors_attrs (
12151230 shape_n = shape_n ,
12161231 shape_k = shape_k ,
12171232 param_dtype = layer .params_dtype ,
12181233 num_experts = num_experts ,
12191234 stack_size = stack_size ,
12201235 )
12211236 for tensor_name , attrs in tensor_attrs .items ():
1237+ tensor = (
1238+ tensor_factory (
1239+ sublayer_name = sublayer_name ,
1240+ tensor_name = tensor_name ,
1241+ attrs = attrs ,
1242+ shape_n = shape_n ,
1243+ shape_k = shape_k ,
1244+ num_experts = num_experts ,
1245+ )
1246+ if tensor_factory is not None
1247+ else torch .ones (
1248+ attrs ["shape" ],
1249+ dtype = attrs ["dtype" ],
1250+ device = "cuda" ,
1251+ )
1252+ )
12221253 layer .register_parameter (
12231254 f"{ sublayer_name } _{ tensor_name } " ,
1224- Parameter (
1225- torch .ones (
1226- attrs ["shape" ],
1227- dtype = attrs ["dtype" ],
1228- device = "cuda" ,
1229- ),
1230- requires_grad = False ,
1231- ),
1255+ Parameter (tensor , requires_grad = False ),
12321256 )
12331257
12341258 humming_utils .convert_to_humming_moe_kernel_format (
12351259 layer ,
12361260 weight_schema = weight_schema ,
1261+ weight_schemas = weight_schemas ,
12371262 input_schema = humming .HummingInputSchema (a_dtype = humming .dtypes .bfloat16 ),
12381263 )
12391264
@@ -1350,6 +1375,167 @@ def test_humming_indexed_writes_supplied_output_buffer():
13501375 assert torch .isfinite (output ).all ()
13511376
13521377
1378+ @pytest .mark .skipif (not current_platform .is_cuda (), reason = "Requires CUDA" )
1379+ @pytest .mark .parametrize (
1380+ ("w13_group_size" , "w2_group_size" , "down_bits" ),
1381+ [
1382+ (128 , 128 , 2 ),
1383+ (256 , 128 , 2 ),
1384+ (512 , 256 , 2 ),
1385+ (512 , 512 , 2 ),
1386+ (512 , 128 , 4 ),
1387+ (512 , 256 , 4 ),
1388+ (512 , 512 , 4 ),
1389+ ],
1390+ ids = [
1391+ "w2-g128-g128" ,
1392+ "w2-g256-g128" ,
1393+ "w2-g512-g256" ,
1394+ "w2-g512-g512" ,
1395+ "w4-g512-g128" ,
1396+ "w4-g512-g256" ,
1397+ "w4-g512-g512" ,
1398+ ],
1399+ )
1400+ def test_humming_wna16_grouped_indexed_numerical_oracle (
1401+ w13_group_size , w2_group_size , down_bits
1402+ ):
1403+ from vllm .forward_context import set_forward_context
1404+ from vllm .utils import humming
1405+
1406+ weight_scale = 2 ** - 5
1407+ swiglu_limit = 10.0
1408+ weight_schemas = {
1409+ sublayer_name : humming .CompressedTensorsWeightSchema (
1410+ format = "pack-quantized" ,
1411+ type = "int" ,
1412+ num_bits = bits ,
1413+ strategy = "group" ,
1414+ symmetric = True ,
1415+ group_size = group_size ,
1416+ )
1417+ for sublayer_name , bits , group_size in (
1418+ ("w13" , 2 , w13_group_size ),
1419+ ("w2" , down_bits , w2_group_size ),
1420+ )
1421+ }
1422+
1423+ def make_positive_unit_weights (
1424+ * ,
1425+ sublayer_name : str ,
1426+ tensor_name : str ,
1427+ attrs : dict [str , Any ],
1428+ shape_n : int ,
1429+ shape_k : int ,
1430+ num_experts : int ,
1431+ ) -> torch .Tensor :
1432+ if tensor_name == "weight_packed" :
1433+ # Compressed-tensors offsets signed codes into unsigned lanes.
1434+ # W2 +1 is lane 0b11; W4 +1 is lane 0b1001.
1435+ packed_positive_one = - 1 if sublayer_name == "w13" else - 1717986919
1436+ if sublayer_name == "w2" and down_bits == 2 :
1437+ packed_positive_one = - 1
1438+ return torch .full (
1439+ attrs ["shape" ],
1440+ packed_positive_one ,
1441+ dtype = attrs ["dtype" ],
1442+ device = "cuda" ,
1443+ )
1444+ if tensor_name == "weight_scale" :
1445+ return torch .full (
1446+ attrs ["shape" ],
1447+ weight_scale ,
1448+ dtype = attrs ["dtype" ],
1449+ device = "cuda" ,
1450+ )
1451+ assert tensor_name == "weight_shape"
1452+ return torch .tensor (
1453+ [[shape_n , shape_k ]] * num_experts ,
1454+ dtype = attrs ["dtype" ],
1455+ device = "cuda" ,
1456+ )
1457+
1458+ activation = MoEActivation .SILU
1459+ experts = _make_humming_indexed_experts (
1460+ activation ,
1461+ weight_schemas = weight_schemas ,
1462+ tensor_factory = make_positive_unit_weights ,
1463+ top_k = 2 ,
1464+ num_experts = 4 ,
1465+ hidden_size = 512 ,
1466+ intermediate_size = 512 ,
1467+ swiglu_limit = swiglu_limit ,
1468+ )
1469+ layer = experts .layer
1470+ for sublayer_name , expected_dtype , expected_group_size in (
1471+ ("w13" , humming .dtypes .uint2 , w13_group_size ),
1472+ (
1473+ "w2" ,
1474+ humming .dtypes .uint2 if down_bits == 2 else humming .dtypes .uint4 ,
1475+ w2_group_size ,
1476+ ),
1477+ ):
1478+ meta = layer .humming_metas [sublayer_name ]
1479+ assert meta .a_dtype == humming .dtypes .bfloat16
1480+ assert meta .b_dtype == expected_dtype
1481+ assert meta .weight_scale_group_size == expected_group_size
1482+
1483+ num_tokens = 4
1484+ top_k = experts .moe_config .experts_per_token
1485+ hidden_size = experts .moe_config .hidden_dim
1486+ intermediate_size = experts .moe_config .intermediate_size
1487+ num_experts = experts .moe_config .num_experts
1488+ workspace13_shape , workspace2_shape , _ = experts .workspace_shapes (
1489+ M = num_tokens ,
1490+ N = intermediate_size ,
1491+ K = hidden_size ,
1492+ topk = top_k ,
1493+ global_num_experts = num_experts ,
1494+ local_num_experts = num_experts ,
1495+ expert_tokens_meta = None ,
1496+ activation = activation ,
1497+ )
1498+ dtype = layer .params_dtype
1499+ workspace13 = torch .empty (workspace13_shape , dtype = dtype , device = "cuda" )
1500+ workspace2 = torch .empty (workspace2_shape , dtype = dtype , device = "cuda" )
1501+ hidden_states = torch .ones ((num_tokens , hidden_size ), dtype = dtype , device = "cuda" )
1502+ output = torch .full_like (hidden_states , torch .nan )
1503+ topk_weights = torch .full (
1504+ (num_tokens , top_k ),
1505+ 1 / top_k ,
1506+ dtype = dtype ,
1507+ device = "cuda" ,
1508+ )
1509+ topk_ids = torch .zeros ((num_tokens , top_k ), dtype = torch .int32 , device = "cuda" )
1510+ unused = torch .empty ((num_experts , 0 ), device = "cuda" )
1511+
1512+ with set_forward_context (None , vllm_config , num_tokens = num_tokens ):
1513+ experts .apply (
1514+ output = output ,
1515+ hidden_states = hidden_states ,
1516+ w1 = unused ,
1517+ w2 = unused ,
1518+ topk_weights = topk_weights ,
1519+ topk_ids = topk_ids ,
1520+ activation = activation ,
1521+ global_num_experts = num_experts ,
1522+ expert_map = None ,
1523+ a1q_scale = None ,
1524+ a2_scale = None ,
1525+ workspace13 = workspace13 ,
1526+ workspace2 = workspace2 ,
1527+ expert_tokens_meta = None ,
1528+ apply_router_weight_on_input = False ,
1529+ )
1530+
1531+ gate_up = hidden_size * weight_scale
1532+ clamped_gate_up = min (gate_up , swiglu_limit )
1533+ expected_value = F .silu (torch .tensor (clamped_gate_up )) * clamped_gate_up
1534+ expected_value *= intermediate_size * weight_scale
1535+ expected = torch .full_like (output , expected_value .item ())
1536+ torch .testing .assert_close (output , expected , atol = 5e-3 , rtol = 2e-2 )
1537+
1538+
13531539@pytest .mark .parametrize ("ep_size" , [1 , 2 ])
13541540def test_moe_align_block_size_opcheck (ep_size ):
13551541 num_experts = 4
0 commit comments