4141from maxtext .layers .quantizations import AqtQuantization as Quant
4242from maxtext .models import (
4343 deepseek ,
44+ deepseek4 ,
4445 deepseek_batchsplit ,
4546 deepseek_batchsplit_fp8 ,
4647 gemma ,
@@ -467,6 +468,10 @@ def get_decoder_layers(self):
467468 deepseek .DeepSeekDenseLayerToLinen ,
468469 deepseek .DeepSeekMoELayerToLinen ,
469470 ]
471+ case DecoderBlockType .DEEPSEEK4 :
472+ return (
473+ [deepseek4 .DeepSeek4ScannableBlockToLinen ] if self .config .scan_layers else [deepseek4 .DeepSeek4LayerToLinen ]
474+ )
470475 case DecoderBlockType .GEMMA :
471476 return [gemma .GemmaDecoderLayerToLinen ]
472477 case DecoderBlockType .GEMMA2 :
@@ -632,6 +637,7 @@ def get_norm_layer(self, num_features: int):
632637 DecoderBlockType .MISTRAL ,
633638 DecoderBlockType .MIXTRAL ,
634639 DecoderBlockType .DEEPSEEK ,
640+ DecoderBlockType .DEEPSEEK4 ,
635641 DecoderBlockType .GEMMA ,
636642 DecoderBlockType .GEMMA2 ,
637643 DecoderBlockType .GEMMA3 ,
@@ -1061,6 +1067,17 @@ def __call__(
10611067 previous_chunk ,
10621068 slot ,
10631069 )
1070+ elif cfg .decoder_block == DecoderBlockType .DEEPSEEK4 :
1071+ y = self ._apply_deepseek4_scanned_blocks (
1072+ y ,
1073+ decoder_segment_ids ,
1074+ decoder_positions ,
1075+ deterministic ,
1076+ model_mode ,
1077+ previous_chunk ,
1078+ slot ,
1079+ decoder_input_tokens ,
1080+ )
10641081 else :
10651082 RemattedBlockLayer = RemattedBlockLayers [0 ]
10661083 scan_length = int (cfg .num_decoder_layers / cfg .inhomogeneous_layer_cycle_interval )
@@ -1195,7 +1212,7 @@ def __call__(
11951212 "is_nope_layer" : llama4 .determine_is_nope_layer (lyr , self .config .nope_layer_interval ),
11961213 "is_moe_layer" : llama4 .determine_is_moe_layer (lyr , self .config .interleave_moe_layer_step ),
11971214 }
1198- if cfg .decoder_block in (DecoderBlockType .QWEN3_NEXT , DecoderBlockType .QWEN3_5 ):
1215+ if cfg .decoder_block in (DecoderBlockType .QWEN3_NEXT , DecoderBlockType .QWEN3_5 , DecoderBlockType . DEEPSEEK4 ):
11991216 layer_kwargs = {"layer_idx" : lyr }
12001217 kv_cache = None
12011218 if kv_caches is not None :
@@ -1221,6 +1238,7 @@ def __call__(
12211238 previous_chunk = previous_chunk ,
12221239 slot = slot ,
12231240 kv_cache = kv_cache ,
1241+ decoder_input_tokens = decoder_input_tokens ,
12241242 attention_metadata = attention_metadata ,
12251243 ** layer_call_kwargs ,
12261244 )
@@ -1423,6 +1441,102 @@ def _apply_gemma4_scanned_blocks(
14231441
14241442 return y
14251443
1444+ def _apply_deepseek4_scanned_blocks (
1445+ self ,
1446+ y ,
1447+ decoder_segment_ids ,
1448+ decoder_positions ,
1449+ deterministic ,
1450+ model_mode ,
1451+ previous_chunk ,
1452+ slot ,
1453+ decoder_input_tokens ,
1454+ ):
1455+ """Applies DeepSeek V4 scanned decoder blocks.
1456+
1457+ DeepSeek V4 has some number of prefix layers (defined by `first_num_hash_layers`)
1458+ that use static Hash Routing. The remaining layers alternate `compress_ratio=128` (HCA)
1459+ and `compress_ratio=4` (CSA) and are evaluated in a single `nn.scan` block.
1460+
1461+ For DeepSeek4-Flash (43 hidden layers total):
1462+ - 3 Prefix layers (Indices 0, 1, 2)
1463+ - 40 Scanned layers: 20 perfectly repeating chunks of [128, 4]
1464+ """
1465+
1466+ cfg = self .config
1467+ mesh = self .mesh
1468+
1469+ broadcast_args = (
1470+ decoder_segment_ids ,
1471+ decoder_positions ,
1472+ deterministic ,
1473+ model_mode ,
1474+ slot ,
1475+ previous_chunk ,
1476+ )
1477+
1478+ layer_call_kwargs = {
1479+ "previous_chunk" : previous_chunk ,
1480+ "slot" : slot ,
1481+ "decoder_input_tokens" : decoder_input_tokens ,
1482+ }
1483+
1484+ # 1. Prefix Unrolling
1485+ # Prefix layers are unrolled (unscanned) for two architectural reasons:
1486+ # 1. Heterogeneous Attention: JAX nn.scan requires identical computation graphs, but the first few layers
1487+ # use different attention configurations (e.g., DeepSeek-V4 uses compress_ratios [0, 0, 4] for layers 0, 1, 2).
1488+ # 2. Static Hash Routing: The first `first_num_hash_layers` (which is 3 for DeepSeek-V4) use deterministic
1489+ # token-to-expert Hash Routing instead of learned top-k routing.
1490+ # Therefore, these prefix layers are instantiated individually before we scan the remaining uniform blocks.
1491+ num_hash_layers = cfg .first_num_hash_layers
1492+ for layer_idx in range (num_hash_layers ):
1493+ prefix_layer = deepseek4 .DeepSeek4LayerToLinen (
1494+ config = cfg ,
1495+ mesh = mesh ,
1496+ name = f"layers_{ layer_idx } " ,
1497+ quant = self .quant ,
1498+ model_mode = self .model_mode ,
1499+ layer_idx = layer_idx ,
1500+ )
1501+ y , _ = prefix_layer (
1502+ y ,
1503+ decoder_segment_ids ,
1504+ decoder_positions ,
1505+ deterministic ,
1506+ model_mode ,
1507+ ** layer_call_kwargs ,
1508+ )
1509+
1510+ # 2. Chunked Scanning
1511+ # The remaining layers perfectly alternate HCA (128) and CSA (4).
1512+ num_remaining_layers = cfg .num_decoder_layers - num_hash_layers
1513+ num_full_blocks = num_remaining_layers // 2
1514+
1515+ if num_full_blocks > 0 :
1516+ ScannableBlockToLinen = deepseek4 .DeepSeek4ScannableBlockToLinen
1517+ policy = self .get_remat_policy ()
1518+ RemattedDeepSeek4Block = self .set_remat_policy ([ScannableBlockToLinen ], policy )[0 ]
1519+
1520+ y , _ = nn .scan (
1521+ RemattedDeepSeek4Block ,
1522+ variable_axes = {
1523+ "params" : cfg .param_scan_axis ,
1524+ "cache" : 0 ,
1525+ "intermediates" : 0 ,
1526+ "aqt" : 0 ,
1527+ "_overwrite_with_gradient" : 0 ,
1528+ },
1529+ split_rngs = {"params" : True , "dropout" : cfg .enable_dropout },
1530+ in_axes = (nn .broadcast ,) * len (broadcast_args ),
1531+ length = num_full_blocks ,
1532+ metadata_params = {
1533+ nn .PARTITION_NAME : "layers" ,
1534+ "abstract_init" : False ,
1535+ },
1536+ )(config = cfg , mesh = mesh , quant = self .quant , model_mode = model_mode , name = "scanned_blocks" ,)(y , * broadcast_args )
1537+
1538+ return y
1539+
14261540 def _apply_gemma4_small_layers (
14271541 self ,
14281542 y ,
0 commit comments