1818import transformer_engine .common .recipe
1919import transformer_engine .pytorch as te
2020import transformer_engine .pytorch .ops as te_ops
21+ from transformer_engine .pytorch .ops ._common import (
22+ _nvidia_cudnn_frontend_supports_scaled_clamped_qgeglu ,
23+ )
2124
2225from transformer_engine .pytorch .ops .fused import (
2326 BackwardActivationBias ,
@@ -2234,6 +2237,91 @@ def test_interleaved_scaled_swiglu(self):
22342237 scales_requires_grad = True ,
22352238 )
22362239
2240+ @pytest .mark .parametrize ("in_shape" , ((71 , 192 ), (5 , 7 , 128 )))
2241+ @pytest .mark .parametrize ("input_requires_grad" , (False , True ))
2242+ @pytest .mark .parametrize ("scales_requires_grad" , (False , True ))
2243+ def test_scaled_clamped_qgeglu (
2244+ self ,
2245+ * ,
2246+ in_shape : Iterable [int ],
2247+ glu_interleave_size : Optional [int ] = None ,
2248+ dtype : torch .dtype = torch .float32 ,
2249+ device : torch .device = "cuda" ,
2250+ input_requires_grad : bool ,
2251+ scales_requires_grad : bool ,
2252+ limit : float = 7.0 ,
2253+ alpha : float = 1.702 ,
2254+ ) -> None :
2255+ """ScaledClampedQGeGLU (clamped QGeGLU with post-scale)"""
2256+
2257+ # Tensor dims
2258+ out_shape = list (in_shape )
2259+ out_shape [- 1 ] //= 2
2260+
2261+ # Random data
2262+ x_ref , x_test = make_reference_and_test_tensors (
2263+ in_shape ,
2264+ test_dtype = dtype ,
2265+ test_device = device ,
2266+ requires_grad = input_requires_grad ,
2267+ )
2268+ scales_ref , scales_test = make_reference_and_test_tensors (
2269+ in_shape [:- 1 ],
2270+ test_dtype = dtype ,
2271+ test_device = device ,
2272+ requires_grad = scales_requires_grad ,
2273+ )
2274+ dy_ref , dy_test = make_reference_and_test_tensors (
2275+ out_shape ,
2276+ test_dtype = dtype ,
2277+ test_device = device ,
2278+ requires_grad = False ,
2279+ )
2280+
2281+ # Plain PyTorch reference (matches :class:`ClampedSwiGLU` numerics)
2282+ x = x_ref
2283+ if glu_interleave_size is not None :
2284+ x = x .reshape (
2285+ - 1 ,
2286+ in_shape [- 1 ] // (2 * glu_interleave_size ),
2287+ 2 ,
2288+ glu_interleave_size ,
2289+ )
2290+ x = x .transpose (1 , 2 )
2291+ x = x .reshape (in_shape )
2292+ x_glu , x_linear = x .chunk (2 , dim = - 1 )
2293+ x_glu = x_glu .clamp (min = None , max = limit )
2294+ x_linear = x_linear .clamp (min = - limit , max = limit )
2295+ out_glu = x_glu * torch .sigmoid (alpha * x_glu )
2296+ y = out_glu * (x_linear + 1 )
2297+ y_ref = scales_ref .unsqueeze (- 1 ) * y
2298+ if input_requires_grad or scales_requires_grad :
2299+ y_ref .backward (dy_ref )
2300+
2301+ op = te_ops .ScaledClampedQGeGLU (
2302+ glu_interleave_size = glu_interleave_size ,
2303+ limit = limit ,
2304+ alpha = alpha ,
2305+ )
2306+ y_test = op (x_test , scales_test )
2307+ if input_requires_grad or scales_requires_grad :
2308+ y_test .backward (dy_test )
2309+
2310+ tols = dtype_tols (dtype )
2311+ y_test = y_test .to (dtype = torch .float64 , device = "cpu" )
2312+ assert_close (y_test , y_ref , ** tols )
2313+ assert_close_grads (x_test , x_ref , ** tols )
2314+ assert_close_grads (scales_test , scales_ref , ** tols )
2315+
2316+ def test_interleaved_scaled_clamped_qgeglu (self ):
2317+ """ScaledClampedQGeGLU with block interleaved input format"""
2318+ self .test_scaled_clamped_qgeglu (
2319+ in_shape = (32 , 192 ),
2320+ glu_interleave_size = 32 ,
2321+ input_requires_grad = True ,
2322+ scales_requires_grad = True ,
2323+ )
2324+
22372325
22382326class TestFusedOps :
22392327 """Tests for fused operations"""
@@ -3249,6 +3337,7 @@ def to_cpu(tensor: Optional[torch.Tensor]) -> Optional[torch.Tensor]:
32493337 @pytest .mark .parametrize ("accumulate_into_main_grad" , (False , True ))
32503338 @pytest .mark .parametrize ("glu_interleave_size" , (None , 32 ))
32513339 @pytest .mark .parametrize ("delay_wgrad_compute" , (False , True ))
3340+ @pytest .mark .parametrize ("activation" , ("scaled_swiglu" , "scaled_clamped_qgeglu" ))
32523341 def test_grouped_mlp (
32533342 self ,
32543343 * ,
@@ -3264,8 +3353,9 @@ def test_grouped_mlp(
32643353 split_alignment : int = 256 ,
32653354 glu_interleave_size : Optional [int ],
32663355 delay_wgrad_compute : bool ,
3356+ activation : str ,
32673357 ) -> None :
3268- """GroupedLinear + ScaledSwiGLU + GroupedLinear"""
3358+ """GroupedLinear + ScaledSwiGLU / ScaledClampedQGeGLU + GroupedLinear"""
32693359
32703360 # Split sizes
32713361 split_sizes = [split_alignment * (i ) for i in range (group_size )]
@@ -3288,6 +3378,9 @@ def test_grouped_mlp(
32883378 if quantization == "mxfp8" and bias :
32893379 # Will be supported in future CUDNN release.
32903380 pytest .skip ("Bias/dbias not yet supported in MXFP8 fused grouped MLP" )
3381+ if quantization == "nvfp4" and activation == "scaled_clamped_qgeglu" and bias :
3382+ # TODO: ksivaman: Need to debug numerics for this case.
3383+ pytest .skip ("Bias/dbias not yet supported in NVFP4 fused grouped MLP with GeGLU" )
32913384
32923385 # Random data
32933386 x_ref , x_test = make_reference_and_test_tensors (
@@ -3376,7 +3469,14 @@ def test_grouped_mlp(
33763469 x = x .transpose (1 , 2 )
33773470 x = x .reshape (- 1 , 2 * hidden_size )
33783471 x1 , x2 = x .chunk (2 , dim = - 1 )
3379- x = torch .nn .functional .silu (x1 ) * x2
3472+ if activation == "scaled_swiglu" :
3473+ x = torch .nn .functional .silu (x1 ) * x2
3474+ else :
3475+ lim = torch .tensor (7.0 , device = x1 .device , dtype = x1 .dtype )
3476+ geglu_alpha = 1.702
3477+ x1c = torch .minimum (x1 , lim )
3478+ x2c = torch .clamp (x2 , - lim , lim )
3479+ x = (x2c + 1 ) * (x1c * torch .sigmoid (geglu_alpha * x1c ))
33803480 x = x * probs [group_idx ].unsqueeze (- 1 )
33813481 x = torch .nn .functional .linear (x , fc2_ws_ref [group_idx ], bias = fc2_bs_ref [group_idx ])
33823482 ys .append (x )
@@ -3385,6 +3485,11 @@ def test_grouped_mlp(
33853485
33863486 # Construct operations
33873487 recipe = make_recipe (quantization )
3488+ scaled_act = (
3489+ te_ops .ScaledSwiGLU (glu_interleave_size = glu_interleave_size )
3490+ if activation == "scaled_swiglu"
3491+ else te_ops .ScaledClampedQGeGLU (glu_interleave_size = glu_interleave_size )
3492+ )
33883493 with te .quantized_model_init (enabled = with_quantization , recipe = recipe ):
33893494 fc1 = te_ops .GroupedLinear (
33903495 group_size ,
@@ -3412,7 +3517,7 @@ def test_grouped_mlp(
34123517 )
34133518 module = te_ops .Sequential (
34143519 fc1 ,
3415- te_ops . ScaledSwiGLU ( glu_interleave_size = glu_interleave_size ) ,
3520+ scaled_act ,
34163521 fc2 ,
34173522 )
34183523
@@ -3484,6 +3589,10 @@ def test_grouped_mlp(
34843589 quantization == "mxfp8"
34853590 and dtype in (torch .bfloat16 , torch .float16 )
34863591 and glu_interleave_size == 32
3592+ and (
3593+ activation != "scaled_clamped_qgeglu"
3594+ or _nvidia_cudnn_frontend_supports_scaled_clamped_qgeglu ()
3595+ )
34873596 ):
34883597 if te_ops .fused .ForwardGroupedMLP_CuTeGEMMSwiGLU_MXFP8 .is_supported ():
34893598 forward_ops = module ._module_groups [0 ]._forward_ops
@@ -3572,13 +3681,15 @@ def test_grouped_mlp(
35723681 @pytest .mark .parametrize ("dtype" , _dtypes )
35733682 @pytest .mark .parametrize ("single_grouped_weight" , (False , True ))
35743683 @pytest .mark .parametrize ("accumulate_into_main_grad" , (False , True ))
3684+ @pytest .mark .parametrize ("activation" , ("scaled_swiglu" , "scaled_clamped_qgeglu" ))
35753685 @pytest .mark .skipif (not mxfp8_available , reason = reason_for_no_mxfp8 )
35763686 def test_grouped_mlp_cuda_graph_safe_mxfp8 (
35773687 self ,
35783688 * ,
35793689 dtype : torch .dtype ,
35803690 single_grouped_weight : bool ,
35813691 accumulate_into_main_grad : bool ,
3692+ activation : str ,
35823693 device : torch .device = "cuda" ,
35833694 group_size : int = 4 ,
35843695 hidden_size : int = 256 ,
@@ -3591,6 +3702,12 @@ def test_grouped_mlp_cuda_graph_safe_mxfp8(
35913702 pytest .skip ("MXFP8 fused grouped MLP is not supported on this system" )
35923703 if dtype not in (torch .bfloat16 , torch .float16 ):
35933704 pytest .skip ("MXFP8 fused grouped MLP is only supported with BF16/FP16" )
3705+ if activation == "scaled_clamped_qgeglu" and not (
3706+ _nvidia_cudnn_frontend_supports_scaled_clamped_qgeglu ()
3707+ ):
3708+ pytest .skip (
3709+ "ScaledClampedQGeGLU fused grouped MLP requires nvidia-cudnn-frontend >= 1.23.0"
3710+ )
35943711
35953712 split_sizes = [split_alignment * (i + 1 ) for i in range (group_size )]
35963713 random .shuffle (split_sizes )
@@ -3619,9 +3736,14 @@ def test_grouped_mlp_cuda_graph_safe_mxfp8(
36193736 single_grouped_weight = single_grouped_weight ,
36203737 accumulate_into_main_grad = accumulate_into_main_grad ,
36213738 )
3739+ scaled_act = (
3740+ te_ops .ScaledSwiGLU (glu_interleave_size = glu_interleave_size )
3741+ if activation == "scaled_swiglu"
3742+ else te_ops .ScaledClampedQGeGLU (glu_interleave_size = glu_interleave_size )
3743+ )
36223744 module = te_ops .Sequential (
36233745 fc1 ,
3624- te_ops . ScaledSwiGLU ( glu_interleave_size = glu_interleave_size ) ,
3746+ scaled_act ,
36253747 fc2 ,
36263748 )
36273749
0 commit comments