@@ -95,6 +95,16 @@ def _get_te_moe_cudnn_gmm_fusion_kwargs(disable_cudnn_gmm_fusion: bool) -> dict[
9595 return {"use_cudnn_fusion" : False } if disable_cudnn_gmm_fusion else {}
9696
9797
98+ def _get_te_moe_weight_layout_kwargs (moe_fn ) -> dict [str , bool ]:
99+ """Declare MaxText's standard [E, K, 2N] FC1 storage when TE accepts the layout flag.
100+
101+ TE infers the layout from the FC1 shape, which is ambiguous when K == 2N (e.g. DeepSeek V4).
102+ """
103+ if "cudnn_native_weight_layout" in inspect .signature (moe_fn ).parameters :
104+ return {"cudnn_native_weight_layout" : False }
105+ return {}
106+
107+
98108@struct .dataclass
99109class RouteMetadata :
100110 """EP communication state needed to undo the forward all-to-all after expert computation."""
@@ -401,8 +411,7 @@ def load_balance_updates_from_counts(expert_counts, num_experts, rate):
401411 total_tokens = jnp .sum (expert_counts , axis = - 1 , keepdims = True )
402412 average_load = total_tokens / num_experts
403413 direction = jnp .sign (average_load - expert_counts )
404- output = direction * rate
405- return output
414+ return direction * rate
406415
407416
408417def finalize_deferred_bias_signal (partial_counts , config ):
@@ -1022,6 +1031,7 @@ def __init__(
10221031 self .quant_einsums = None
10231032
10241033 gate_weight_dtype = ctypes .get_weight_dtype (self .config , "gate" )
1034+ self .use_gate_bias = self .config .routed_bias and not self .is_hash_routing
10251035 self .gate = GateLogit (
10261036 in_features_shape = self .moe_expert_input_dim ,
10271037 out_features_shape = self .num_experts ,
@@ -1032,7 +1042,7 @@ def __init__(
10321042 quant = ctypes .get_quant (self .config , "gate" , self .quant ),
10331043 kernel_init = self .kernel_init ,
10341044 kernel_axes = self .kernel_axes ,
1035- use_bias = self .config . routed_bias and not self . is_hash_routing ,
1045+ use_bias = self .use_gate_bias ,
10361046 bias_dtype = jnp .float32 if self .config .float32_gate_logits else gate_weight_dtype ,
10371047 # tpu-inference applies the score function in the fused_moe_gmm kernel,
10381048 # so we don't apply it here to avoid redundant computation.
@@ -4867,6 +4877,8 @@ def _te_moe_block(
48674877 w0_bias : jax .Array | None ,
48684878 w1_bias : jax .Array | None ,
48694879 wo_bias : jax .Array | None ,
4880+ routing_indices : jax .Array | None = None ,
4881+ routing_weights : jax .Array | None = None ,
48704882 out_sharding : NamedSharding | None = None ,
48714883 ) -> tuple [jax .Array , Optional [jax .Array ], Optional [jax .Array ]]:
48724884 """Run TransformerEngine's fused EP MoEBlock using MaxText-owned params."""
@@ -4881,10 +4893,10 @@ def _te_moe_block(
48814893
48824894 if self .quant is None or not hasattr (self .quant , "get_moe_block_quantizer_sets" ):
48834895 raise ValueError ("te_moe_block=True requires TransformerEngine quantization or te_gmm_quantization=te_no_quant." )
4896+ if self .is_hash_routing and (routing_indices is None or routing_weights is None ):
4897+ raise ValueError ("TE MoE hash routing requires precomputed routing indices and weights." )
48844898
4885- expert_bias = None
4886- if self .config .routed_bias :
4887- expert_bias = jnp .asarray (self .gate .bias [...], jnp .float32 )
4899+ expert_bias = jnp .asarray (self .gate .bias [...], jnp .float32 ) if self .use_gate_bias else None
48884900
48894901 fsdp_size = self .mesh .shape .get ("fsdp" , 1 )
48904902 ep_size = self .mesh .shape .get (self ._expert_parallelism_name , 1 )
@@ -4907,45 +4919,107 @@ def _te_moe_block(
49074919
49084920 checkpoint_kwargs = _get_te_moe_checkpoint_kwargs (te_moe .moe )
49094921 cudnn_gmm_fusion_kwargs = _get_te_moe_cudnn_gmm_fusion_kwargs (self .config .te_moe_disable_cudnn_gmm_fusion )
4922+ weight_layout_kwargs = _get_te_moe_weight_layout_kwargs (te_moe .moe )
4923+
4924+ # Older TE versions take the router arguments directly and do not return expert counts.
4925+ supports_router_info = hasattr (te_moe , "RouterComputationInfo" )
4926+ if not supports_router_info :
4927+ if self .is_hash_routing :
4928+ raise ImportError (
4929+ "te_moe_block=True with hash routing requires TransformerEngine with "
4930+ "moe.RoutingMapInfo support. Please upgrade TransformerEngine."
4931+ )
4932+ if self .should_update_load_balance ():
4933+ raise ImportError (
4934+ "te_moe_block=True with routed_bias_update_rate > 0 requires TransformerEngine with "
4935+ "MoE expert-count support. Please upgrade TransformerEngine."
4936+ )
4937+ router_kwargs = {
4938+ "gate_kernel" : gate_kernel ,
4939+ "num_experts_per_tok" : self .num_experts_per_tok ,
4940+ "expert_bias" : expert_bias ,
4941+ "score_function" : self .config .routed_score_func or "softmax" ,
4942+ "use_pre_softmax" : False ,
4943+ "num_groups" : None if self .config .n_routing_groups <= 0 else self .config .n_routing_groups ,
4944+ "group_topk" : None if self .config .topk_routing_group <= 0 else self .config .topk_routing_group ,
4945+ "scaling_factor" : self .config .routed_scaling_factor ,
4946+ "aux_loss_coeff" : self .config .load_balance_loss_weight ,
4947+ "gate_kernel_axes" : self .kernel_axes ,
4948+ }
4949+ elif self .is_hash_routing :
4950+ router_kwargs = {
4951+ "router_info" : te_moe .RoutingMapInfo (
4952+ routing_indices = routing_indices ,
4953+ routing_weights = routing_weights ,
4954+ ),
4955+ "collect_expert_counts" : False ,
4956+ }
4957+ else :
4958+ router_kwargs = {
4959+ "router_info" : te_moe .RouterComputationInfo (
4960+ gate_kernel = gate_kernel ,
4961+ num_experts_per_tok = self .num_experts_per_tok ,
4962+ expert_bias = expert_bias ,
4963+ score_function = self .config .routed_score_func or "softmax" ,
4964+ use_pre_softmax = False ,
4965+ num_groups = None if self .config .n_routing_groups <= 0 else self .config .n_routing_groups ,
4966+ group_topk = None if self .config .topk_routing_group <= 0 else self .config .topk_routing_group ,
4967+ scaling_factor = self .config .routed_scaling_factor ,
4968+ aux_loss_coeff = self .config .load_balance_loss_weight ,
4969+ gate_kernel_axes = self .kernel_axes ,
4970+ ),
4971+ "collect_expert_counts" : self .should_update_load_balance (),
4972+ }
49104973
4911- output , lb_loss , total_recv_tokens = te_moe .moe (
4974+ # Pass the weights by keyword: the older interface has gate_kernel in the second positional slot.
4975+ te_moe_outputs = te_moe .moe (
49124976 inputs ,
4913- gate_kernel ,
4914- wi_kernel ,
4915- wo_kernel ,
4916- w0_bias ,
4917- w1_bias ,
4918- wo_bias ,
4919- expert_bias ,
4977+ wi = wi_kernel ,
4978+ wo = wo_kernel ,
4979+ wi_0_bias = w0_bias ,
4980+ wi_1_bias = w1_bias ,
4981+ wo_bias = wo_bias ,
49204982 num_experts = self .num_experts ,
4921- num_experts_per_tok = self .num_experts_per_tok ,
49224983 activation_type = self .config .mlp_activations [0 ],
4923- score_function = self .config .routed_score_func or "softmax" ,
4924- use_pre_softmax = False ,
4925- num_groups = None if self .config .n_routing_groups <= 0 else self .config .n_routing_groups ,
4926- group_topk = None if self .config .topk_routing_group <= 0 else self .config .topk_routing_group ,
4927- scaling_factor = self .config .routed_scaling_factor ,
4928- aux_loss_coeff = self .config .load_balance_loss_weight ,
49294984 apply_topk_weights_early = True ,
49304985 quantizer_sets = (fc1_quantizer_set , fc2_quantizer_set ),
49314986 mesh_resource = MeshResource (dp_resource = "data" , fsdp_resource = "fsdp" , ep_resource = self ._expert_parallelism_name ),
49324987 input_axes = ("activation_batch" , "activation_norm_length" , None ),
4933- gate_kernel_axes = self .kernel_axes ,
49344988 wi_kernel_axes = self .wi_kernel_axes ,
49354989 wo_kernel_axes = self .wo_kernel_axes ,
49364990 dtype = self .dtype ,
49374991 recv_capacity_per_rank = max_utils .get_te_moe_recv_capacity_per_rank (),
49384992 quant_before_fsdp_ag = self .config .te_moe_quantize_before_fsdp_all_gather ,
4993+ ** router_kwargs ,
49394994 ** cudnn_gmm_fusion_kwargs ,
4995+ ** weight_layout_kwargs ,
49404996 ** checkpoint_kwargs ,
49414997 )
4998+ if supports_router_info :
4999+ output , lb_loss , total_recv_tokens , expert_counts = te_moe_outputs
5000+ else :
5001+ output , lb_loss , total_recv_tokens = te_moe_outputs
5002+ expert_counts = None
49425003 recv_capacity_per_rank = max_utils .get_te_moe_recv_capacity_per_rank ()
49435004 output = output .astype (self .dtype )
49445005 if lb_loss is not None :
49455006 lb_loss = lb_loss .astype (self .dtype )
49465007 if out_sharding is not None :
49475008 output = jax .lax .with_sharding_constraint (output , out_sharding )
4948- return output , lb_loss , (total_recv_tokens , jnp .asarray (recv_capacity_per_rank , dtype = jnp .int32 ))
5009+ bias_updates = None
5010+ if self .should_update_load_balance ():
5011+ bias_updates = load_balance_updates_from_counts (
5012+ expert_counts , self .num_experts , self .config .routed_bias_update_rate
5013+ )
5014+ return (
5015+ output ,
5016+ lb_loss ,
5017+ (
5018+ total_recv_tokens ,
5019+ jnp .asarray (recv_capacity_per_rank , dtype = jnp .int32 ),
5020+ bias_updates ,
5021+ ),
5022+ )
49495023
49505024 def __call__ (
49515025 self ,
@@ -4980,6 +5054,16 @@ def __call__(
49805054 routing_inputs = inputs if gate_inputs is None else gate_inputs .astype (gate_dtype )
49815055
49825056 if cfg .te_moe_block :
5057+ routing_indices = None
5058+ routing_weights = None
5059+ if self .is_hash_routing :
5060+ gate_logits , pre_bias_logits = self .gate (routing_inputs )
5061+ routing_weights , routing_indices = self .get_topk (
5062+ gate_logits ,
5063+ pre_bias_logits ,
5064+ self .rngs ,
5065+ input_ids = input_ids ,
5066+ )
49835067 gate_kernel = jnp .asarray (self .gate .kernel [...], self .dtype )
49845068 wi_kernel = jnp .asarray (self .wi [...], self .dtype )
49855069 wo_kernel = jnp .asarray (self .wo [...], self .dtype )
@@ -4999,7 +5083,9 @@ def __call__(
49995083 w0_bias ,
50005084 w1_bias ,
50015085 wo_bias ,
5002- out_sharding ,
5086+ routing_indices = routing_indices ,
5087+ routing_weights = routing_weights ,
5088+ out_sharding = out_sharding ,
50035089 )
50045090
50055091 gate_logits , pre_bias_logits = self .gate (routing_inputs )
0 commit comments