2424from flax import linen as nn
2525from flax import nnx
2626from flax .core import meta
27+ from flax .linen import partitioning as nn_partitioning
2728import jax
2829import jax .numpy as jnp
2930from jax .sharding import Mesh
@@ -154,23 +155,24 @@ def stage_factory(stage_rngs):
154155 return raw_stage_class (config = config , mesh = mesh , model_mode = model_mode , rngs = stage_rngs )
155156
156157 my_pipeline = pipeline .create_pipeline (config = config , layers = stage_factory , mesh = mesh )
157- init_pipeline_params = my_pipeline .init (
158- jax .random .PRNGKey (0 ), inputs , inputs_position , inputs_segmentation , deterministic , model_mode
159- )
160- # `get_weight_sharding` is a compact method on `PipelineLinen` (callable as
161- # `my_pipeline.get_weight_sharding(...)` directly) but on the ToLinen-wrapped NNX
162- # pipeline it must be invoked inside a bound module context. Use `bind` so the
163- # same call shape works on both paths.
164- logical_partition_spec = my_pipeline .bind (init_pipeline_params ).get_weight_sharding (
165- inputs , inputs_position , inputs_segmentation , deterministic , model_mode
166- )
158+ with jax .set_mesh (mesh ), nn_partitioning .axis_rules (config .logical_axis_rules ):
159+ init_pipeline_params = my_pipeline .init (
160+ jax .random .PRNGKey (0 ), inputs , inputs_segmentation , inputs_position , deterministic , model_mode
161+ )
162+ # `get_weight_sharding` is a compact method on `PipelineLinen` (callable as
163+ # `my_pipeline.get_weight_sharding(...)` directly) but on the ToLinen-wrapped NNX
164+ # pipeline it must be invoked inside a bound module context. Use `bind` so the
165+ # same call shape works on both paths.
166+ logical_partition_spec = my_pipeline .bind (init_pipeline_params ).get_weight_sharding (
167+ inputs , inputs_segmentation , inputs_position , deterministic , model_mode
168+ )
167169
168170 # Create a dummy scalar loss function so we may take the gradient wrt weights
169171 def pipeline_parallelism_dummy_loss_extra (
170172 params ,
171173 inputs ,
172- inputs_position ,
173174 inputs_segmentation ,
175+ inputs_position ,
174176 deterministic ,
175177 model_mode ,
176178 dummy_targets ,
@@ -179,8 +181,8 @@ def pipeline_parallelism_dummy_loss_extra(
179181 outputs = my_pipeline .apply (
180182 params ,
181183 inputs ,
182- inputs_position ,
183184 inputs_segmentation ,
185+ inputs_position ,
184186 deterministic ,
185187 model_mode ,
186188 logical_partition_spec = logical_partition_spec ,
@@ -192,7 +194,7 @@ def pipeline_parallelism_dummy_loss_extra(
192194 pipeline_parallelism_dummy_loss_extra , logical_partition_spec = logical_partition_spec
193195 )
194196
195- def regular_sequential_layers (params , inputs , inputs_position , inputs_segmentation , deterministic , model_mode ):
197+ def regular_sequential_layers (params , inputs , inputs_segmentation , inputs_position , deterministic , model_mode ):
196198 def get_cur_layer_params (params , layer_idx ):
197199 def get_cur_layer_params_arr (leaf ):
198200 # Reshape layers into a linear list of layers, e.g. [repeat, stage] into [layers]
@@ -213,34 +215,35 @@ def get_cur_layer_params_arr(leaf):
213215 for layer in range (config .num_decoder_layers ):
214216 cur_layer_params = get_cur_layer_params (params , layer )
215217 cur_layer_params ["params" ] = cur_layer_params ["params" ]["layers" ]
216- if config .num_pipeline_repeats > 1 and config . num_layers_per_pipeline_stage > 1 :
218+ if config .num_pipeline_repeats > 1 :
217219 cur_layer_params ["params" ] = meta .remove_axis (
218220 cur_layer_params ["params" ], 0 , {nn .PARTITION_NAME : "circular_repeats" }
219221 )
220- cur_layer_params ["params" ] = meta .remove_axis (cur_layer_params ["params" ], 0 , {nn .PARTITION_NAME : "layers" })
222+ cur_layer_params ["params" ] = meta .remove_axis (cur_layer_params ["params" ], 0 , {nn .PARTITION_NAME : "layers" })
221223 reg_layer_activations , _ = single_pipeline_stage .apply (
222- cur_layer_params , reg_layer_activations , inputs_position , inputs_segmentation , deterministic , model_mode
224+ cur_layer_params , reg_layer_activations , inputs_segmentation , inputs_position , deterministic , model_mode
223225 )
224226 return reg_layer_activations
225227
226228 def regular_sequential_layers_dummy_loss (
227- params , inputs , inputs_position , inputs_segmentation , deterministic , model_mode , dummy_targets
229+ params , inputs , inputs_segmentation , inputs_position , deterministic , model_mode , dummy_targets
228230 ):
229- outputs = regular_sequential_layers (params , inputs , inputs_position , inputs_segmentation , deterministic , model_mode )
231+ outputs = regular_sequential_layers (params , inputs , inputs_segmentation , inputs_position , deterministic , model_mode )
230232 loss = jnp .linalg .norm (outputs - dummy_targets )
231233 return loss
232234
233- assert_same_output_and_grad (
234- regular_sequential_layers_dummy_loss ,
235- pipeline_parallelism_dummy_loss ,
236- init_pipeline_params ,
237- inputs ,
238- inputs_segmentation ,
239- inputs_position ,
240- deterministic ,
241- model_mode ,
242- dummy_targets ,
243- )
235+ with jax .set_mesh (mesh ), nn_partitioning .axis_rules (config .logical_axis_rules ):
236+ assert_same_output_and_grad (
237+ regular_sequential_layers_dummy_loss ,
238+ pipeline_parallelism_dummy_loss ,
239+ init_pipeline_params ,
240+ inputs ,
241+ inputs_segmentation ,
242+ inputs_position ,
243+ deterministic ,
244+ model_mode ,
245+ dummy_targets ,
246+ )
244247
245248 def assert_pipeline_same_output_and_grad (self , config , single_pipeline_stage_class = None ):
246249 """
@@ -342,6 +345,40 @@ def test_circular_deepseek_megablox_same_output_and_grad(self):
342345 config , single_pipeline_stage_class = deepseek .DeepSeekMoELayerToLinen
343346 )
344347
348+ @pytest .mark .tpu_only
349+ def test_deepseek_ragged_a2a_ep_same_output_and_grad (self ):
350+ config = pyconfig .initialize (
351+ [sys .argv [0 ], get_test_config_path ()],
352+ enable_checkpointing = False ,
353+ enable_goodput_recording = False ,
354+ run_name = "deepseek_ragged_a2a_ep" ,
355+ dtype = "bfloat16" ,
356+ weight_dtype = "bfloat16" ,
357+ matmul_precision = "high" ,
358+ max_target_length = 128 ,
359+ base_emb_dim = 256 ,
360+ ici_pipeline_parallelism = 2 ,
361+ ici_expert_parallelism = 2 ,
362+ allow_split_physical_axes = True ,
363+ base_num_decoder_layers = 2 ,
364+ num_pipeline_microbatches = 4 ,
365+ per_device_batch_size = 4 ,
366+ num_experts = 4 ,
367+ num_experts_per_tok = 2 ,
368+ shared_experts = 1 ,
369+ megablox = True ,
370+ sparse_matmul = True ,
371+ capacity_factor = - 1 ,
372+ decoder_block = "deepseek" ,
373+ attention_type = "mla" ,
374+ base_moe_mlp_dim = 256 ,
375+ base_mlp_dim = 256 ,
376+ )
377+ self .assert_pipeline_same_output_and_grad (
378+ config ,
379+ single_pipeline_stage_class = deepseek .DeepSeekMoELayerToLinen ,
380+ )
381+
345382 @pytest .mark .tpu_only
346383 def test_circular_ag_once (self ):
347384 # 2 stages, 8 microbatches, all gather once
0 commit comments