@@ -384,6 +384,7 @@ def _build_layer(self, *, has_bias=True, zero_expert_num=0):
384384 @pytest .mark .parametrize ("enable_fused_mc2" , [True , False ])
385385 def test_process_weights_after_loading_transposes_and_formats (self , monkeypatch , enable_fused_mc2 ):
386386 method = AscendUnquantizedFusedMoEMethod .__new__ (AscendUnquantizedFusedMoEMethod )
387+ method .dynamic_eplb = False
387388 method ._maybe_pad_weight = MagicMock (side_effect = lambda weight : weight )
388389 layer = self ._build_layer ()
389390 original_w13 = layer .w13_weight .detach ().clone ()
@@ -408,6 +409,36 @@ def test_process_weights_after_loading_transposes_and_formats(self, monkeypatch,
408409 assert maybe_trans_nz .call_count == 2
409410 format_cast .assert_not_called ()
410411
412+ def test_process_weights_after_loading_splits_dynamic_eplb_fused_mc2_weights (self , monkeypatch ):
413+ method = AscendUnquantizedFusedMoEMethod .__new__ (AscendUnquantizedFusedMoEMethod )
414+ method .dynamic_eplb = True
415+ method ._maybe_pad_weight = MagicMock (side_effect = lambda weight : weight )
416+ layer = nn .Module ()
417+ layer .w13_weight = nn .Parameter (torch .randn (2 , 3 , 4 ))
418+ layer .w2_weight = nn .Parameter (torch .randn (2 , 4 , 3 ))
419+ expected_w13 = layer .w13_weight .detach ().clone ().transpose (1 , 2 ).contiguous ()
420+ expected_w2 = layer .w2_weight .detach ().clone ().transpose (1 , 2 ).contiguous ()
421+ format_cast = MagicMock (side_effect = lambda weight , _ : weight )
422+ empty_cache = MagicMock ()
423+
424+ mock_ascend_config = MagicMock ()
425+ mock_ascend_config .enable_fused_mc2 = True
426+ monkeypatch .setattr (fused_moe_module , "get_ascend_config" , lambda : mock_ascend_config )
427+ monkeypatch .setattr (fused_moe_module .torch_npu , "npu_format_cast" , format_cast )
428+ monkeypatch .setattr (fused_moe_module .torch , "npu" , SimpleNamespace (empty_cache = empty_cache ), raising = False )
429+
430+ method .process_weights_after_loading (layer )
431+
432+ assert "w13_weight" not in layer ._parameters
433+ assert "w2_weight" not in layer ._parameters
434+ assert len (layer .w13_weight_list ) == 2
435+ assert len (layer .w2_weight_list ) == 2
436+ torch .testing .assert_close (layer .w13_weight_list [0 ], expected_w13 [0 ])
437+ torch .testing .assert_close (layer .w2_weight_list [1 ], expected_w2 [1 ])
438+ assert layer .w13_weight_list [0 ].untyped_storage ().data_ptr () != expected_w13 [0 ].untyped_storage ().data_ptr ()
439+ assert format_cast .call_count == 2
440+ empty_cache .assert_called_once ()
441+
411442 @pytest .mark .parametrize ("moe_comm_type" , [MoECommType .MC2 , MoECommType .FUSED_MC2 ])
412443 def test_apply_builds_fused_experts_input (self , monkeypatch , moe_comm_type ):
413444 method = AscendUnquantizedFusedMoEMethod .__new__ (AscendUnquantizedFusedMoEMethod )
@@ -459,12 +490,105 @@ def test_apply_builds_fused_experts_input(self, monkeypatch, moe_comm_type):
459490 assert fused_input .weights .w2 [0 ] is layer .w2_weight
460491 assert isinstance (fused_input .weights .w1_scale , list )
461492 assert isinstance (fused_input .weights .w2_scale , list )
493+ assert fused_input .weights .w1_scale [0 ].dtype == torch .int64
494+ assert fused_input .weights .w2_scale [0 ].dtype == torch .int64
495+ assert fused_input .weights .w1_scale_bias [0 ].dtype == torch .float32
496+ assert fused_input .weights .w2_scale_bias [0 ].dtype == torch .float32
462497 else :
463498 assert fused_input .weights .w1 is layer .w13_weight
464499 assert fused_input .weights .w2 is layer .w2_weight
465500 assert fused_input .weights .w1_scale is None
466501 assert fused_input .weights .w2_scale is None
467502
503+ @pytest .mark .parametrize ("moe_comm_type" , [MoECommType .MC2 , MoECommType .FUSED_MC2 ])
504+ def test_apply_uses_weight_lists_when_dynamic_eplb_splits_weights (self , monkeypatch , moe_comm_type ):
505+ method = AscendUnquantizedFusedMoEMethod .__new__ (AscendUnquantizedFusedMoEMethod )
506+ method .moe = SimpleNamespace (has_bias = False )
507+ method .dynamic_eplb = True
508+ method .tid2eid = None
509+ layer = self ._build_layer (has_bias = False )
510+ layer .w13_weight_list = [torch .randn (4 , 6 ), torch .randn (4 , 6 )]
511+ layer .w2_weight_list = [torch .randn (3 , 4 ), torch .randn (3 , 4 )]
512+ hidden_states = torch .randn (2 , 4 , dtype = torch .float16 )
513+ topk_weights = torch .ones (2 , 2 , dtype = torch .float32 )
514+ topk_ids = torch .tensor ([[0 , 1 ], [1 , 0 ]], dtype = torch .int64 )
515+ moe_comm_method = MagicMock ()
516+ moe_comm_method .fused_experts .return_value = torch .ones_like (hidden_states )
517+ monkeypatch .setattr (
518+ fused_moe_module ,
519+ "_EXTRA_CTX" ,
520+ SimpleNamespace (moe_comm_type = moe_comm_type , moe_comm_method = moe_comm_method ),
521+ )
522+ monkeypatch .setattr (fused_moe_module , "select_experts" , MagicMock (return_value = (topk_weights , topk_ids )))
523+ monkeypatch .setattr (fused_moe_module , "get_forward_context" , MagicMock (return_value = MagicMock (input_ids = None )))
524+
525+ method .apply (
526+ layer = layer ,
527+ x = hidden_states ,
528+ use_grouped_topk = False ,
529+ top_k = 2 ,
530+ router_logits = torch .randn (2 , 4 ),
531+ renormalize = True ,
532+ num_experts = 4 ,
533+ )
534+
535+ fused_input = moe_comm_method .fused_experts .call_args .kwargs ["fused_experts_input" ]
536+ assert fused_input .weights .w1 is layer .w13_weight_list
537+ assert fused_input .weights .w2 is layer .w2_weight_list
538+ if moe_comm_type == MoECommType .FUSED_MC2 :
539+ assert len (fused_input .weights .w1_scale ) == 1
540+ assert len (fused_input .weights .w2_scale ) == 1
541+ assert fused_input .weights .w1_scale [0 ].dtype == torch .int64
542+ assert fused_input .weights .w2_scale [0 ].dtype == torch .int64
543+ assert fused_input .weights .w1_scale [0 ].numel () == 0
544+ assert fused_input .weights .w2_scale [0 ].numel () == 0
545+ assert fused_input .weights .w1_scale_bias [0 ].dtype == torch .float32
546+ assert fused_input .weights .w2_scale_bias [0 ].dtype == torch .float32
547+ assert fused_input .weights .w1_scale_bias [0 ].numel () == 0
548+ assert fused_input .weights .w2_scale_bias [0 ].numel () == 0
549+ else :
550+ assert fused_input .weights .w1_scale is None
551+ assert fused_input .weights .w2_scale is None
552+
553+ def test_apply_warns_when_dynamic_eplb_fused_mc2_weights_are_not_split (self , monkeypatch ):
554+ method = AscendUnquantizedFusedMoEMethod .__new__ (AscendUnquantizedFusedMoEMethod )
555+ method .moe = SimpleNamespace (has_bias = False )
556+ method .dynamic_eplb = True
557+ method .tid2eid = None
558+ layer = self ._build_layer (has_bias = False )
559+ hidden_states = torch .randn (2 , 4 , dtype = torch .float16 )
560+ topk_weights = torch .ones (2 , 2 , dtype = torch .float32 )
561+ topk_ids = torch .tensor ([[0 , 1 ], [1 , 0 ]], dtype = torch .int64 )
562+ moe_comm_method = MagicMock ()
563+ moe_comm_method .fused_experts .return_value = torch .ones_like (hidden_states )
564+ warning_once = MagicMock ()
565+ monkeypatch .setattr (
566+ fused_moe_module ,
567+ "_EXTRA_CTX" ,
568+ SimpleNamespace (moe_comm_type = MoECommType .FUSED_MC2 , moe_comm_method = moe_comm_method ),
569+ )
570+ monkeypatch .setattr (fused_moe_module , "select_experts" , MagicMock (return_value = (topk_weights , topk_ids )))
571+ monkeypatch .setattr (fused_moe_module , "get_forward_context" , MagicMock (return_value = MagicMock (input_ids = None )))
572+ monkeypatch .setattr (fused_moe_module .logger , "warning_once" , warning_once )
573+
574+ method .apply (
575+ layer = layer ,
576+ x = hidden_states ,
577+ use_grouped_topk = False ,
578+ top_k = 2 ,
579+ router_logits = torch .randn (2 , 4 ),
580+ renormalize = True ,
581+ num_experts = 4 ,
582+ )
583+
584+ warning_once .assert_called_once ()
585+ warning_msg = warning_once .call_args .args [0 ]
586+ assert "dynamic EPLB" in warning_msg
587+ assert "not split into tensor lists" in warning_msg
588+ fused_input = moe_comm_method .fused_experts .call_args .kwargs ["fused_experts_input" ]
589+ assert fused_input .weights .w1 [0 ] is layer .w13_weight
590+ assert fused_input .weights .w2 [0 ] is layer .w2_weight
591+
468592 def test_apply_adds_zero_expert_result_and_force_balances (self , monkeypatch ):
469593 method = AscendUnquantizedFusedMoEMethod .__new__ (AscendUnquantizedFusedMoEMethod )
470594 method .moe = SimpleNamespace (has_bias = False )
0 commit comments