2424from jax .sharding import Mesh
2525from maxtext .common .common_types import Array , Config
2626from maxtext .common .common_types import HyperConnectionType
27- from maxtext .layers .initializers import default_bias_init , default_scalar_init , nd_dense_init
27+ from maxtext .layers .initializers import default_bias_init , default_scalar_init , nd_dense_init , variable_to_logically_partitioned
28+ from maxtext .layers import nnx_wrappers
2829from maxtext .layers .normalizations import RMSNorm
2930
3031
@@ -61,21 +62,18 @@ def sinkhorn(t, iters=20):
6162 # Use float32 precision for numerical stability during normalization
6263 initial_dtype = t .dtype
6364 t = t .astype (jnp .float32 )
65+ eps = 1e-5
6466
65- # Column-wise normalization (axis=-2) - positive and sum up to 1 across columns
66- # Equivalent to t = exp(t) / jnp.sum(jnp.exp(t), axis=-2)
67- t = jax .nn .softmax (t , axis = - 2 )
67+ t = jax .nn .softmax (t , axis = - 1 ) + eps
68+ t = t / (jnp .sum (t , axis = - 2 , keepdims = True ) + eps )
6869
6970 def body_fun (i , val ):
70- # L1 Normalization: val / sum(val) with clipping of denominator
71- # Normalize rows (axis -1)
72- val = val / jnp .clip (jnp .sum (val , axis = - 1 , keepdims = True ), min = 1e-12 )
73- # Normalize columns (axis -2)
74- val = val / jnp .clip (jnp .sum (val , axis = - 2 , keepdims = True ), min = 1e-12 )
71+ val = val / (jnp .sum (val , axis = - 1 , keepdims = True ) + eps )
72+ val = val / (jnp .sum (val , axis = - 2 , keepdims = True ) + eps )
7573 return val
7674
7775 # Use lax.fori_loop for an efficient, JIT-friendly loop
78- t = jax .lax .fori_loop (0 , iters , body_fun , t )
76+ t = jax .lax .fori_loop (0 , iters - 1 , body_fun , t )
7977 return t .astype (initial_dtype )
8078
8179
@@ -224,7 +222,7 @@ def res_mapping(self, x: Array):
224222 output = sinkhorn (intermediate , self .sinkhorn_iterations )
225223 return output
226224
227- def mapping (self , x : Array , alpha_scale : Array , alpha : Array , beta : Array , scale : int ):
225+ def mapping (self , x : Array , alpha_scale : Array , alpha : Array , beta : Array , scale : float , eps : float = 0.0 ):
228226 """Helper function for both pre and post mappings."""
229227 # In MaxText, we match weight precision to activations before Matmul
230228 alpha = jnp .asarray (alpha , self .dtype )
@@ -233,7 +231,7 @@ def mapping(self, x: Array, alpha_scale: Array, alpha: Array, beta: Array, scale
233231 # Apply projection: (b, s, k*d) @ (k*d, k) -> (b, s, k)
234232 h = jnp .einsum ("bsm,mk -> bsk" , x , alpha , precision = self .matmul_precision )
235233 intermediate = alpha_scale * h + beta [None , None , :]
236- output = scale * jax .nn .sigmoid (intermediate )
234+ output = scale * jax .nn .sigmoid (intermediate ) + eps
237235 return output
238236
239237 def __call__ (
@@ -269,6 +267,7 @@ def __call__(
269267 self .pre_alpha [...],
270268 self .pre_beta [...],
271269 1.0 ,
270+ eps = 1e-5 ,
272271 )
273272 layer_input = jnp .einsum ("bskd,bsk -> bsd" , x , pre_mapping , precision = self .matmul_precision )
274273
@@ -307,3 +306,71 @@ def __call__(
307306 res_mapping = self .res_mapping (norm_x )
308307 res_out = jnp .einsum ("bskd,bskm -> bsmd" , x , res_mapping , precision = self .matmul_precision )
309308 return res_out + post_out , metadata
309+
310+
311+ class DeepSeek4HyperHead (nnx .Module ):
312+ """Final HC-stream collapse; used by DeepSeek V4 before the shared RMSNorm."""
313+
314+ def __init__ (
315+ self ,
316+ config : Config ,
317+ mesh : Mesh ,
318+ rngs : nnx .Rngs ,
319+ ):
320+ self .config = config
321+ self .mesh = mesh
322+ self .rngs = rngs
323+ self .dtype = config .dtype
324+ self .weight_dtype = config .weight_dtype
325+ self .mhc_expansion_rate = config .mhc_expansion_rate
326+ self .emb_dim = config .emb_dim
327+ self .eps = 1e-6
328+
329+ # Weight matrices
330+ weight_init = nd_dense_init (1.0 , "fan_in" , "normal" )
331+ self .hc_fn = nnx .Param (
332+ weight_init (
333+ rngs .params (),
334+ (self .mhc_expansion_rate * self .emb_dim , self .mhc_expansion_rate ),
335+ self .weight_dtype ,
336+ in_axis = 0 ,
337+ out_axis = 1 ,
338+ ),
339+ out_sharding = ("activation_embed" , None ),
340+ )
341+ self .hc_base = nnx .Param (
342+ default_bias_init (rngs .params (), (self .mhc_expansion_rate ,), self .weight_dtype ),
343+ out_sharding = (None ,),
344+ )
345+ self .hc_scale = nnx .Param (
346+ default_scalar_init (rngs .params (), (1 ,), self .weight_dtype ),
347+ out_sharding = (None ,),
348+ )
349+
350+ def __call__ (self , x : Array ) -> Array :
351+ # x shape: [batch, length, k, d]
352+ b , s , k , d = x .shape
353+ assert k == self .mhc_expansion_rate
354+ assert d == self .emb_dim
355+
356+ flat = jnp .reshape (x , (b , s , k * d ))
357+ flat_f32 = flat .astype (jnp .float32 )
358+ variance = jnp .mean (jnp .square (flat_f32 ), axis = - 1 , keepdims = True )
359+ flat_norm = flat_f32 * jax .lax .rsqrt (variance + self .eps )
360+
361+ hc_fn = jnp .asarray (self .hc_fn [...], jnp .float32 )
362+ hc_base = jnp .asarray (self .hc_base [...], jnp .float32 )
363+ hc_scale = jnp .asarray (self .hc_scale [...], jnp .float32 )
364+
365+ mixes = jnp .einsum ("bsm,mk->bsk" , flat_norm , hc_fn , precision = jax .lax .Precision (self .config .matmul_precision ))
366+ pre = jax .nn .sigmoid (mixes * hc_scale [None , None , :] + hc_base [None , None , :]) + self .eps
367+
368+ x_f32 = x .astype (jnp .float32 )
369+ out = jnp .sum (pre [:, :, :, None ] * x_f32 , axis = 2 )
370+ return out .astype (self .dtype )
371+
372+
373+ DeepSeek4HyperHeadToLinen = nnx_wrappers .to_linen_class (
374+ DeepSeek4HyperHead ,
375+ base_metadata_fn = variable_to_logically_partitioned ,
376+ )
0 commit comments