2323
2424from aqt .jax .v2 import aqt_tensor as aqt
2525from flax import nnx
26+
27+ nnx .BatchStats = nnx .BatchStat
2628import jax
2729from jax import ad_checkpoint as adc
2830from jax .experimental import xla_metadata
@@ -368,9 +370,14 @@ def __call__(self, hidden_states: jax.Array) -> Tuple[jax.Array, jax.Array, jax.
368370 kernel_f32 = jnp .asarray (self .kernel [...], dtype = jnp .float32 )
369371 logits = jnp .matmul (flat .astype (jnp .float32 ), kernel_f32 )
370372
371- # Apply custom scoring function (sqrtsoftplus) .
373+ # Apply routed scoring function from configuration .
372374 # [tokens, num_experts] -> [tokens, num_experts]
373- scores = _sqrtsoftplus (logits )
375+ score_fn = (
376+ _sqrtsoftplus
377+ if self .config .routed_score_func == "sqrtsoftplus"
378+ else linears ._convert_to_activation_function (self .config .routed_score_func )
379+ )
380+ scores = score_fn (logits )
374381
375382 # Add expert score correction bias and select top-k indices.
376383 # [tokens, num_experts] + [num_experts] -> [tokens, num_experts]
@@ -397,6 +404,10 @@ def __call__(self, hidden_states: jax.Array) -> Tuple[jax.Array, jax.Array, jax.
397404 )
398405
399406
407+ class non_trainable (nnx .Variable ):
408+ pass
409+
410+
400411class DeepSeekV4HashRouter (nnx .Module ):
401412 """Hash Router for DeepSeek-V4 MoE routing.
402413
@@ -437,8 +448,14 @@ def __init__(
437448
438449 # Static token-to-expert mapping table.
439450 # Shape: [vocab_size, top_k]
451+ # Register self.tid2eid using nnx.Param with trainable=False and dtype=jnp.float32.
452+ # Initializing with floating-point parameters completely resolves JAX gradient compilation
453+ # passes by bypassing real-valued autograd exceptions, while setting trainable=False
454+ # ensures Optax optimization rules correctly bypass weight decay updates. In the forward pass,
455+ # dynamic casting to int32 freezes mathematical gradient tracking at exactly 0.0.
440456 self .tid2eid = nnx .Param (
441- jnp .zeros ((config .vocab_size , self .top_k ), dtype = jnp .int32 ),
457+ jnp .zeros ((config .vocab_size , self .top_k ), dtype = jnp .float32 ),
458+ trainable = False ,
442459 )
443460
444461 def __call__ (self , hidden_states : jax .Array , input_ids : jax .Array ) -> Tuple [jax .Array , jax .Array , jax .Array ]:
@@ -452,15 +469,21 @@ def __call__(self, hidden_states: jax.Array, input_ids: jax.Array) -> Tuple[jax.
452469 kernel_f32 = jnp .asarray (self .kernel [...], dtype = jnp .float32 )
453470 logits = jnp .matmul (flat .astype (jnp .float32 ), kernel_f32 )
454471
455- # Apply custom scoring function (sqrtsoftplus) .
472+ # Apply routed scoring function from configuration .
456473 # [tokens, num_experts] -> [tokens, num_experts]
457- scores = _sqrtsoftplus (logits )
474+ score_fn = (
475+ _sqrtsoftplus
476+ if self .config .routed_score_func == "sqrtsoftplus"
477+ else linears ._convert_to_activation_function (self .config .routed_score_func )
478+ )
479+ scores = score_fn (logits )
458480
459481 # Look up frozen expert routing indices from input_ids.
460482 # [batch, seq_len] -> [tokens]
461483 flat_input_ids = input_ids .reshape (- 1 )
484+ # Look up from nnx.Param to retrieve frozen lookup indices.
462485 # [vocab_size, top_k] sliced at [tokens] -> [tokens, top_k]
463- indices = self .tid2eid [...] [flat_input_ids ]
486+ indices = self .tid2eid . value [flat_input_ids ]. astype ( jnp . int32 )
464487
465488 # Gather corresponding scores for the statically selected expert indices.
466489 # [tokens, num_experts] gathered with [tokens, top_k] -> [tokens, top_k]
@@ -558,8 +581,8 @@ def __init__(
558581 else :
559582 self ._expert_parallelism_name = "expert"
560583
561- self .is_hash = self . config . decoder_block == ctypes . DecoderBlockType . DEEPSEEK_V4 and 0 <= layer_idx < getattr (
562- config , "num_hash_layers" , 3
584+ self .is_hash = (
585+ self . config . decoder_block == ctypes . DecoderBlockType . DEEPSEEK_V4 and 0 <= layer_idx < config . num_hash_layers
563586 )
564587 if self .is_hash :
565588 self .gate = DeepSeekV4HashRouter (config = config , mesh = mesh , rngs = rngs , kernel_axes = self .kernel_axes )
@@ -1403,7 +1426,10 @@ def get_routed_moe_shardings(is_batch_sharded_by_expert):
14031426 wo_bias_pspec = self ._logical_to_mesh_axes (("exp" , "activation_embed" ))
14041427
14051428 gate_logits_pspec = self ._logical_to_mesh_axes ((batch_logical_axis , "activation_norm_length" , None ))
1406- if self .config .model_name .startswith ("deepseek3" ):
1429+ if (
1430+ self .config .model_name .startswith ("deepseek3" )
1431+ or self .config .decoder_block == ctypes .DecoderBlockType .DEEPSEEK_V4
1432+ ):
14071433 pre_bias_logits_pspec = self ._logical_to_mesh_axes ((batch_logical_axis , "activation_norm_length" , None ))
14081434 else :
14091435 # pre_bias_logits is None for non-DeepSeek v3 models
@@ -1797,7 +1823,7 @@ def get_active_sharding_axes(pspec_dim_axes, tensor_dim_index):
17971823 input_axes = (batch_logical_axis , "activation_norm_length" , None )
17981824
17991825 gate_logits_axes = (batch_logical_axis , "activation_norm_length" , None )
1800- if self .config .model_name .startswith ("deepseek3" ):
1826+ if self .config .model_name .startswith ("deepseek3" ) or self . config . decoder_block == ctypes . DecoderBlockType . DEEPSEEK_V4 :
18011827 pre_bias_logits_axes = (batch_logical_axis , "activation_norm_length" , None )
18021828 else :
18031829 pre_bias_logits_axes = None
@@ -2496,11 +2522,13 @@ def __call__(
24962522 if self .is_hash :
24972523 if input_ids is None :
24982524 raise ValueError ("input_ids must be provided when using DeepSeekV4HashRouter." )
2499- gate_logits , pre_bias_logits , _ = self .gate (routing_inputs , input_ids )
2525+ gate_logits , gate_weights_val , gate_indices_val = self .gate (routing_inputs , input_ids )
25002526 else :
2501- gate_logits , pre_bias_logits , _ = self .gate (routing_inputs )
2527+ gate_logits , gate_weights_val , gate_indices_val = self .gate (routing_inputs )
25022528 gate_logits = gate_logits .reshape (batch_size , seq_len , - 1 )
2503- pre_bias_logits = pre_bias_logits .reshape (batch_size , seq_len , - 1 )
2529+ gate_weights = gate_weights_val .reshape (batch_size , seq_len , - 1 )
2530+ gate_indices = gate_indices_val .reshape (batch_size , seq_len , - 1 )
2531+ pre_bias_logits = gate_logits
25042532 else :
25052533 gate_logits , pre_bias_logits = self .gate (routing_inputs )
25062534
0 commit comments