@@ -25,6 +25,23 @@ def _mesh():
2525 return Mesh (np .asarray (devices [:4 ]).reshape (2 , 2 ), ("expert" , "fsdp" ))
2626
2727
28+ def _mesh_with_dp_tp ():
29+ devices = jax .devices ()
30+ if len (devices ) < 4 :
31+ pytest .skip ("Grouped GEMM partitioning tests require at least 4 visible GPUs." )
32+ return Mesh (np .asarray (devices [:4 ]).reshape (2 , 1 , 2 , 1 ), ("expert" , "dp" , "fsdp" , "tp" ))
33+
34+
35+ def _mesh_with_arbitrary_axis ():
36+ devices = jax .devices ()
37+ if len (devices ) < 4 :
38+ pytest .skip ("Grouped GEMM partitioning tests require at least 4 visible GPUs." )
39+ return Mesh (
40+ np .asarray (devices [:4 ]).reshape (2 , 1 , 2 , 1 ),
41+ ("expert" , "dp" , "fsdp" , "myaxis123" ),
42+ )
43+
44+
2845def _arg_info (mesh , shape , spec ):
2946 return SimpleNamespace (
3047 shape = shape ,
@@ -40,6 +57,14 @@ def _normalize_spec(spec):
4057 return spec
4158
4259
60+ def _spec_contains_axis (spec , axis ):
61+ for axis_spec in spec :
62+ axis_tuple = axis_spec if isinstance (axis_spec , tuple ) else (axis_spec ,)
63+ if axis in axis_tuple :
64+ return True
65+ return False
66+
67+
4368def _mxfp8_grouped_quantizer_set (n_groups ):
4469 return QuantizerFactory .create_set (
4570 scaling_mode = ScalingMode .MXFP8_1D_SCALING ,
@@ -50,10 +75,10 @@ def _mxfp8_grouped_quantizer_set(n_groups):
5075 )
5176
5277
53- def test_grouped_quantize_specs_preserve_ep_and_fsdp_for_block_scales ():
78+ def test_grouped_quantize_gathers_hidden_axis_for_block_scales ():
5479 mesh = _mesh ()
5580 with global_shard_guard (MeshResource (fsdp_resource = "fsdp" , ep_resource = "expert" )):
56- _ , _ , out_shardings , _ = GroupedQuantizePrimitive .partition (
81+ _ , _ , out_shardings , arg_shardings = GroupedQuantizePrimitive .partition (
5782 jnp .float8_e4m3fn ,
5883 ScalingMode .MXFP8_1D_SCALING .value ,
5984 QuantizeLayout .ROWWISE ,
@@ -68,16 +93,17 @@ def test_grouped_quantize_specs_preserve_ep_and_fsdp_for_block_scales():
6893 (),
6994 )
7095
96+ assert tuple (arg_shardings [0 ].spec ) == ("expert" , None , None )
7197 specs = tuple (tuple (sharding .spec ) for sharding in out_shardings )
72- assert _normalize_spec (specs [0 ]) == (( "expert" , "fsdp" ) ,)
73- assert _normalize_spec (specs [2 ]) == (( "expert" , "fsdp" ) ,)
98+ assert _normalize_spec (specs [0 ]) == ("expert" ,)
99+ assert _normalize_spec (specs [2 ]) == ("expert" ,)
74100 assert _normalize_spec (specs [4 ]) == ("expert" ,)
75101
76102
77- def test_grouped_quantize_mxfp8_colwise_specs_preserve_ep_and_fsdp ():
103+ def test_grouped_quantize_mxfp8_colwise_specs_gather_hidden_axis ():
78104 mesh = _mesh ()
79105 with global_shard_guard (MeshResource (fsdp_resource = "fsdp" , ep_resource = "expert" )):
80- _ , _ , out_shardings , _ = GroupedQuantizePrimitive .partition (
106+ _ , _ , out_shardings , arg_shardings = GroupedQuantizePrimitive .partition (
81107 jnp .float8_e4m3fn ,
82108 ScalingMode .MXFP8_1D_SCALING .value ,
83109 QuantizeLayout .ROWWISE_COLWISE ,
@@ -92,14 +118,47 @@ def test_grouped_quantize_mxfp8_colwise_specs_preserve_ep_and_fsdp():
92118 (),
93119 )
94120
121+ assert tuple (arg_shardings [0 ].spec ) == ("expert" , None , None )
95122 specs = tuple (tuple (sharding .spec ) for sharding in out_shardings )
96- assert _normalize_spec (specs [0 ]) == (( "expert" , "fsdp" ) ,)
97- assert _normalize_spec (specs [1 ]) == (( "expert" , "fsdp" ) ,)
98- assert _normalize_spec (specs [2 ]) == (( "expert" , "fsdp" ) ,)
99- assert _normalize_spec (specs [3 ]) == (( "expert" , "fsdp" ) ,)
123+ assert _normalize_spec (specs [0 ]) == ("expert" ,)
124+ assert _normalize_spec (specs [1 ]) == ("expert" ,)
125+ assert _normalize_spec (specs [2 ]) == ("expert" ,)
126+ assert _normalize_spec (specs [3 ]) == ("expert" ,)
100127 assert _normalize_spec (specs [4 ]) == ("expert" ,)
101128
102129
130+ def test_grouped_quantize_strips_unsupported_axes_and_gathers_hidden_axes ():
131+ mesh = _mesh_with_dp_tp ()
132+ with jax .set_mesh (mesh ), global_shard_guard (
133+ MeshResource (dp_resource = "dp" , tp_resource = "tp" , fsdp_resource = "fsdp" , ep_resource = "expert" )
134+ ):
135+ _ , _ , out_shardings , arg_shardings = GroupedQuantizePrimitive .partition (
136+ jnp .float8_e4m3fn ,
137+ ScalingMode .MXFP8_1D_SCALING .value ,
138+ QuantizeLayout .ROWWISE ,
139+ - 1 ,
140+ jnp .float8_e8m0fnu ,
141+ mesh ,
142+ (
143+ _arg_info (mesh , (8 , 128 , 128 ), ("expert" , "dp" , ("fsdp" , "tp" ))),
144+ _arg_info (mesh , (8 ,), (("expert" , "tp" ),)),
145+ _arg_info (mesh , (8 ,), (("expert" , "tp" ),)),
146+ ),
147+ (),
148+ )
149+
150+ assert tuple (arg_shardings [0 ].spec ) == ("expert" , None , None )
151+ assert tuple (arg_shardings [1 ].spec ) == ("expert" ,)
152+ assert tuple (arg_shardings [2 ].spec ) == ("expert" ,)
153+
154+ out_specs = tuple (tuple (sharding .spec ) for sharding in out_shardings )
155+ assert _normalize_spec (out_specs [0 ]) == ("expert" ,)
156+ assert _normalize_spec (out_specs [2 ]) == ("expert" ,)
157+ assert _normalize_spec (out_specs [4 ]) == ("expert" ,)
158+ for spec in (* out_specs , * (tuple (sharding .spec ) for sharding in arg_shardings )):
159+ assert not _spec_contains_axis (spec , "tp" )
160+
161+
103162def test_grouped_gemm_rhs_weight_specs_gather_fsdp_but_preserve_ep ():
104163 mesh = _mesh ()
105164 arg_infos = (
@@ -143,6 +202,169 @@ def test_grouped_gemm_rhs_weight_specs_gather_fsdp_but_preserve_ep():
143202 assert tuple (out_sharding [0 ].spec ) == (None , None , None )
144203
145204
205+ def test_grouped_gemm_strips_unsupported_axes_preserves_dp_and_gathers_rhs_fsdp ():
206+ mesh = _mesh_with_dp_tp ()
207+ arg_infos = (
208+ _arg_info (mesh , (8192 ,), (("dp" , "tp" ),)),
209+ _arg_info (mesh , (0 ,), (("tp" ,),)),
210+ _arg_info (mesh , (65536 ,), (("expert" , "fsdp" , "tp" ),)),
211+ _arg_info (mesh , (2048 ,), (("expert" , "fsdp" , "tp" ),)),
212+ _arg_info (mesh , (0 ,), (("fsdp" , "tp" ),)),
213+ _arg_info (mesh , (8 ,), (("expert" , "tp" ),)),
214+ _arg_info (mesh , (0 ,), (("tp" ,),)),
215+ _arg_info (mesh , (0 ,), (("tp" ,),)),
216+ _arg_info (mesh , (0 ,), (("tp" ,),)),
217+ _arg_info (mesh , (8 ,), (("expert" , "tp" ),)),
218+ _arg_info (mesh , (0 ,), (("tp" ,),)),
219+ _arg_info (mesh , (1 ,), (("tp" ,),)),
220+ _arg_info (mesh , (0 ,), (("tp" ,),)),
221+ )
222+ result_infos = (_arg_info (mesh , (1 , 128 , 64 ), ("expert" , "tp" , None )),)
223+ with jax .set_mesh (mesh ), global_shard_guard (
224+ MeshResource (dp_resource = "dp" , tp_resource = "tp" , fsdp_resource = "fsdp" , ep_resource = "expert" )
225+ ):
226+ _ , _ , out_sharding , arg_shardings = GroupedGemmPrimitive .partition (
227+ False ,
228+ False ,
229+ ScalingMode .NO_SCALING .value ,
230+ jnp .bfloat16 ,
231+ False ,
232+ False ,
233+ False ,
234+ 1 ,
235+ 1 ,
236+ (1 , 128 , 64 ),
237+ 128 ,
238+ 64 ,
239+ 128 ,
240+ 64 ,
241+ mesh ,
242+ arg_infos ,
243+ result_infos ,
244+ )
245+
246+ assert tuple (arg_shardings [0 ].spec ) == ("dp" ,)
247+ assert tuple (arg_shardings [2 ].spec ) == ("expert" ,)
248+ assert tuple (arg_shardings [3 ].spec ) == ("expert" ,)
249+ assert tuple (arg_shardings [5 ].spec ) == ("expert" ,)
250+ assert tuple (out_sharding [0 ].spec ) == ("expert" , None , None )
251+ for spec in (
252+ * (tuple (sharding .spec ) for sharding in arg_shardings ),
253+ tuple (out_sharding [0 ].spec ),
254+ ):
255+ assert not _spec_contains_axis (spec , "tp" )
256+
257+
258+ def test_grouped_gemm_reduce_axis_skips_ep_and_uses_dp ():
259+ mesh = _mesh_with_dp_tp ()
260+ arg_infos = (
261+ _arg_info (mesh , (8192 ,), (("expert" , "dp" ),)),
262+ _arg_info (mesh , (0 ,), (None ,)),
263+ _arg_info (mesh , (8192 ,), (("expert" , "dp" ),)),
264+ _arg_info (mesh , (0 ,), (None ,)),
265+ _arg_info (mesh , (0 ,), (None ,)),
266+ _arg_info (mesh , (8 ,), ("expert" ,)),
267+ _arg_info (mesh , (0 ,), (None ,)),
268+ _arg_info (mesh , (8 ,), ("expert" ,)),
269+ _arg_info (mesh , (0 ,), (None ,)),
270+ _arg_info (mesh , (8 ,), ("expert" ,)),
271+ _arg_info (mesh , (0 ,), (None ,)),
272+ _arg_info (mesh , (1 ,), (None ,)),
273+ _arg_info (mesh , (0 ,), (None ,)),
274+ )
275+
276+ with jax .set_mesh (mesh ), global_shard_guard (
277+ MeshResource (dp_resource = "dp" , fsdp_resource = "fsdp" , ep_resource = "expert" )
278+ ):
279+ _ , _ , reduce_axis = GroupedGemmPrimitive ._parse_partition_specs (
280+ mesh ,
281+ arg_infos ,
282+ (),
283+ out_shape = (1 , 128 , 64 ),
284+ lhs_is_trans = False ,
285+ lhs_axis_boundary = 1 ,
286+ )
287+
288+ assert reduce_axis == "dp"
289+
290+
291+ def test_grouped_partitioning_strips_arbitrary_unsupported_axis ():
292+ mesh = _mesh_with_arbitrary_axis ()
293+ mesh_resource = MeshResource (dp_resource = "dp" , fsdp_resource = "fsdp" , ep_resource = "expert" )
294+
295+ with jax .set_mesh (mesh ), global_shard_guard (mesh_resource ):
296+ _ , _ , quantize_out_shardings , quantize_arg_shardings = GroupedQuantizePrimitive .partition (
297+ jnp .float8_e4m3fn ,
298+ ScalingMode .MXFP8_1D_SCALING .value ,
299+ QuantizeLayout .ROWWISE ,
300+ - 1 ,
301+ jnp .float8_e8m0fnu ,
302+ mesh ,
303+ (
304+ _arg_info (mesh , (8 , 128 , 128 ), ("expert" , "myaxis123" , ("dp" , "fsdp" ))),
305+ _arg_info (mesh , (8 ,), (("expert" , "myaxis123" ),)),
306+ _arg_info (mesh , (8 ,), (("expert" , "myaxis123" ),)),
307+ ),
308+ (),
309+ )
310+
311+ gemm_arg_infos = (
312+ _arg_info (mesh , (8192 ,), (("dp" , "myaxis123" ),)),
313+ _arg_info (mesh , (0 ,), (("myaxis123" ,),)),
314+ _arg_info (mesh , (65536 ,), (("expert" , "fsdp" , "myaxis123" ),)),
315+ _arg_info (mesh , (2048 ,), (("expert" , "fsdp" , "myaxis123" ),)),
316+ _arg_info (mesh , (0 ,), (("fsdp" , "myaxis123" ),)),
317+ _arg_info (mesh , (8 ,), (("expert" , "myaxis123" ),)),
318+ _arg_info (mesh , (0 ,), (("myaxis123" ,),)),
319+ _arg_info (mesh , (0 ,), (("myaxis123" ,),)),
320+ _arg_info (mesh , (0 ,), (("myaxis123" ,),)),
321+ _arg_info (mesh , (8 ,), (("expert" , "myaxis123" ),)),
322+ _arg_info (mesh , (0 ,), (("myaxis123" ,),)),
323+ _arg_info (mesh , (1 ,), (("myaxis123" ,),)),
324+ _arg_info (mesh , (0 ,), (("myaxis123" ,),)),
325+ )
326+ gemm_result_infos = (_arg_info (mesh , (1 , 128 , 64 ), ("expert" , "myaxis123" , None )),)
327+ _ , _ , gemm_out_sharding , gemm_arg_shardings = GroupedGemmPrimitive .partition (
328+ False ,
329+ False ,
330+ ScalingMode .NO_SCALING .value ,
331+ jnp .bfloat16 ,
332+ False ,
333+ False ,
334+ False ,
335+ 1 ,
336+ 1 ,
337+ (1 , 128 , 64 ),
338+ 128 ,
339+ 64 ,
340+ 128 ,
341+ 64 ,
342+ mesh ,
343+ gemm_arg_infos ,
344+ gemm_result_infos ,
345+ )
346+
347+ assert tuple (quantize_arg_shardings [0 ].spec ) == ("expert" , None , None )
348+ assert tuple (quantize_arg_shardings [1 ].spec ) == ("expert" ,)
349+ quantize_out_specs = tuple (tuple (sharding .spec ) for sharding in quantize_out_shardings )
350+ assert _normalize_spec (quantize_out_specs [0 ]) == ("expert" ,)
351+ assert _normalize_spec (quantize_out_specs [2 ]) == ("expert" ,)
352+
353+ assert tuple (gemm_arg_shardings [0 ].spec ) == ("dp" ,)
354+ assert tuple (gemm_arg_shardings [2 ].spec ) == ("expert" ,)
355+ assert tuple (gemm_arg_shardings [3 ].spec ) == ("expert" ,)
356+ assert tuple (gemm_out_sharding [0 ].spec ) == ("expert" , None , None )
357+
358+ all_specs = (
359+ * quantize_out_specs ,
360+ * (tuple (sharding .spec ) for sharding in quantize_arg_shardings ),
361+ * (tuple (sharding .spec ) for sharding in gemm_arg_shardings ),
362+ tuple (gemm_out_sharding [0 ].spec ),
363+ )
364+ for spec in all_specs :
365+ assert not _spec_contains_axis (spec , "myaxis123" )
366+
367+
146368def test_grouped_partitioning_shardy_rules_smoke ():
147369 mesh = _mesh ()
148370 quantize_rule = GroupedQuantizePrimitive .shardy_sharding_rule (
0 commit comments