πŸ”¬ Kimi K3: Delta Attention, Attention Residuals, LatentMoE

A 2026 frontier architecture taken apart equation by equation - linear attention with a bounded forget gate, and a routed MoE that stays stable
Transformers & Attention Advanced
Contents

    Kimi K3 from Scratch in NumPy β€” Kimi Delta Attention, Attention Residuals, Stable LatentMoE, step by step

    This notebook implements the architecture of Kimi K3 (Moonshot AI, technical report arXiv:2607.24653) in pure NumPy, with every intermediate matrix printed and every formula written out β€” the same treatment the companion notebook gave to the original Transformer.

    Kimi K3 is a 2.8-trillion-parameter Mixture-of-Experts model with 104B activated parameters, native vision, and a 1-million-token context window. Everything below runs at $d_{model} = 8$ with 8 tokens, so you can check the arithmetic by hand. The structure is identical; only the numbers shrink.


    What is different from the original Transformer

    If you have the Vaswani notebook in mind, here is the whole delta. Each row is a section below.

    # Transformer (2017) Kimi K3 (2026) Why
    1 Sinusoidal positional encoding added to the input No positional encoding at all (NoPE) position is carried implicitly by the decay/gating of the recurrent layers
    2 Every layer is softmax attention, $O(n^2)$, KV cache grows with $n$ 3 Kimi Delta Attention layers : 1 Gated MLA layer KDA keeps a fixed-size state $S \in \mathbb{R}^{d_k \times d_v}$; only 1 layer in 4 pays the quadratic cost
    3 LayerNorm (mean-centred) RMSNorm (no mean subtraction) cheaper, and empirically no worse
    4 Post-LN: LN(x + Sublayer(x)) Pre-norm: x + Sublayer(RMSNorm(x)) stable at 93 layers
    5 Residual stream: $h_l = h_{l-1} + f(h_{l-1})$ Attention Residuals: each sublayer attends over depth to choose its own input a single running sum is "a bottleneck reminiscent of RNNs over time"
    6 Dense FFN with ReLU Stable LatentMoE: 896 experts, 16 active, in a latent space of width $d/2$, with SiTU-GLU width scaling without width cost
    7 β€” Quantile Balancing for expert load exact dual coordinate minimiser, no learning rate to tune
    8 Encoder + decoder Decoder-only as in every modern LLM

    The chain carried end to end

    tokens β†’ IDs β†’ embeddings β†’ [ AttnRes read β†’ RMSNorm β†’ KDA/MLA β†’ group sum ] Γ—8 β†’ block summaries β†’ Final Block AttnRes β†’ RMSNorm β†’ linear β†’ logits β†’ softmax β†’ next token

    and then the same thing again one token at a time, to show that the KDA recurrent state makes incremental decoding exact and $O(1)$ in sequence length.


    Honest caveat, stated once

    The weights here are random, not trained. Every number is computed correctly and every identity is verified, but the predicted token is meaningless. The purpose is the mechanics of the forward pass.

    Two things are deliberately left out: the vision tower (MoonViT-V2) and the MXFP4 quantisation-aware training. Both are described in the report; neither changes the shapes below.

    NumPy: 2.4.4

    0. Display helpers

    Identical in spirit to the Transformer notebook: every matrix product prints both operands, the result, one element worked out term by term, and the shape rule. Two extra helpers are added for things the Transformer did not have β€” the $d_k \times d_v$ recurrent state, and vectors.

    Display helpers ready: expand, show_matmul, show_elementwise, show_mat, show_state, check.

    1. The task and the hyper-parameters

    One sentence, 8 tokens, decoder-only. There is no encoder and no source/target split β€” Kimi K3 is a causal language model, so the task is simply: given tokens $1 \dots t$, predict token $t+1$.

    The dimensions are chosen so that every structural ratio of the real model survives:

    this notebook Kimi K3 preserved?
    $d_{model}$ 8 7,168 β€”
    tokens $T$ 8 1,000,000 β€”
    layers 8 = 2 blocks Γ— 4 93 β€”
    KDA : MLA per block 3 : 1 3 : 1 βœ…
    chunk size $C$ 4 64–128 β€”
    $g_{\min}$ βˆ’5 βˆ’5 βœ…
    MoE latent $\ell$ 4 = $d/2$ 3,584 = $d/2$ βœ…
    routed experts $n$ 8 896 β€”
    active $k$ 2 16 β€”
    sparsity $n/k$ 4 56 β€”
    shared experts 1 2 β€”
    SiTU-GLU $\beta_1, \beta_2$ 4, 25 4, 25 βœ…
    layer 1 FFN dense dense βœ…
    AttnRes 2 blocks 8 blocks (7Γ—12 + 1Γ—9 layers) β€”

    The target load per expert works out to $q = mk/n = 8 \cdot 2 / 8 = 2$ tokens, which makes the Quantile Balancing section in Β§9 exactly checkable by counting.

    sentence : 'the cat sat on the mat and slept'
    tokens   : ['the', 'cat', 'sat', 'on', 'the', 'mat', 'and', 'slept']
    T = 8, d_model = 8, chunks of 4 -> 2 chunks
    layers   : 8 = 2 blocks x 4 (3 KDA + 1 MLA)
    sublayers: 16  (each layer = attention sublayer + FFN/MoE sublayer)
    MoE      : 8 routed experts, top-2 -> sparsity 4, latent width 4
    target load per expert q = T*k/n = 8*2/8 = 2

    2. Vocabulary and token IDs

    Same convention as the Transformer notebook: IDs are random integers in $[50, 500)$, mimicking a real tokenizer where the ID is an arbitrary index carrying no meaning by itself. Three IDs are reserved below 50.

    Note that "the" appears twice, at positions 0 and 4. In the Transformer notebook this was the motivating example for positional encoding: without PE, the two copies had identical input vectors. Kimi K3 adds no positional encoding, so here the two copies genuinely do start identical β€” and Β§6 shows where they stop being identical.

    vocabulary
       the      -> 83
       cat      -> 68
       sat      -> 170
       on       -> 277
       mat      -> 187
       and      -> 333
       slept    -> 427
    
      position 0: the      id=83
      position 1: cat      id=68
      position 2: sat      id=170
      position 3: on       id=277
      position 4: the      id=83   <- same ID as position 0
      position 5: mat      id=187
      position 6: and      id=333
      position 7: slept    id=427

    3. Weight initialization, RMSNorm, and the activations

    Initialization

    Same Xavier/Glorot scheme as the Transformer notebook β€” zero-mean and variance-preserving. The failure mode it avoids (all-positive weights $\Rightarrow$ monotonically growing activations $\Rightarrow$ representation collapse) is identical here.

    RMSNorm instead of LayerNorm

    LayerNorm subtracts the mean, then divides by the standard deviation: $$\mathrm{LN}(x) = \gamma \odot \frac{x - \mu}{\sqrt{\sigma^2 + \epsilon}} + \beta$$

    RMSNorm drops the mean subtraction entirely: $$\mathrm{RMSNorm}(x) = \gamma \odot \frac{x}{\sqrt{\frac{1}{d}\sum_j x_j^2 + \epsilon}}$$

    It is not just cheaper. Because it is a pure rescaling, it commutes with anything that only cares about direction β€” which is exactly why K3 can drop it into the middle of the MoE (Β§8.5) and into the AttnRes keys (Β§5) without changing what those operations mean.

    Swish, Sigmoid, and the soft cap

    $$\mathrm{Swish}(x) = x\,\sigma(x), \qquad \sigma(x) = \frac{1}{1+e^{-x}}, \qquad \mathrm{softcap}(x,\beta) = \beta \tanh(x/\beta)$$

    RMSNorm, by hand
      x            = [ 2.  -1.   0.5  3.  -2.5  1.   0.  -0.5]
      sum of x^2   = 2.00^2 + -1.00^2 + 0.50^2 + 3.00^2 + -2.50^2 + 1.00^2 + 0.00^2 + -0.50^2
                   = 21.750000
      mean         = 21.750000 / 8 = 2.718750
      rms          = sqrt(2.718750 + 1e-6) = 1.648864
      x / rms      = [ 1.213 -0.607  0.303  1.819 -1.516  0.607  0.    -0.303]
      from helper  = [ 1.213 -0.607  0.303  1.819 -1.516  0.607  0.    -0.303]
      RMSNorm by hand vs helper                                  max|diff| = 0.000e+00   MATCH: True
    
      mean of output = +0.1895   <- NOT zero: RMSNorm does not centre
      rms  of output = +1.0000   <- this is what it fixes

    4. Embeddings β€” and the absence of positional encoding

    $$E \in \mathbb{R}^{V \times d}, \qquad \mathrm{emb}(t) = \mathbf{e}_t^\top E$$

    That is the entire input stage. No $\sqrt{d}$ scaling and no $PE$ term.

    The report is explicit about this (Β§3.4, Positional encoding): K3 "uses no explicit positional embedding (NoPE), and instead encodes positional information implicitly through the recurrent gating and decay mechanism of KDA. As a result, the model extrapolates directly to 1M-token contexts without any positional-encoding modification, such as RoPE rescaling or interpolation."

    Two consequences worth separating:

    1. The KDA layers must supply order. They do, structurally: a recurrence visits tokens one at a time, and the decay $\alpha^{t}$ makes the influence of a past token shrink with distance. Position is not a vector you add; it is a property of the update rule.
    2. The MLA layers are then free to be purely content-based. No RoPE on their queries or keys. When you extend the context from 64K to 1M, there is no frequency base to retune and no YaRN interpolation to apply β€” there is no positional parameter at all.

    The two occurrences of "the" therefore start with identical vectors, unlike in the Transformer notebook where $PE$ separated them at the input.

    Embedding table E : (500, 8)
    X0 = E[ids]       : (8, 8)   <- this IS the input, nothing added
    
    X0  (8, 8)
                     d0         d1         d2         d3         d4         d5         d6         d7
        0:the    -0.252     +0.038     -0.090     +0.532     -0.937     +0.386     +0.441     -0.733
        1:cat    -0.874     -0.282     +0.204     -0.072     +0.131     -0.214     +0.031     -0.055
        2:sat    -0.005     +0.205     +0.042     -0.344     +0.423     -0.056     -0.010     -0.330
         3:on    -0.154     -0.023     +0.742     -0.087     -0.127     -0.229     +0.263     -0.064
        4:the    -0.252     +0.038     -0.090     +0.532     -0.937     +0.386     +0.441     -0.733
        5:mat    +0.729     +0.427     +0.362     +0.209     +0.275     -0.195     -0.289     -0.001
        6:and    +0.164     -0.483     +0.300     -0.436     +0.195     +0.221     -0.246     +0.206
      7:slept    +0.297     +0.073     -0.457     -0.188     -0.215     -0.027     +0.151     +0.148
    
    'the' at position 0 : [-0.252  0.038 -0.09   0.532 -0.937  0.386  0.441 -0.733]
    'the' at position 4 : [-0.252  0.038 -0.09   0.532 -0.937  0.386  0.441 -0.733]
    identical? True   <- yes: NoPE, no order information at the input
    
    Order enters later, in the KDA recurrence (section 6). Nothing is broken here.

    5. Attention Residuals β€” the depth axis

    The problem

    A standard residual stream is $$h_l = h_{l-1} + f_{l-1}(h_{l-1})$$ Every layer receives one vector that has everything summed into it, in fixed proportion. The report's framing (Β§2.2): this "compresses all prior information into a single state $h_l$ over depth β€” a bottleneck reminiscent of RNNs over time."

    The 2017 Transformer solved exactly this problem over time by replacing recurrence with attention. AttnRes applies the same move over depth.

    Full Attention Residuals

    Each layer $l$ gets a learnable pseudo-query $q_l = w_l \in \mathbb{R}^d$. Keys and values are the outputs of all preceding layers, plus the embedding:

    $$k_i = v_i = \begin{cases} h_1 & i = 0 \quad \text{(the token embedding)} \\ f_i(h_i) & 1 \le i \le l-1 \end{cases}$$

    $$\phi(q, k) = \exp\!\big(q^\top \mathrm{RMSNorm}(k)\big), \qquad \alpha_{i \to l} = \frac{\phi(q_l, k_i)}{\sum_{j=0}^{l-1} \phi(q_l, k_j)}, \qquad h_l = \sum_{i=0}^{l-1} \alpha_{i \to l}\, v_i$$

    Three things to notice:

    • The key is normalised, the value is not. Without RMSNorm on the key, a layer that simply produces large-magnitude outputs would dominate every downstream query regardless of content. Normalising removes scale from the routing decision while the value that actually gets mixed in keeps its magnitude.
    • There is no separate residual add. $h_l$ is the weighted sum. The "skip connection" is recovered whenever $\alpha$ puts mass on the embedding or on the running sum.
    • $\alpha$ is computed per token. $w_l$ is shared across positions, but $k_i$ is a per-token vector, so each token in the sequence can read a different mixture of depths.

    Block Attention Residuals

    Keeping all $L$ layer outputs alive costs $O(Ld)$ memory and, under pipeline parallelism, cross-stage communication. So the $L$ layers are partitioned into $N$ blocks of $S = L/N$. Inside block $n$, outputs are summed into a running group sum:

    $$b_n = \sum_{j \in \mathcal{B}_n} f_j(h_j), \qquad b_n^i = \text{partial sum over the first } i \text{ sublayers}, \qquad b_0 = h_1$$

    and attention runs over only the block-level representations:

    $$V = \begin{cases} [\,b_0, b_1, \dots, b_{n-1}\,]^\top & i = 1 \quad \text{(first sublayer of block } n) \\ [\,b_0, b_1, \dots, b_{n-1}, b_n^{\,i-1}\,]^\top & i \ge 2 \end{cases}$$

    Memory drops from $O(Ld)$ to $O(Nd)$. In K3, $L = 93$ and $N = 8$ (seven 12-layer blocks and one 9-layer block), so a sublayer chooses among at most 9 sources instead of 92.

    Every sublayer is its own AttnRes consumer β€” the attention sublayer and the FFN/MoE sublayer each have their own $w$ and their own $\alpha$. So a 4-layer block is 8 AttnRes reads.

    pseudo-query w = [ 1.76   0.742  1.369 -1.078 -0.192 -0.814  1.505  0.658]
      3 source(s): b0 (emb), b1, group sum
    
    logits = w . RMSNorm(k)  (8, 3)
               b0 (emb)         b1  group sum
        0:the   -2.0714    -0.0364    -2.9321
        1:cat   -3.5629    +0.2938    +1.4031
        2:sat   +1.2766    +1.9661    +2.1766
         3:on   +4.6026    +0.8300    -2.4641
        4:the   -2.0714    -1.0778    +2.7572
        5:mat   +4.1770    +5.6275    -0.7467
        6:and   +1.1800    +0.6915    -3.7478
      7:slept   +2.3407    -1.4552    -2.0780
    
    alpha  = softmax over sources  (8, 3)
               b0 (emb)         b1  group sum
        0:the   +0.1102    +0.8432    +0.0466
        1:cat   +0.0052    +0.2467    +0.7481
        2:sat   +0.1834    +0.3655    +0.4511
         3:on   +0.9767    +0.0225    +0.0008
        4:the   +0.0078    +0.0210    +0.9713
        5:mat   +0.1897    +0.8090    +0.0014
        6:and   +0.6170    +0.3786    +0.0045
      7:slept   +0.9666    +0.0217    +0.0116
    
    --- the RMSNorm on the keys, demonstrated ---
    token 0 raw source norms   : b0=1.454, b1=2.368, gs=6.985
    after RMSNorm, all norms   : [2.828 2.828 2.828]   <- equal
    so alpha[0] = [0.11  0.843 0.047] depends on DIRECTION, not on the fact that
    the group sum happens to be ~3x larger. The value mixed in keeps its magnitude.
    
    --- hand check of alpha[0] ---
      logits            = [-2.071 -0.036 -2.932]
      exp(logits - max) = [0.131 1.    0.055]
      sum               = 1.185945
      alpha             = [0.11  0.843 0.047]
      alpha[0] by hand                                           max|diff| = 0.000e+00   MATCH: True
      h[0] = alpha . V                                           max|diff| = 0.000e+00   MATCH: True

    6. Kimi Delta Attention β€” the sequence axis

    Three of every four layers are KDA. This is where the 1M context comes from.

    The update rule

    $$\boxed{\;S_t = \big(I - \beta_t k_t k_t^\top\big)\,\mathrm{Diag}(\alpha_t)\,S_{t-1} + \beta_t k_t v_t^\top, \qquad \tilde o_t = S_t^\top q_t\;}$$

    with $S_t \in \mathbb{R}^{d_k \times d_v}$, $\alpha_t \in (0,1)^{d_k}$, $\beta_t \in (0,1)$.

    Read it as three operations applied in order to the state:

    step operation effect
    decay $\mathrm{Diag}(\alpha_t) S_{t-1}$ scale row $j$ of the state by $\alpha_{t,j}$ β€” a per-channel forget gate
    erase $(I - \beta_t k_t k_t^\top)\,\cdot$ remove the component of the state lying along the current key
    write $+\,\beta_t k_t v_t^\top$ add the new key–value outer product

    The erase step is the delta rule (Schlag et al. 2021): instead of blindly accumulating $k v^\top$ like vanilla linear attention, first clear out whatever was stored at that key. Since $\|k_t\| = 1$ (Β§6.1), $I - \beta k k^\top$ has eigenvalues $1$ (on the orthogonal complement of $k$) and $1-\beta$ (along $k$) β€” a partial projection that can never amplify.

    Why this is not softmax attention

    softmax attention KDA
    what is kept between tokens all past $K, V$ β€” grows as $O(t)$ one matrix $S \in \mathbb{R}^{d_k \times d_v}$ β€” constant
    cost of token $t$ $O(t \cdot d)$ $O(d_k d_v)$
    cost of a length-$n$ sequence $O(n^2 d)$ $O(n\, d_k d_v)$
    decoding step re-read the whole cache update one matrix in place

    The price is that $S$ has finite capacity: $d_k \times d_v$ numbers must hold everything worth remembering. That is why K3 still puts one full-attention MLA layer in every block.

    6.1 Projections β†’ ShortConv β†’ Swish β†’ L2Norm

    $$q_t^h, k_t^h = \mathrm{L2Norm}\Big(\mathrm{Swish}\big(\mathrm{ShortConv}(W^h_{q/k} x_t)\big)\Big) \in \mathbb{R}^{d_k}, \qquad v_t^h = \mathrm{Swish}\big(\mathrm{ShortConv}(W^h_v x_t)\big) \in \mathbb{R}^{d_v}$$

    ShortConv is a causal depth-wise convolution β€” here kernel size 2, so $$y_t = c_0 \odot u_t + c_1 \odot u_{t-1}$$ Each channel gets its own two scalars; nothing mixes across channels. It gives every position a small local receptive field before the recurrence runs, which is where much of the token-shift behaviour of linear-attention models comes from.

    L2Norm on $q$ and $k$ only. Unit-norm keys are what make the delta-rule projector well behaved. The value carries magnitude and is deliberately left un-normalised.

    STEP 1  linear projections
    ==============================================================================
      X (8,8) (8, 8)   @   Wq (8,4) (8, 4)   =   q_proj (8,4) (8, 4)
      shape rule: (8 x 8) @ (8 x 4) -> (8 x 4)   inner dim 8 cancels
    ==============================================================================
    
    X (8,8) =
            0:the |   -0.252    +0.038    -0.090    +0.532    -0.937    +0.386    +0.441    -0.733
            1:cat |   -0.874    -0.282    +0.204    -0.072    +0.131    -0.214    +0.031    -0.055
            2:sat |   -0.005    +0.205    +0.042    -0.344    +0.423    -0.056    -0.010    -0.330
             3:on |   -0.154    -0.023    +0.742    -0.087    -0.127    -0.229    +0.263    -0.064
            4:the |   -0.252    +0.038    -0.090    +0.532    -0.937    +0.386    +0.441    -0.733
            5:mat |   +0.729    +0.427    +0.362    +0.209    +0.275    -0.195    -0.289    -0.001
            6:and |   +0.164    -0.483    +0.300    -0.436    +0.195    +0.221    -0.246    +0.206
          7:slept |   +0.297    +0.073    -0.457    -0.188    -0.215    -0.027    +0.151    +0.148
    
    Wq (8,4) =
         -0.525    -0.001    +0.144    -0.667
         -0.498    +0.606    -0.608    -0.524
         +0.634    +0.172    -0.185    +0.016
         +0.230    -0.318    -0.512    +0.407
         +0.241    +0.018    +0.448    +0.069
         +0.680    -0.418    +0.076    -0.023
         -0.208    +0.130    -0.374    +0.427
         +0.519    -0.525    -0.047    -0.315
    
    q_proj (8,4) =
            0:the |   -0.257    +0.103    -0.836    +0.709
            1:cat |   +0.564    +0.013    +0.078    +0.748
            2:sat |   -0.257    +0.443    +0.247    -0.113
             3:on |   +0.269    +0.303    -0.270    +0.221
            4:the |   -0.257    +0.103    -0.836    +0.709
            5:mat |   -0.325    +0.303    -0.112    -0.718
            6:and |   +0.600    -0.332    +0.672    -0.190
          7:slept |   -0.551    -0.025    +0.018    -0.317
    
      worked element  q_proj (8,4)[1,0]  =  row 1 of X (8,8)  .  column 0 of Wq (8,4)
          row  1 of X (8,8) = [-0.874 -0.282  0.204 -0.072  0.131 -0.213  0.031 -0.055]
          col  0 of Wq (8,4) = [-0.525 -0.498  0.634  0.23   0.241  0.68  -0.207  0.519]
          (-0.8739)*(-0.5253) + (-0.2817)*(-0.4979) + (+0.2040)*(+0.6340) + (-0.0718)*(+0.2303) + 
          (+0.1312)*(+0.2409) + (-0.2135)*(+0.6801) + (+0.0306)*(-0.2075) + (-0.0550)*(+0.5195)
          = +0.563568   |  from array: +0.563568   |  MATCH: True
    STEP 2  ShortConv -- causal, depthwise, kernel 2, one pair of weights PER CHANNEL
      c0 (current token)  = [0.59  0.67  0.65  0.394]
      c1 (previous token) = [0.658 0.903 0.42  0.308]
      y[t] = c0 (x) u[t] + c1 (x) u[t-1]     -- (x) is element-wise, no channel mixing
    
      hand check, token 2, every channel:
        k0: +0.5896*-0.2572 + +0.6584*+0.5636 = +0.219432   array = +0.219432
        k1: +0.6698*+0.4435 + +0.9035*+0.0126 = +0.308445   array = +0.308445
        k2: +0.6496*+0.2473 + +0.4199*+0.0783 = +0.193550   array = +0.193550
        k3: +0.3939*-0.1126 + +0.3080*+0.7485 = +0.186222   array = +0.186222
      ShortConv[2]                                               max|diff| = 0.000e+00   MATCH: True
    
      token 0 has no predecessor, so y[0] = c0 (x) u[0]:
      ShortConv[0] (no history)                                  max|diff| = 0.000e+00   MATCH: True
    
    ShortConv(q_proj)  (8, 4)
                     k0         k1         k2         k3
        0:the   -0.1513    +0.0688    -0.5430    +0.2792
        1:cat   +0.1633    +0.1013    -0.3001    +0.5131
        2:sat   +0.2194    +0.3084    +0.1936    +0.1862
         3:on   -0.0106    +0.6035    -0.0718    +0.0523
        4:the   +0.0259    +0.3425    -0.6565    +0.3472
        5:mat   -0.3604    +0.2960    -0.4240    -0.0645
        6:and   +0.1400    +0.0518    +0.3891    -0.2962
      7:slept   +0.0704    -0.3166    +0.2934    -0.1836
    
      This is the ONLY place before the recurrence where a token sees its neighbour.
      It is what gives linear-attention models their token-shift behaviour.
    STEP 3  Swish, then L2Norm
      Swish(x) = x * sigmoid(x)
    
      hand check token 1 channel 0: Swish(+0.1633) = +0.1633 * 0.540741 = +0.088318   array = +0.088318
    
    Swish(ShortConv(q))  (8, 4)
                     k0         k1         k2         k3
        0:the   -0.0699    +0.0356    -0.1995    +0.1589
        1:cat   +0.0883    +0.0532    -0.1277    +0.3210
        2:sat   +0.1217    +0.1778    +0.1061    +0.1018
         3:on   -0.0053    +0.3902    -0.0346    +0.0268
        4:the   +0.0131    +0.2003    -0.2242    +0.2034
        5:mat   -0.1481    +0.1697    -0.1677    -0.0312
        6:and   +0.0749    +0.0266    +0.2319    -0.1263
      7:slept   +0.0364    -0.1334    +0.1681    -0.0834
    
      L2 norms before: [0.267 0.36  0.261 0.393 0.363 0.282 0.276 0.233]
    Q = L2Norm(...)  (8, 4)
                     k0         k1         k2         k3
        0:the   -0.2621    +0.1334    -0.7476    +0.5955
        1:cat   +0.2450    +0.1477    -0.3542    +0.8904
        2:sat   +0.4666    +0.6817    +0.4068    +0.3901
         3:on   -0.0134    +0.9937    -0.0882    +0.0683
        4:the   +0.0362    +0.5514    -0.6173    +0.5600
        5:mat   -0.5240    +0.6007    -0.5936    -0.1104
        6:and   +0.2716    +0.0964    +0.8409    -0.4581
      7:slept   +0.1563    -0.5725    +0.7210    -0.3578
      L2 norms after : [1. 1. 1. 1. 1. 1. 1. 1.]   <- all 1
    
    K (same pipeline)  (8, 4)
                     k0         k1         k2         k3
        0:the   -0.2063    -0.5586    -0.7716    -0.2236
        1:cat   -0.6983    +0.0642    -0.7052    -0.1043
        2:sat   -0.3172    +0.5506    +0.7704    +0.0513
         3:on   -0.5595    -0.7116    +0.1575    +0.3948
        4:the   -0.3967    -0.6173    -0.6212    +0.2750
        5:mat   +0.0656    -0.6709    -0.6963    -0.2467
        6:and   +0.1843    -0.9239    -0.3220    +0.0935
      7:slept   +0.3487    -0.5323    +0.0883    +0.7663
    
    V = Swish(...) -- NOT normalised  (8, 4)
                     v0         v1         v2         v3
        0:the   -0.0187    -0.1387    -0.1815    +0.2072
        1:cat   +0.0381    -0.0862    -0.1876    +0.3263
        2:sat   +0.2638    +0.0539    +0.1246    -0.0089
         3:on   +0.1608    -0.0792    +0.2606    +0.0543
        4:the   +0.0558    -0.1862    -0.1420    +0.3487
        5:mat   -0.1016    -0.1084    +0.1286    +0.1155
        6:and   -0.0512    -0.0291    +0.1933    -0.1850
      7:slept   +0.2080    +0.0160    +0.0323    -0.1821
      V norms: [0.309 0.388 0.297 0.321 0.424 0.228 0.274 0.279]   <- free to vary; V carries magnitude
    
    --- what unit-norm keys buy: the erase projector ---
      k (unit norm)      = [-0.317  0.551  0.77   0.051],  ||k|| = 1.000000
      P = I - 0.7 k k^T  eigenvalues = [0.3 1.  1.  1. ]
      expected           = [1-beta, 1, 1, 1] = [0.3, 1.0, 1.0, 1.0]
      spectral norm      = 1.000000  <= 1  -> the erase step can never amplify

    6.2 Write strength $\beta$

    $$\beta_t = \sigma(W_\beta x_t) \in (0,1)$$

    A single scalar per token per head. It interpolates between two known algorithms:

    • $\beta \to 0$: the erase term vanishes, $S_t \approx \mathrm{Diag}(\alpha)S_{t-1}$ β€” gated linear attention, pure accumulation.
    • $\beta \to 1$: $(I - kk^\top)$ is a full projection β€” the old content at key $k$ is completely replaced. A hard overwrite.
    beta = sigmoid(W_beta x)
    
      0:the      W_b.x = -0.2556   beta = sigmoid = 0.4364  #################
      1:cat      W_b.x = -0.3775   beta = sigmoid = 0.4067  ################
      2:sat      W_b.x = -0.2852   beta = sigmoid = 0.4292  #################
      3:on       W_b.x = -0.1693   beta = sigmoid = 0.4578  ##################
      4:the      W_b.x = -0.2556   beta = sigmoid = 0.4364  #################
      5:mat      W_b.x = +0.5715   beta = sigmoid = 0.6391  #########################
      6:and      W_b.x = +0.0717   beta = sigmoid = 0.5179  ####################
      7:slept    W_b.x = -0.2033   beta = sigmoid = 0.4494  #################
    
      hand check token 3: sigmoid(-0.169320) = 1/(1+exp(+0.169320)) = 0.457771
      beta[3]                                                    max|diff| = 0.000e+00   MATCH: True
    
      token 3 erases 45.8% of whatever the state holds along k_3, then writes v_3 there.

    6.3 The decay gate β€” and why it is bounded from below

    $$z_t^h = W^{\uparrow}_\alpha W^{\downarrow}_\alpha x_t + b^h_\alpha \in \mathbb{R}^{d_k}, \qquad g_t^h = g_{\min}\,\sigma\!\big(e^{A_h} z_t^h\big) \in (g_{\min}, 0), \qquad \alpha_t^h = \exp(g_t^h) \in (e^{g_{\min}}, 1)$$

    The logit comes through a rank-2 bottleneck ($d \to 2 \to d_k$), so per-channel forgetting is cheap to parameterise. $A_h$ is a learnable per-head log-scale initialised at 0; $b_\alpha$ is initialised negative so that $\sigma(z)$ is small and retention starts close to 1.

    This is the one change from Kimi Linear, and it is a kernel decision

    Kimi Linear (and GDN, and Mamba-2) used an unbounded mapping $$g_t^h = -e^{A_h}\,\mathrm{softplus}(z_t^h) \in (-\infty, 0)$$

    Kimi K3 replaces it with a scaled sigmoid bounded below by $g_{\min} = -5$. The reason has nothing to do with modelling capacity:

    The chunkwise form (Β§6.5) rescales keys by $1/\Gamma$, the reciprocal cumulative decay. Since $\Gamma$ is a product of numbers in $(0,1)$, that reciprocal grows without bound and overflows in finite precision. Kimi Linear had to work around this by computing relative decay in log space and splitting each chunk into 16-token secondary tiles β€” and even then, the diagonal tiles still needed explicit position-pair arithmetic, which the report names as the main intra-chunk bottleneck.

    With $g \ge -5$, the cumulative log-decay over a 16-token tile lies in $(-80, 0)$, so the rescaling factor is at most $e^{80} \approx 5.5 \times 10^{34}$ β€” comfortably inside BF16's $3.4 \times 10^{38}$. Every causal tile, diagonal included, becomes a dense Tensor-Core matmul, and the position-pair path disappears.

    The cost: a channel can never forget faster than $e^{-5} \approx 0.0067$ per step. Over 16 steps that is already $e^{-80}$ β€” nothing survives anyway.

    low-rank decay path:  d_model=8  ->  2  ->  d_k=4
    ==============================================================================
      X (8,8) (8, 8)   @   Wa_dn (8,2) (8, 2)   =   z_low (8,2) (8, 2)
      shape rule: (8 x 8) @ (8 x 2) -> (8 x 2)   inner dim 8 cancels
    ==============================================================================
    
    X (8,8) =
            0:the |   -0.252    +0.038    -0.090    +0.532    -0.937    +0.386    +0.441    -0.733
            1:cat |   -0.874    -0.282    +0.204    -0.072    +0.131    -0.214    +0.031    -0.055
            2:sat |   -0.005    +0.205    +0.042    -0.344    +0.423    -0.056    -0.010    -0.330
             3:on |   -0.154    -0.023    +0.742    -0.087    -0.127    -0.229    +0.263    -0.064
            4:the |   -0.252    +0.038    -0.090    +0.532    -0.937    +0.386    +0.441    -0.733
            5:mat |   +0.729    +0.427    +0.362    +0.209    +0.275    -0.195    -0.289    -0.001
            6:and |   +0.164    -0.483    +0.300    -0.436    +0.195    +0.221    -0.246    +0.206
          7:slept |   +0.297    +0.073    -0.457    -0.188    -0.215    -0.027    +0.151    +0.148
    
    Wa_dn (8,2) =
         -0.537    +0.116
         -0.641    +0.253
         +0.479    +0.644
         -0.081    -0.593
         +0.624    +0.574
         +0.724    +0.146
         +0.269    -0.196
         -0.491    -0.323
    
    z_low (8,2) =
            0:the |   +0.198    -0.725
            1:cat |   +0.715    +0.057
            2:sat |   +0.302    +0.625
             3:on |   +0.318    +0.369
            4:the |   +0.198    -0.725
            5:mat |   -0.555    +0.489
            6:and |   +0.515    +0.474
          7:slept |   -0.596    -0.334
    
      worked element  z_low (8,2)[0,0]  =  row 0 of X (8,8)  .  column 0 of Wa_dn (8,2)
          row  0 of X (8,8) = [-0.252  0.038 -0.09   0.532 -0.937  0.386  0.441 -0.733]
          col  0 of Wa_dn (8,2) = [-0.537 -0.641  0.479 -0.081  0.624  0.724  0.269 -0.491]
          (-0.2520)*(-0.5367) + (+0.0376)*(-0.6407) + (-0.0901)*(+0.4790) + (+0.5317)*(-0.0805) + 
          (-0.9373)*(+0.6235) + (+0.3859)*(+0.7244) + (+0.4406)*(+0.2686) + (-0.7331)*(-0.4909)
          = +0.198487   |  from array: +0.198487   |  MATCH: True
    
    b_alpha (head 0) = [-3.435 -2.717 -2.334 -2.462]   <- negative by construction
    
    z = Wa_up @ z_low + b_alpha  (8, 4)
                     k0         k1         k2         k3
        0:the   -3.2737    -3.4645    -1.7737    -2.4429
        1:cat   -3.1253    -2.9148    -2.0974    -2.4607
        2:sat   -3.3657    -2.2384    -2.6354    -2.4771
         3:on   -3.3326    -2.4839    -2.4581    -2.4704
        4:the   -3.2737    -3.4645    -1.7737    -2.4429
        5:mat   -3.7300    -2.0662    -2.8726    -2.4773
        6:and   -3.2562    -2.4542    -2.4529    -2.4723
      7:slept   -3.6643    -2.8209    -2.3392    -2.4564
    
    g = -5 * sigmoid(e^A z)  (8, 4)
                     k0         k1         k2         k3
        0:the   -0.1824    -0.1517    -0.7254    -0.3998
        1:cat   -0.2104    -0.2571    -0.5467    -0.3933
        2:sat   -0.1669    -0.4818    -0.3345    -0.3874
         3:on   -0.1723    -0.3850    -0.3943    -0.3898
        4:the   -0.1824    -0.1517    -0.7254    -0.3998
        5:mat   -0.1172    -0.5621    -0.2676    -0.3873
        6:and   -0.1855    -0.3956    -0.3961    -0.3891
      7:slept   -0.1249    -0.2810    -0.4397    -0.3949
    
    alpha = exp(g)  -- retention per step  (8, 4)
                     k0         k1         k2         k3
        0:the   +0.8333    +0.8593    +0.4841    +0.6705
        1:cat   +0.8103    +0.7733    +0.5788    +0.6748
        2:sat   +0.8463    +0.6177    +0.7157    +0.6788
         3:on   +0.8417    +0.6805    +0.6742    +0.6772
        4:the   +0.8333    +0.8593    +0.4841    +0.6705
        5:mat   +0.8895    +0.5700    +0.7652    +0.6789
        6:and   +0.8307    +0.6732    +0.6729    +0.6777
      7:slept   +0.8826    +0.7550    +0.6443    +0.6738
      alpha vs helper                                            max|diff| = 0.000e+00   MATCH: True
    
      hand check token 0, channel 0:
        z      = -3.273745
        e^A z  = -3.273745          (A=0 at init)
        sigma  = 0.036483
        g      = -5 * 0.036483 = -0.182415
        alpha  = exp(-0.182415) = 0.833256
    
      half-life per channel (how many steps until a memory decays to 1/2):
        channel k0: mean alpha = 0.8459  ->  half-life = 4.14 tokens
        channel k1: mean alpha = 0.7235  ->  half-life = 2.14 tokens
        channel k2: mean alpha = 0.6274  ->  half-life = 1.49 tokens
        channel k3: mean alpha = 0.6753  ->  half-life = 1.77 tokens
    
      different channels forget at different rates. That is the point of a
      CHANNEL-WISE gate: one head can hold slow context and fast detail at once.
    Why the lower bound exists: 1/Gamma over a 16-token tile
    
         z |  Kimi Linear alpha          1/Gamma |   K3 alpha        1/Gamma
    ------------------------------------------------------------------------------
         0 |          5.000e-01         6.55e+04 |     0.0821       2.35e+17
         2 |          1.192e-01         6.02e+14 |     0.0122       4.00e+30
         5 |          6.693e-03         6.17e+34 |     0.0070       3.24e+34
        10 |          4.540e-05         3.07e+69 |     0.0067       5.52e+34  <-- OVERFLOWS BF16
        20 |          2.061e-09        9.42e+138 |     0.0067       5.54e+34  <-- OVERFLOWS BF16
        50 |          1.929e-22              inf |     0.0067       5.54e+34  <-- OVERFLOWS BF16
    ------------------------------------------------------------------------------
    BF16 max representable      = 3.39e+38
    K3 worst case = e^(-16*g_min) = e^80 = 5.54e+34   -> True, always safe

    6.4 The recurrence, token by token

    This is the definition. Eight tokens, and at each one the state is printed before and after the update, together with the read.

    Watch the two occurrences of "the" (positions 0 and 4). They entered Β§4 with identical vectors. Position 0 reads from an empty state; position 4 reads from a state that has absorbed four tokens. This is where NoPE stops being a problem β€” the recurrence itself is what distinguishes them.

    ==============================================================================
      t = 0   token 'the'    beta = 0.4364    alpha = [0.833 0.859 0.484 0.67 ]
    ==============================================================================
      (1) decayed   Diag(alpha) S_{t-1}  (4, 4)
                     v0         v1         v2         v3
           k0   +0.0000    +0.0000    +0.0000    +0.0000
           k1   +0.0000    +0.0000    +0.0000    +0.0000
           k2   +0.0000    +0.0000    +0.0000    +0.0000
           k3   +0.0000    +0.0000    +0.0000    +0.0000
    
      (2) erase with P = I - 0.4364 * k k^T,   k = [-0.206 -0.559 -0.772 -0.224]
          P @ decayed  (4, 4)
                     v0         v1         v2         v3
           k0   +0.0000    +0.0000    +0.0000    +0.0000
           k1   +0.0000    +0.0000    +0.0000    +0.0000
           k2   +0.0000    +0.0000    +0.0000    +0.0000
           k3   +0.0000    +0.0000    +0.0000    +0.0000
    
      (3) write  + 0.4364 * k v^T,   v = [-0.019 -0.139 -0.181  0.207]
          beta k v^T  (4, 4)
                     v0         v1         v2         v3
           k0   +0.0017    +0.0125    +0.0163    -0.0187
           k1   +0.0045    +0.0338    +0.0443    -0.0505
           k2   +0.0063    +0.0467    +0.0611    -0.0698
           k3   +0.0018    +0.0135    +0.0177    -0.0202
      ==> S_0  (4, 4)
                     v0         v1         v2         v3
           k0   +0.0017    +0.0125    +0.0163    -0.0187
           k1   +0.0045    +0.0338    +0.0443    -0.0505
           k2   +0.0063    +0.0467    +0.0611    -0.0698
           k3   +0.0018    +0.0135    +0.0177    -0.0202
    
      (4) read   o = S^T q,   q = [-0.262  0.133 -0.748  0.596]
          o_0 = [-0.003 -0.026 -0.034  0.038]
          hand check o[0,0] = column 0 of S . q:
            (+0.0017)*(-0.2621) + (+0.0045)*(+0.1334) + (+0.0063)*(-0.7476) + (+0.0018)*(+0.5955)
            = -0.003447   array = -0.003447   MATCH: True
    
    ==============================================================================
      t = 1   token 'cat'    beta = 0.4067    alpha = [0.81  0.773 0.579 0.675]
    ==============================================================================
      (1) decayed   Diag(alpha) S_{t-1}  (4, 4)
                     v0         v1         v2         v3
           k0   +0.0014    +0.0101    +0.0132    -0.0151
           k1   +0.0035    +0.0261    +0.0342    -0.0391
           k2   +0.0036    +0.0270    +0.0354    -0.0404
           k3   +0.0012    +0.0091    +0.0120    -0.0136
    
      (2) erase with P = I - 0.4067 * k k^T,   k = [-0.698  0.064 -0.705 -0.104]
          P @ decayed  (4, 4)
                     v0         v1         v2         v3
           k0   +0.0004    +0.0029    +0.0038    -0.0043
           k1   +0.0036    +0.0268    +0.0351    -0.0401
           k2   +0.0027    +0.0197    +0.0258    -0.0295
           k3   +0.0011    +0.0081    +0.0105    -0.0120
    
      (3) write  + 0.4067 * k v^T,   v = [ 0.038 -0.086 -0.188  0.326]
          beta k v^T  (4, 4)
                     v0         v1         v2         v3
           k0   -0.0108    +0.0245    +0.0533    -0.0927
           k1   +0.0010    -0.0023    -0.0049    +0.0085
           k2   -0.0109    +0.0247    +0.0538    -0.0936
           k3   -0.0016    +0.0037    +0.0080    -0.0138
      ==> S_1  (4, 4)
                     v0         v1         v2         v3
           k0   -0.0104    +0.0274    +0.0571    -0.0970
           k1   +0.0046    +0.0246    +0.0302    -0.0315
           k2   -0.0083    +0.0445    +0.0797    -0.1231
           k3   -0.0005    +0.0117    +0.0185    -0.0259
    
      (4) read   o = S^T q,   q = [ 0.245  0.148 -0.354  0.89 ]
          o_1 = [ 0.001  0.005  0.007 -0.008]
          hand check o[1,0] = column 0 of S . q:
            (-0.0104)*(+0.2450) + (+0.0046)*(+0.1477) + (-0.0083)*(-0.3542) + (-0.0005)*(+0.8904)
            = +0.000580   array = +0.000580   MATCH: True
    
    ==============================================================================
      t = 4   token 'the'    beta = 0.4364    alpha = [0.833 0.859 0.484 0.67 ]
    ==============================================================================
      (1) decayed   Diag(alpha) S_{t-1}  (4, 4)
                     v0         v1         v2         v3
           k0   -0.0658    +0.0277    -0.0348    -0.0663
           k1   -0.0070    +0.0313    -0.0567    -0.0110
           k2   +0.0322    +0.0111    +0.0377    -0.0232
           k3   +0.0220    -0.0041    +0.0409    -0.0050
    
      (2) erase with P = I - 0.4364 * k k^T,   k = [-0.397 -0.617 -0.621  0.275]
          P @ decayed  (4, 4)
                     v0         v1         v2         v3
           k0   -0.0629    +0.0211    -0.0284    -0.0583
           k1   -0.0025    +0.0209    -0.0469    +0.0014
           k2   +0.0367    +0.0007    +0.0476    -0.0107
           k3   +0.0200    +0.0005    +0.0365    -0.0106
    
      (3) write  + 0.4364 * k v^T,   v = [ 0.056 -0.186 -0.142  0.349]
          beta k v^T  (4, 4)
                     v0         v1         v2         v3
           k0   -0.0097    +0.0322    +0.0246    -0.0604
           k1   -0.0150    +0.0502    +0.0383    -0.0939
           k2   -0.0151    +0.0505    +0.0385    -0.0945
           k3   +0.0067    -0.0224    -0.0170    +0.0419
      ==> S_4  (4, 4)
                     v0         v1         v2         v3
           k0   -0.0726    +0.0533    -0.0039    -0.1186
           k1   -0.0176    +0.0711    -0.0086    -0.0925
           k2   +0.0215    +0.0512    +0.0862    -0.1053
           k3   +0.0267    -0.0219    +0.0195    +0.0313
    
      (4) read   o = S^T q,   q = [ 0.036  0.551 -0.617  0.56 ]
          o_4 = [-0.011 -0.003 -0.047  0.027]
          hand check o[4,0] = column 0 of S . q:
            (-0.0726)*(+0.0362) + (-0.0176)*(+0.5514) + (+0.0215)*(-0.6173) + (+0.0267)*(+0.5600)
            = -0.010637   array = -0.010637   MATCH: True
    all eight reads
    
    o_tilde (head 0)  (8, 4)
                     v0         v1         v2         v3
        0:the   -0.0034    -0.0256    -0.0335    +0.0383
        1:cat   +0.0006    +0.0050    +0.0067    -0.0079
        2:sat   +0.0588    +0.0439    +0.0838    -0.0835
         3:on   -0.0106    +0.0333    -0.0678    -0.0080
        4:the   -0.0106    -0.0027    -0.0472    +0.0272
        5:mat   +0.0159    -0.0222    -0.0414    +0.0692
        6:and   +0.0104    +0.0499    -0.0324    -0.0448
      7:slept   +0.0094    +0.0111    +0.0396    -0.0634
    
    --- the two copies of 'the' ---
      input  X0[0] = [-0.252  0.038 -0.09   0.532 -0.937  0.386  0.441 -0.733]
      input  X0[4] = [-0.252  0.038 -0.09   0.532 -0.937  0.386  0.441 -0.733]   identical: True
      output o[0]  = [-0.003 -0.026 -0.034  0.038]
      output o[4]  = [-0.011 -0.003 -0.047  0.027]   identical: False
    
      Identical inputs, different outputs. The recurrence supplied the order
      information that NoPE declined to put in the embedding.
    The state is 4x4 = 16 numbers at token 0 and still 16 numbers
    at token 8 -- and would still be 16 numbers at token 1,000,000.

    6.5 The chunkwise parallel form β€” what the GPU actually runs

    The recurrence above is serial in $t$. A GPU wants wide uniform parallelism. The chunkwise form is serial across chunks of size $C$, fully parallel inside one.

    The quantities

    Within a chunk, let $\gamma^{r} = \prod_{j=1}^{r}\alpha^{j}$ be the cumulative decay from the chunk start, and $\Gamma^{1\to C}$ the $C \times d_k$ matrix stacking $\gamma^1 \dots \gamma^C$.

    $$\tilde V = U - W S_{[t]}, \qquad A = \mathrm{Tril}\Big[(Q \odot \Gamma)(K / \Gamma)^\top\Big]$$

    $$\boxed{\;O_{[t]} = \underbrace{(\Gamma \odot Q)\,S_{[t]}}_{\text{inter-chunk}} \;+\; \underbrace{A\,\tilde V}_{\text{intra-chunk}}\;}$$

    Where $U$ and $W$ come from β€” the UT transform

    Define the normalised state $\hat S_r = \mathrm{Diag}(1/\gamma^r) S_r$. Substituting into the recurrence and using $\mathrm{Diag}(u)(I - \beta k k^\top) = \big(I - \beta (u\odot k)(k/u)^\top\big)\mathrm{Diag}(u)$ gives

    $$\hat S_r = \hat S_{r-1} + \underbrace{(k_r/\gamma^r)}_{\text{"}K/\Gamma\text{"}}\, u_r^\top, \qquad u_r^\top = \beta_r\Big(v_r^\top - (k_r \odot \gamma^r)^\top \hat S_{r-1}\Big)$$

    so every write is a rank-1 update whose magnitude depends on all earlier writes in the chunk. Collecting the $u_r$ into a matrix gives a triangular linear system:

    $$(I + P)\,\tilde V = \mathrm{Diag}(\beta)\big(V - (K\odot\Gamma)S_{[t]}\big), \qquad P = \mathrm{tril}\Big(\mathrm{Diag}(\beta)(K\odot\Gamma)(K/\Gamma)^\top,\,-1\Big)$$

    Inverting once per chunk: $$T = (I+P)^{-1}\mathrm{Diag}(\beta), \qquad U = T V, \qquad W = T(K \odot \Gamma)$$

    Two details that are easy to get wrong

    • The diagonal of $A$ is kept, not masked. Each output reads the state after its own token's update, so $A_{rr} \ne 0$.
    • The query side gets $\odot\,\Gamma$ and the key side gets $/\,\Gamma$. Their product recovers $\gamma^{j+1 \to r}$ β€” the decay accumulated strictly between the two positions. That is the whole trick: a pairwise decay expressed as a ratio of two cumulative products, so it can be folded into the operands of a single matmul.

    Everything below is verified against Β§6.4 at the end.

    chunk 0 has S_[t] = 0, so the inter-chunk term vanishes and only A V~ survives.
    chunk 1 is the interesting one: both terms are live.
    
    ==============================================================================
      CHUNK 0   tokens 0..3
    ==============================================================================
    alpha inside the chunk  (4, 4)
                     k0         k1         k2         k3
        0:the   +0.8333    +0.8593    +0.4841    +0.6705
        1:cat   +0.8103    +0.7733    +0.5788    +0.6748
        2:sat   +0.8463    +0.6177    +0.7157    +0.6788
         3:on   +0.8417    +0.6805    +0.6742    +0.6772
    
    Gamma = cumprod(alpha, axis=0)  (4, 4)
                     k0         k1         k2         k3
        0:the   +0.8333    +0.8593    +0.4841    +0.6705
        1:cat   +0.6752    +0.6644    +0.2802    +0.4524
        2:sat   +0.5714    +0.4104    +0.2006    +0.3071
         3:on   +0.4809    +0.2793    +0.1352    +0.2080
    
      hand check Gamma[2,0] = alpha[0,0]*alpha[1,0]*alpha[2,0]
        = 0.833256 * 0.810283 * 0.846260 = 0.571372   array = 0.571372
    
      S_[t] entering this chunk  (4, 4)
                     v0         v1         v2         v3
           k0   +0.0000    +0.0000    +0.0000    +0.0000
           k1   +0.0000    +0.0000    +0.0000    +0.0000
           k2   +0.0000    +0.0000    +0.0000    +0.0000
           k3   +0.0000    +0.0000    +0.0000    +0.0000
    
    Q (x) Gamma  (4, 4)
                     k0         k1         k2         k3
        0:the   -0.2184    +0.1146    -0.3619    +0.3993
        1:cat   +0.1654    +0.0981    -0.0993    +0.4028
        2:sat   +0.2666    +0.2798    +0.0816    +0.1198
         3:on   -0.0064    +0.2775    -0.0119    +0.0142
    
    K / Gamma  (4, 4)
                     k0         k1         k2         k3
        0:the   -0.2476    -0.6501    -1.5938    -0.3335
        1:cat   -1.0342    +0.0966    -2.5167    -0.2306
        2:sat   -0.5552    +1.3416    +3.8413    +0.1671
         3:on   -1.1634    -2.5480    +1.1649    +1.8980
    
    K (x) Gamma   (key side of the UT system)  (4, 4)
                     k0         k1         k2         k3
        0:the   -0.1719    -0.4800    -0.3736    -0.1499
        1:cat   -0.4715    +0.0427    -0.1976    -0.0472
        2:sat   -0.1812    +0.2260    +0.1545    +0.0158
         3:on   -0.2691    -0.1987    +0.0213    +0.0821
    
      ratio check: (Q(x)Gamma)[3] . (K/Gamma)[1]  should carry gamma^{2->3}
        Gamma[3]/Gamma[1] = [0.712 0.42  0.483 0.46 ]
        alpha[2]*alpha[3] = [0.712 0.42  0.483 0.46 ]   MATCH: True
    
    P = tril(Diag(beta) (K(x)Gamma)(K/Gamma)^T, -1)  (4, 4)
                  0:the      1:cat      2:sat       3:on
        0:the   +0.0000    +0.0000    +0.0000    +0.0000
        1:cat   +0.1707    +0.0000    +0.0000    +0.0000
        2:sat   -0.1517    -0.0786    +0.0000    +0.0000
         3:on   +0.0616    +0.0854    -0.0099    +0.0000
    
    T = (I+P)^-1 Diag(beta)  (4, 4)
                  0:the      1:cat      2:sat       3:on
        0:the   +0.4364    +0.0000    +0.0000    +0.0000
        1:cat   -0.0745    +0.4067    +0.0000    +0.0000
        2:sat   +0.0604    +0.0320    +0.4292    +0.0000
         3:on   -0.0199    -0.0344    +0.0043    +0.4578
    
    U = T V  (4, 4)
                     v0         v1         v2         v3
        0:the   -0.0081    -0.0605    -0.0792    +0.0904
        1:cat   +0.0169    -0.0247    -0.0628    +0.1173
        2:sat   +0.1133    +0.0120    +0.0365    +0.0191
         3:on   +0.0738    -0.0303    +0.1299    +0.0095
    
    W = T (K(x)Gamma)  (4, 4)
                     k0         k1         k2         k3
        0:the   -0.0750    -0.2095    -0.1630    -0.0654
        1:cat   -0.1789    +0.0531    -0.0525    -0.0080
        2:sat   -0.1032    +0.0694    +0.0374    -0.0038
         3:on   -0.1043    -0.0819    +0.0246    +0.0423
    
    V~ = U - W S_[t]  (the actual rank-1 write magnitudes)  (4, 4)
                     v0         v1         v2         v3
        0:the   -0.0081    -0.0605    -0.0792    +0.0904
        1:cat   +0.0169    -0.0247    -0.0628    +0.1173
        2:sat   +0.1133    +0.0120    +0.0365    +0.0191
         3:on   +0.0738    -0.0303    +0.1299    +0.0095
    
    A = Tril[(Q(x)Gamma)(K/Gamma)^T]   -- diagonal KEPT  (4, 4)
                  0:the      1:cat      2:sat       3:on
        0:the   +0.4232    +0.0000    +0.0000    +0.0000
        1:cat   -0.0809    -0.0047    +0.0000    +0.0000
        2:sat   -0.4179    -0.4816    +0.5608    +0.0000
         3:on   -0.1645    +0.0602    +0.3324    -0.6865
    
      A is lower-triangular INCLUDING the diagonal: A[0,0] = +0.423243 != 0
      (each output reads the state AFTER its own token's update)
    
    inter-chunk  (Gamma(x)Q) S_[t]  (4, 4)
                     v0         v1         v2         v3
        0:the   +0.0000    +0.0000    +0.0000    +0.0000
        1:cat   +0.0000    +0.0000    +0.0000    +0.0000
        2:sat   +0.0000    +0.0000    +0.0000    +0.0000
         3:on   +0.0000    +0.0000    +0.0000    +0.0000
    
    intra-chunk  A V~  (4, 4)
                     v0         v1         v2         v3
        0:the   -0.0034    -0.0256    -0.0335    +0.0383
        1:cat   +0.0006    +0.0050    +0.0067    -0.0079
        2:sat   +0.0588    +0.0439    +0.0838    -0.0835
         3:on   -0.0106    +0.0333    -0.0678    -0.0080
    
    O = inter + intra  (4, 4)
                     v0         v1         v2         v3
        0:the   -0.0034    -0.0256    -0.0335    +0.0383
        1:cat   +0.0006    +0.0050    +0.0067    -0.0079
        2:sat   +0.0588    +0.0439    +0.0838    -0.0835
         3:on   -0.0106    +0.0333    -0.0678    -0.0080
    
      S out -> next chunk  (4, 4)
                     v0         v1         v2         v3
           k0   -0.0790    +0.0333    -0.0418    -0.0795
           k1   -0.0081    +0.0364    -0.0660    -0.0128
           k2   +0.0665    +0.0229    +0.0779    -0.0480
           k3   +0.0328    -0.0062    +0.0611    -0.0075
    
    ==============================================================================
      CHUNK 1   tokens 4..7
    ==============================================================================
    alpha inside the chunk  (4, 4)
                     k0         k1         k2         k3
        4:the   +0.8333    +0.8593    +0.4841    +0.6705
        5:mat   +0.8895    +0.5700    +0.7652    +0.6789
        6:and   +0.8307    +0.6732    +0.6729    +0.6777
      7:slept   +0.8826    +0.7550    +0.6443    +0.6738
    
    Gamma = cumprod(alpha, axis=0)  (4, 4)
                     k0         k1         k2         k3
        4:the   +0.8333    +0.8593    +0.4841    +0.6705
        5:mat   +0.7411    +0.4898    +0.3705    +0.4551
        6:and   +0.6156    +0.3297    +0.2493    +0.3084
      7:slept   +0.5434    +0.2490    +0.1606    +0.2078
    
      hand check Gamma[2,0] = alpha[0,0]*alpha[1,0]*alpha[2,0]
        = 0.833256 * 0.889452 * 0.830670 = 0.615643   array = 0.615643
    
      S_[t] entering this chunk  (4, 4)
                     v0         v1         v2         v3
           k0   -0.0790    +0.0333    -0.0418    -0.0795
           k1   -0.0081    +0.0364    -0.0660    -0.0128
           k2   +0.0665    +0.0229    +0.0779    -0.0480
           k3   +0.0328    -0.0062    +0.0611    -0.0075
    
    Q (x) Gamma  (4, 4)
                     k0         k1         k2         k3
        4:the   +0.0301    +0.4738    -0.2988    +0.3755
        5:mat   -0.3884    +0.2942    -0.2199    -0.0503
        6:and   +0.1672    +0.0318    +0.2096    -0.1413
      7:slept   +0.0849    -0.1425    +0.1158    -0.0743
    
    K / Gamma  (4, 4)
                     k0         k1         k2         k3
        4:the   -0.4761    -0.7184    -1.2832    +0.4102
        5:mat   +0.0885    -1.3697    -1.8795    -0.5419
        6:and   +0.2993    -2.8020    -1.2916    +0.3032
      7:slept   +0.6418    -2.1383    +0.5501    +3.6874
    
    K (x) Gamma   (key side of the UT system)  (4, 4)
                     k0         k1         k2         k3
        4:the   -0.3306    -0.5304    -0.3007    +0.1844
        5:mat   +0.0486    -0.3286    -0.2579    -0.1123
        6:and   +0.1135    -0.3046    -0.0803    +0.0288
      7:slept   +0.1895    -0.1325    +0.0142    +0.1592
    
      ratio check: (Q(x)Gamma)[3] . (K/Gamma)[1]  should carry gamma^{2->3}
        Gamma[3]/Gamma[1] = [0.733 0.508 0.434 0.457]
        alpha[2]*alpha[3] = [0.733 0.508 0.434 0.457]   MATCH: True
    
    P = tril(Diag(beta) (K(x)Gamma)(K/Gamma)^T, -1)  (4, 4)
                  4:the      5:mat      6:and    7:slept
        4:the   +0.0000    +0.0000    +0.0000    +0.0000
        5:mat   +0.3182    +0.0000    +0.0000    +0.0000
        6:and   +0.1448    +0.2914    +0.0000    +0.0000
      7:slept   +0.0234    +0.0383    +0.2058    +0.0000
    
    T = (I+P)^-1 Diag(beta)  (4, 4)
                  4:the      5:mat      6:and    7:slept
        4:the   +0.4364    +0.0000    +0.0000    +0.0000
        5:mat   -0.1389    +0.6391    +0.0000    +0.0000
        6:and   -0.0228    -0.1862    +0.5179    +0.0000
      7:slept   -0.0002    +0.0138    -0.1066    +0.4494
    
    U = T V  (4, 4)
                     v0         v1         v2         v3
        4:the   +0.0243    -0.0813    -0.0620    +0.1522
        5:mat   -0.0727    -0.0434    +0.1019    +0.0254
        6:and   -0.0089    +0.0094    +0.0794    -0.1252
      7:slept   +0.0975    +0.0088    -0.0043    -0.0606
    
    W = T (K(x)Gamma)  (4, 4)
                     k0         k1         k2         k3
        4:the   -0.1443    -0.2315    -0.1313    +0.0805
        5:mat   +0.0770    -0.1363    -0.1231    -0.0974
        6:and   +0.0572    -0.0845    +0.0133    +0.0316
      7:slept   +0.0738    -0.0315    +0.0114    +0.0669
    
    V~ = U - W S_[t]  (the actual rank-1 write magnitudes)  (4, 4)
                     v0         v1         v2         v3
        4:the   +0.0172    -0.0645    -0.0780    +0.1320
        5:mat   -0.0563    -0.0388    +0.1116    +0.0231
        6:and   -0.0070    +0.0104    +0.0732    -0.1209
      7:slept   +0.1001    +0.0077    -0.0083    -0.0541
    
    A = Tril[(Q(x)Gamma)(K/Gamma)^T]   -- diagonal KEPT  (4, 4)
                  4:the      5:mat      6:and    7:slept
        4:the   +0.1828    +0.0000    +0.0000    +0.0000
        5:mat   +0.2351    +0.0032    +0.0000    +0.0000
        6:and   -0.4294    -0.3462    -0.3526    +0.0000
      7:slept   -0.1171    +0.0254    +0.2527    +0.1488
    
      A is lower-triangular INCLUDING the diagonal: A[0,0] = +0.182750 != 0
      (each output reads the state AFTER its own token's update)
    
    inter-chunk  (Gamma(x)Q) S_[t]  (4, 4)
                     v0         v1         v2         v3
        4:the   -0.0138    +0.0091    -0.0329    +0.0031
        5:mat   +0.0120    -0.0069    -0.0234    +0.0380
        6:and   -0.0042    +0.0124    -0.0014    -0.0227
      7:slept   -0.0003    +0.0007    +0.0103    -0.0099
    
    intra-chunk  A V~  (4, 4)
                     v0         v1         v2         v3
        4:the   +0.0031    -0.0118    -0.0143    +0.0241
        5:mat   +0.0039    -0.0153    -0.0180    +0.0311
        6:and   +0.0146    +0.0375    -0.0310    -0.0221
      7:slept   +0.0097    +0.0103    +0.0293    -0.0535
    
    O = inter + intra  (4, 4)
                     v0         v1         v2         v3
        4:the   -0.0106    -0.0027    -0.0472    +0.0272
        5:mat   +0.0159    -0.0222    -0.0414    +0.0692
        6:and   +0.0104    +0.0499    -0.0324    -0.0448
      7:slept   +0.0094    +0.0111    +0.0396    -0.0634
    
      S out -> next chunk  (4, 4)
                     v0         v1         v2         v3
           k0   -0.0163    +0.0373    +0.0119    -0.1148
           k1   -0.0343    +0.0225    -0.0873    +0.0784
           k2   +0.0344    +0.0272    -0.0210    -0.0216
           k3   +0.0909    +0.0041    -0.0082    -0.0419
    ==============================================================================
      EQUIVALENCE:  the serial recurrence and the chunkwise kernel
    ==============================================================================
    recurrent  (eq. 1, token by token)  (8, 4)
                     v0         v1         v2         v3
        0:the   -0.0034    -0.0256    -0.0335    +0.0383
        1:cat   +0.0006    +0.0050    +0.0067    -0.0079
        2:sat   +0.0588    +0.0439    +0.0838    -0.0835
         3:on   -0.0106    +0.0333    -0.0678    -0.0080
        4:the   -0.0106    -0.0027    -0.0472    +0.0272
        5:mat   +0.0159    -0.0222    -0.0414    +0.0692
        6:and   +0.0104    +0.0499    -0.0324    -0.0448
      7:slept   +0.0094    +0.0111    +0.0396    -0.0634
    
    chunkwise  (eq. 4, two chunks)  (8, 4)
                     v0         v1         v2         v3
        0:the   -0.0034    -0.0256    -0.0335    +0.0383
        1:cat   +0.0006    +0.0050    +0.0067    -0.0079
        2:sat   +0.0588    +0.0439    +0.0838    -0.0835
         3:on   -0.0106    +0.0333    -0.0678    -0.0080
        4:the   -0.0106    -0.0027    -0.0472    +0.0272
        5:mat   +0.0159    -0.0222    -0.0414    +0.0692
        6:and   +0.0104    +0.0499    -0.0324    -0.0448
      7:slept   +0.0094    +0.0111    +0.0396    -0.0634
    
      outputs                                                    max|diff| = 2.776e-17   MATCH: True
      final state                                                max|diff| = 1.388e-17   MATCH: True
    
      Same function, different parallelism. This identity is what FlashKDA exploits:
      because the intra-chunk work is a matmul and the cross-chunk work is a state
      scan, the two can be OVERLAPPED instead of alternating, and the SMs never idle
      during the serial propagation.

    6.6 Head-wise RMSNorm, the full-rank gate, and $W_O$

    $$y_t = W_o\Big[\sigma(W_g x_t) \odot \mathrm{RMSNorm}(\tilde o_t)\Big]$$

    Two changes from Kimi Linear, both stated in Β§2.1.1 of the report:

    • RMSNorm is applied per head, before concatenation. Without it, a head whose values happen to be large would dominate the concatenated vector and therefore $W_o$.
    • $W_g$ is full rank. Kimi Linear used a low-rank output gate; K3 makes it a full $d \times (H d_v)$ projection so every output channel can be modulated independently by the current token. The same change is applied to MLA (Β§10).
    o_tilde, both heads concatenated  (8, 8)
                   h0v0       h0v1       h0v2       h0v3       h1v0       h1v1       h1v2       h1v3
        0:the   -0.0034    -0.0256    -0.0335    +0.0383    -0.0999    +0.0233    +0.0683    +0.0609
        1:cat   +0.0006    +0.0050    +0.0067    -0.0079    -0.0818    -0.0213    +0.0946    +0.0188
        2:sat   +0.0588    +0.0439    +0.0838    -0.0835    -0.0822    -0.0067    +0.0671    -0.0009
         3:on   -0.0106    +0.0333    -0.0678    -0.0080    -0.0226    -0.0082    -0.0107    -0.0206
        4:the   -0.0106    -0.0027    -0.0472    +0.0272    -0.0965    -0.0244    +0.0872    -0.0035
        5:mat   +0.0159    -0.0222    -0.0414    +0.0692    -0.1163    +0.0544    +0.0794    +0.0791
        6:and   +0.0104    +0.0499    -0.0324    -0.0448    +0.0114    -0.0287    -0.0903    -0.1177
      7:slept   +0.0094    +0.0111    +0.0396    -0.0634    +0.0350    -0.0059    -0.0247    -0.0320
    
      per-head RMS before norm: head0 = [0.029 0.006 0.07 ] ...
                                head1 = [0.069 0.064 0.053] ...
    
    RMSNorm applied PER HEAD  (8, 8)
                   h0v0       h0v1       h0v2       h0v3       h1v0       h1v1       h1v2       h1v3
        0:the   -0.1207    -0.8971    -1.1745    +1.3403    -1.4534    +0.3394    +0.9935    +0.8857
        1:cat   +0.0993    +0.8587    +1.1487    -1.3476    -1.2757    -0.3329    +1.4748    +0.2926
        2:sat   +0.8445    +0.6308    +1.2037    -1.1996    -1.5461    -0.1261    +1.2618    -0.0178
         3:on   -0.2772    +0.8679    -1.7675    -0.2075    -1.3512    -0.4876    -0.6357    -1.2321
        4:the   -0.3829    -0.0976    -1.6974    +0.9785    -1.4574    -0.3690    +1.3176    -0.0535
        5:mat   +0.3726    -0.5225    -0.9724    +1.6249    -1.3645    +0.6379    +0.9322    +0.9283
        6:and   +0.2773    +1.3264    -0.8611    -1.1914    +0.1506    -0.3792    -1.1914    -1.5535
      7:slept   +0.2471    +0.2913    +1.0396    -1.6645    +1.3009    -0.2204    -0.9180    -1.1878
    
      head 0, per token:
    
          token   mean sq before    rms after   sqrt(m/(m+eps))
      ----------------------------------------------------------
          0:the        8.143e-04     0.999387          0.999387
          1:cat        3.306e-05     0.985211          0.985211
          2:sat        4.849e-03     0.999897          0.999897
           3:on        1.469e-03     0.999660          0.999660
          4:the        7.707e-04     0.999352          0.999352
          5:mat        1.810e-03     0.999724          0.999724
          6:and        1.412e-03     0.999646          0.999646
        7:slept        1.450e-03     0.999655          0.999655
      ----------------------------------------------------------
    
      RMSNorm does NOT give exactly 1. It gives sqrt(m/(m+eps)), because
      the epsilon sits inside the square root. Matches to floating point:
          max|rms - sqrt(m/(m+eps))| = 1.11e-16   MATCH: True
    
      The deviation is largest where the activations are smallest -
      at mean square 3.3e-05 the eps is not negligible beside it and
      the rms comes out 0.9852. At realistic activation scales
      eps is invisible; here the model is deliberately tiny.
    
    sigma(W_g x)  -- full rank, one value per output channel  (8, 8)
                   h0v0       h0v1       h0v2       h0v3       h1v0       h1v1       h1v2       h1v3
        0:the   +0.5107    +0.6613    +0.5027    +0.5880    +0.4810    +0.6943    +0.5640    +0.4624
        1:cat   +0.5123    +0.4588    +0.5348    +0.5461    +0.5687    +0.4172    +0.5981    +0.5936
        2:sat   +0.5544    +0.4829    +0.4683    +0.5306    +0.4676    +0.4252    +0.5112    +0.5251
         3:on   +0.5361    +0.6019    +0.5327    +0.5495    +0.3777    +0.3941    +0.5100    +0.4566
        4:the   +0.5107    +0.6613    +0.5027    +0.5880    +0.4810    +0.6943    +0.5640    +0.4624
        5:mat   +0.4194    +0.4529    +0.4141    +0.4895    +0.4070    +0.5011    +0.3737    +0.3412
        6:and   +0.5485    +0.5300    +0.4815    +0.5634    +0.4382    +0.4186    +0.6054    +0.5037
      7:slept   +0.5283    +0.5216    +0.5479    +0.4317    +0.5193    +0.5607    +0.4379    +0.5242
    
    ==============================================================================
      gated = gate * RMSNorm(o)      all three are (8, 8) - element-wise, no mixing
    ==============================================================================
    
           the | 
                   gate:   +0.511    +0.661    +0.503    +0.588    +0.481    +0.694    +0.564    +0.462
             RMSNorm(o):   -0.121    -0.897    -1.174    +1.340    -1.453    +0.339    +0.993    +0.886
                  gated:   -0.062    -0.593    -0.590    +0.788    -0.699    +0.236    +0.560    +0.410
    
           cat | 
                   gate:   +0.512    +0.459    +0.535    +0.546    +0.569    +0.417    +0.598    +0.594
             RMSNorm(o):   +0.099    +0.859    +1.149    -1.348    -1.276    -0.333    +1.475    +0.293
                  gated:   +0.051    +0.394    +0.614    -0.736    -0.726    -0.139    +0.882    +0.174
    
           sat | 
                   gate:   +0.554    +0.483    +0.468    +0.531    +0.468    +0.425    +0.511    +0.525
             RMSNorm(o):   +0.844    +0.631    +1.204    -1.200    -1.546    -0.126    +1.262    -0.018
                  gated:   +0.468    +0.305    +0.564    -0.637    -0.723    -0.054    +0.645    -0.009
    
            on | 
                   gate:   +0.536    +0.602    +0.533    +0.550    +0.378    +0.394    +0.510    +0.457
             RMSNorm(o):   -0.277    +0.868    -1.768    -0.208    -1.351    -0.488    -0.636    -1.232
                  gated:   -0.149    +0.522    -0.941    -0.114    -0.510    -0.192    -0.324    -0.563
    
           the | 
                   gate:   +0.511    +0.661    +0.503    +0.588    +0.481    +0.694    +0.564    +0.462
             RMSNorm(o):   -0.383    -0.098    -1.697    +0.978    -1.457    -0.369    +1.318    -0.053
                  gated:   -0.196    -0.065    -0.853    +0.575    -0.701    -0.256    +0.743    -0.025
    
           mat | 
                   gate:   +0.419    +0.453    +0.414    +0.489    +0.407    +0.501    +0.374    +0.341
             RMSNorm(o):   +0.373    -0.523    -0.972    +1.625    -1.364    +0.638    +0.932    +0.928
                  gated:   +0.156    -0.237    -0.403    +0.795    -0.555    +0.320    +0.348    +0.317
    
           and | 
                   gate:   +0.549    +0.530    +0.482    +0.563    +0.438    +0.419    +0.605    +0.504
             RMSNorm(o):   +0.277    +1.326    -0.861    -1.191    +0.151    -0.379    -1.191    -1.554
                  gated:   +0.152    +0.703    -0.415    -0.671    +0.066    -0.159    -0.721    -0.782
    
         slept | 
                   gate:   +0.528    +0.522    +0.548    +0.432    +0.519    +0.561    +0.438    +0.524
             RMSNorm(o):   +0.247    +0.291    +1.040    -1.664    +1.301    -0.220    -0.918    -1.188
                  gated:   +0.131    +0.152    +0.570    -0.719    +0.675    -0.124    -0.402    -0.623
    
      check element [0,0]: +0.510670 * -0.120712 = -0.061644   MATCH: True
    
    ==============================================================================
      gated (8,8) (8, 8)   @   W_o (8,8) (8, 8)   =   KDA output (8,8) (8, 8)
      shape rule: (8 x 8) @ (8 x 8) -> (8 x 8)   inner dim 8 cancels
    ==============================================================================
    
    gated (8,8) =
            0:the |   -0.062    -0.593    -0.590    +0.788    -0.699    +0.236    +0.560    +0.410
            1:cat |   +0.051    +0.394    +0.614    -0.736    -0.726    -0.139    +0.882    +0.174
            2:sat |   +0.468    +0.305    +0.564    -0.637    -0.723    -0.054    +0.645    -0.009
             3:on |   -0.149    +0.522    -0.941    -0.114    -0.510    -0.192    -0.324    -0.563
            4:the |   -0.196    -0.065    -0.853    +0.575    -0.701    -0.256    +0.743    -0.025
            5:mat |   +0.156    -0.237    -0.403    +0.795    -0.555    +0.320    +0.348    +0.317
            6:and |   +0.152    +0.703    -0.415    -0.671    +0.066    -0.159    -0.721    -0.782
          7:slept |   +0.131    +0.152    +0.570    -0.719    +0.675    -0.124    -0.402    -0.623
    
    W_o (8,8) =
         +0.224    -0.116    -0.252    +0.402    -0.276    -0.574    -0.310    -0.586
         +0.381    -0.463    +0.580    -0.352    -0.238    +0.104    -0.235    -0.148
         -0.485    -0.136    -0.581    -0.338    +0.501    -0.268    -0.281    +0.220
         +0.469    -0.250    -0.151    +0.263    +0.358    +0.139    -0.393    -0.391
         +0.316    +0.253    +0.485    +0.343    -0.096    -0.449    +0.478    +0.239
         +0.094    +0.223    -0.141    -0.192    +0.092    -0.021    +0.256    +0.534
         +0.425    -0.138    -0.156    +0.523    -0.129    +0.368    -0.499    -0.060
         +0.020    +0.378    +0.462    -0.425    +0.143    +0.158    -0.427    +0.409
    
    KDA output (8,8) =
            0:the |   +0.464    +0.118    -0.375    +0.424    +0.219    +0.822    -0.714    -0.222
            1:cat |   -0.345    -0.358    -0.420    -0.354    -0.095    +0.425    -0.888    +0.105
            2:sat |   -0.310    -0.400    -0.621    -0.173    -0.166    +0.084    -0.802    -0.191
             3:on |   +0.240    -0.408    +0.475    -0.023    -0.603    +0.402    +0.341    -0.587
            4:the |   +0.685    -0.322    -0.011    +0.592    -0.208    +1.004    -0.672    -0.647
            5:mat |   +0.522    -0.050    -0.285    +0.287    +0.179    +0.525    -0.685    -0.310
            6:and |   -0.129    -0.334    +0.518    -0.213    -0.697    -0.411    +0.853    -0.367
          7:slept |   -0.508    -0.020    -0.047    -0.073    -0.158    -0.859    +0.804    +0.172
    
      worked element  KDA output (8,8)[1,0]  =  row 1 of gated (8,8)  .  column 0 of W_o (8,8)
          row  1 of gated (8,8) = [ 0.051  0.394  0.614 -0.736 -0.726 -0.139  0.882  0.174]
          col  0 of W_o (8,8) = [ 0.224  0.381 -0.485  0.469  0.316  0.093  0.425  0.02 ]
          (+0.0509)*(+0.2242) + (+0.3940)*(+0.3805) + (+0.6143)*(-0.4847) + (-0.7359)*(+0.4690) + 
          (-0.7256)*(+0.3159) + (-0.1389)*(+0.0935) + (+0.8821)*(+0.4253) + (+0.1737)*(+0.0199)
          = -0.345146   |  from array: -0.345146   |  MATCH: True

    7. Stable LatentMoE β€” the width axis

    Kimi K3 has 896 routed experts per layer with 16 active per token β€” a sparsity of 56 β€” plus 2 always-on shared experts. Kimi K2 had 384 experts with 8 active. Getting from one to the other required three changes, and the report is candid that two of them are bug-fixes for failure modes that only appear at 2.8T scale.

    7.1 Why a latent space

    In a conventional MoE, each selected expert receives the full $d$-dimensional token. So the all-to-all communication and the expert-weight traffic both scale with the routing multiplicity $k$. Going from $k=8$ to $k=16$ would double both.

    LatentMoE separates the model width from the routed-expert width:

    $$z = W^{\downarrow}x \in \mathbb{R}^{\ell}, \qquad \ell = d/2$$

    Routed experts live entirely in $\mathbb{R}^{\ell}$; only the shared experts keep a full-width path. In K3, $\ell = 3584$ against $d = 7168$.

    $$u = \sum_{i \in \mathcal{T}_k(x)} p_i\, E^{\mathrm{routed}}_i\big(W^{\downarrow}x\big), \qquad y = \sum_{j=1}^{N_s} E^{\mathrm{shared}}_j(x) \;+\; W^{\uparrow}\,\mathrm{RMSNorm}(u)$$

    7.2 The two failure modes at extreme sparsity

    Exploding activations. The routed path composes $W^{\downarrow}$, a gated multi-branch FFN, and $W^{\uparrow}$ β€” nearly four consecutive matmuls. That chain is ill-conditioned, and at 2.8T parameters it blows up. Fixed by (a) SiTU-GLU inside the expert and (b) the RMSNorm before $W^{\uparrow}$.

    Load balancing. Balancing ~$10^3$ experts is outside the regime where the fixed-step sign update of auxiliary-loss-free routing behaves. Fixed by Quantile Balancing (Β§9).

    7.3 SiTU-GLU

    $$\mathrm{SiTU\text{-}GLU}(x) = \underbrace{\Big[\beta_1 \tanh\!\big(\tfrac{W_g x}{\beta_1}\big) \odot \sigma(W_g x)\Big]}_{\text{capped Swish gate}} \odot \underbrace{\Big[\beta_2 \tanh\!\big(\tfrac{W_u x}{\beta_2}\big)\Big]}_{\text{capped up branch}}$$

    Compare with what it replaces:

    gate branch up branch bounded?
    GLU $\sigma(x)$ $x$ no (up branch unbounded)
    SwiGLU $x\,\sigma(x)$ $x$ no β€” both factors unbounded
    SiTU-GLU $\beta_1\tanh(x/\beta_1)\,\sigma(x)$ $\beta_2\tanh(x/\beta_2)$ yes, $\le \beta_1\beta_2$

    Two properties make it a drop-in replacement rather than a different activation:

    Local agreement. Since $\beta\tanh(z/\beta) = z + O(z^3/\beta^2)$, SiTU-GLU matches SwiGLU to first order near the origin. With $\beta_1 = 4, \beta_2 = 25$ the agreement holds to three decimals out to $|x| \approx 1$.

    Hard bound. Since $|\tanh| < 1$ and $0 < \sigma < 1$: $$\|\mathrm{SiTU\text{-}GLU}(x)\|_\infty \le \beta_1\beta_2 = 100$$

    Why a smooth cap and not a clamp: hard-clamping the pre-activations also bounds the output, but with exactly zero gradient past the clamp. $\tanh$ saturates smoothly, so gradients stay nonzero away from the boundary. The report attributes better training behaviour to precisely this.

    SwiGLU vs SiTU-GLU on a scalar slice (both branches fed the same x)
    
           x   Swish(x)       SwiGLU   SiTU gate   SiTU up    SiTU-GLU    ratio
    ----------------------------------------------------------------------------
       -4.00    -0.0719        0.288     -0.0548    -3.966      0.2173    0.755
       -1.00    -0.2689        0.269     -0.2635    -0.999      0.2633    0.979
       -0.25    -0.1095        0.027     -0.1093    -0.250      0.0273    0.999
        0.00     0.0000        0.000      0.0000     0.000      0.0000    1.000
        0.25     0.1405        0.035      0.1404     0.250      0.0351    0.999
        1.00     0.7311        0.731      0.7162     0.999      0.7158    0.979
        4.00     3.9281       15.712      2.9916     3.966     11.8653    0.755
       20.00    20.0000      400.000      3.9996    16.601     66.3976    0.166  <- capped
       60.00    60.0000     3600.000      4.0000    24.592     98.3675    0.027  <- capped
      200.00   200.0000    40000.000      4.0000    25.000    100.0000    0.002  <- capped
    ----------------------------------------------------------------------------
    bound = b1*b2 = 4.0*25.0 = 100
    at x=200: SwiGLU = 40000, SiTU-GLU = 100.000

    7.4 The routing rule

    $$s_i = \sigma(W_r x_i) \in (0,1)^n, \qquad \mathcal{T}_i = \operatorname{argtop}_k\big(s_i + b\big), \qquad p_{i,j} = \frac{s_{i,j}}{\sum_{r \in \mathcal{T}_i} s_{i,r}},\quad j \in \mathcal{T}_i$$

    Two details that are easy to read past:

    $b$ appears in the selection but not in $p$. The bias steers dispatch without altering the mixture weights or the gradient-based optimisation of the router. Load balancing therefore never distorts what the layer computes β€” it only changes which experts compute it.

    Routing takes Top-$(k{+}1)$, not Top-$k$. The first $k$ entries are the routes actually taken; the $(k{+}1)$-th entry is the cutoff $\alpha_i$ β€” the score an expert must exceed to enter token $i$'s Top-$k$. It costs nothing extra here and is exactly what Β§9 needs, avoiding a separate token-side quantile pass.

    ==============================================================================
      x (8,8) (8, 8)   @   W_r (8,8) (8, 8)   =   router logits (8,8) (8, 8)
      shape rule: (8 x 8) @ (8 x 8) -> (8 x 8)   inner dim 8 cancels
    ==============================================================================
    
    x (8,8) =
            0:the |   +0.969    +0.246    -0.784    +0.887    +0.459    +1.717    -1.492    -0.463
            1:cat |   -0.787    -0.817    -0.958    -0.807    -0.216    +0.969    -2.025    +0.239
            2:sat |   -0.745    -0.962    -1.492    -0.416    -0.400    +0.203    -1.926    -0.459
             3:on |   +0.566    -0.963    +1.121    -0.055    -1.424    +0.948    +0.804    -1.386
            4:the |   +1.149    -0.539    -0.018    +0.992    -0.349    +1.684    -1.127    -1.085
            5:mat |   +1.289    -0.125    -0.705    +0.708    +0.443    +1.297    -1.692    -0.765
            6:and |   -0.260    -0.675    +1.046    -0.431    -1.409    -0.832    +1.723    -0.742
          7:slept |   -1.099    -0.043    -0.102    -0.157    -0.341    -1.861    +1.742    +0.374
    
    W_r (8,8) =
         -0.558    -0.398    -0.146    +0.157    +0.308    +0.091    -0.270    -0.393
         -0.096    -0.299    +0.318    +0.481    +0.373    -0.464    +0.352    -0.594
         -0.447    +0.246    -0.218    +0.401    +0.231    +0.172    +0.123    +0.506
         +0.407    -0.576    +0.594    -0.084    -0.250    +0.452    +0.513    +0.334
         +0.269    -0.600    -0.274    +0.382    +0.610    -0.269    +0.320    -0.474
         +0.303    -0.176    -0.518    -0.397    +0.083    -0.034    +0.229    -0.186
         +0.118    -0.152    +0.328    -0.604    +0.109    +0.107    -0.541    +0.324
         +0.165    +0.551    -0.383    -0.092    +0.447    +0.544    +0.227    -0.180
    
    router logits (8,8) =
            0:the |   +0.539    -1.768    -0.693    +0.319    +0.040    -0.352    +1.426    -1.563
            1:cat |   +0.654    +1.187    -1.615    -0.098    -0.731    -0.285    +0.696    -0.737
            2:sat |   +0.656    +0.702    -0.572    -0.169    -1.470    -0.422    +0.323    -0.420
             3:on |   -0.976    +0.170    +0.028    -1.200    -1.234    +0.349    -1.369    +1.908
            4:the |   -0.072    -1.386    -0.477    -0.192    -0.781    +0.127    +0.645    -0.125
            5:mat |   +0.083    -1.715    -0.709    +0.547    -0.138    -0.385    +1.066    -1.413
            6:and |   -0.984    +1.130    +1.006    -1.092    -1.055    +0.462    -2.001    +2.402
          7:slept |   +0.210    +0.988    +1.562    -0.699    -0.345    +0.375    -1.205    +1.358
    
      worked element  router logits (8,8)[0,0]  =  row 0 of x (8,8)  .  column 0 of W_r (8,8)
          row  0 of x (8,8) = [ 0.969  0.246 -0.784  0.887  0.459  1.717 -1.492 -0.463]
          col  0 of W_r (8,8) = [-0.558 -0.096 -0.447  0.407  0.269  0.303  0.118  0.165]
          (+0.9692)*(-0.5581) + (+0.2463)*(-0.0960) + (-0.7844)*(-0.4468) + (+0.8868)*(+0.4068) + 
          (+0.4585)*(+0.2692) + (+1.7169)*(+0.3034) + (-1.4919)*(+0.1180) + (-0.4632)*(+0.1648)
          = +0.538693   |  from array: +0.538693   |  MATCH: True
    
    s = sigmoid(W_r x)  (8, 8)
                     E0         E1         E2         E3         E4         E5         E6         E7
        0:the   +0.6315    +0.1458    +0.3335    +0.5791    +0.5100    +0.4128    +0.8063    +0.1732
        1:cat   +0.6578    +0.7662    +0.1659    +0.4756    +0.3250    +0.4293    +0.6674    +0.3236
        2:sat   +0.6584    +0.6686    +0.3608    +0.4578    +0.1869    +0.3960    +0.5799    +0.3966
         3:on   +0.2737    +0.5424    +0.5071    +0.2314    +0.2254    +0.5864    +0.2028    +0.8707
        4:the   +0.4819    +0.2001    +0.3830    +0.4521    +0.3140    +0.5317    +0.6559    +0.4688
        5:mat   +0.5207    +0.1525    +0.3298    +0.6334    +0.4656    +0.4049    +0.7438    +0.1958
        6:and   +0.2721    +0.7559    +0.7322    +0.2513    +0.2583    +0.6134    +0.1191    +0.9170
      7:slept   +0.5522    +0.7288    +0.8266    +0.3321    +0.4147    +0.5927    +0.2305    +0.7954
    
    bias b = [0. 0. 0. 0. 0. 0. 0. 0.]   (all zero at initialisation)
    
         token |    Top-2 experts |  cutoff a_i | mixture weights p
    --------------------------------------------------------------------------
    0:the      |           E6, E0 |     +0.5791 | E6:0.561  E0:0.439
    1:cat      |           E1, E6 |     +0.6578 | E1:0.534  E6:0.466
    2:sat      |           E1, E0 |     +0.5799 | E1:0.504  E0:0.496
    3:on       |           E7, E5 |     +0.5424 | E7:0.598  E5:0.402
    4:the      |           E6, E5 |     +0.4819 | E6:0.552  E5:0.448
    5:mat      |           E6, E3 |     +0.5207 | E6:0.540  E3:0.460
    6:and      |           E7, E1 |     +0.7322 | E7:0.548  E1:0.452
    7:slept    |           E2, E7 |     +0.7288 | E2:0.510  E7:0.490
    --------------------------------------------------------------------------
    
    hand check, token 0, p normalisation over the SELECTED experts only:
      s[E6] = 0.806269,  s[E0] = 0.631508
      sum        = 1.437777
      p[E6] = 0.806269 / 1.437777 = 0.560775   array = 0.560775
      p sums to  = 1.000000
    
    expert loads this batch: [2 3 1 1 0 2 4 3]   target q = 2 each
    imbalance: max 4 vs min 0   <- section 9 fixes this exactly
    ==============================================================================
      DOWN-PROJECTION into the latent space
    ==============================================================================
    ==============================================================================
      x (8,8) (8, 8)   @   W_down (8,4) (8, 4)   =   z (8,4) (8, 4)
      shape rule: (8 x 8) @ (8 x 4) -> (8 x 4)   inner dim 8 cancels
      routed experts see 4 dims, not 8 -- this is what makes k=16 affordable
    ==============================================================================
    
    x (8,8) =
            0:the |   +0.969    +0.246    -0.784    +0.887    +0.459    +1.717    -1.492    -0.463
            1:cat |   -0.787    -0.817    -0.958    -0.807    -0.216    +0.969    -2.025    +0.239
            2:sat |   -0.745    -0.962    -1.492    -0.416    -0.400    +0.203    -1.926    -0.459
             3:on |   +0.566    -0.963    +1.121    -0.055    -1.424    +0.948    +0.804    -1.386
            4:the |   +1.149    -0.539    -0.018    +0.992    -0.349    +1.684    -1.127    -1.085
            5:mat |   +1.289    -0.125    -0.705    +0.708    +0.443    +1.297    -1.692    -0.765
            6:and |   -0.260    -0.675    +1.046    -0.431    -1.409    -0.832    +1.723    -0.742
          7:slept |   -1.099    -0.043    -0.102    -0.157    -0.341    -1.861    +1.742    +0.374
    
    W_down (8,4) =
         +0.398    +0.150    +0.297    -0.581
         +0.185    +0.680    -0.108    -0.548
         +0.648    +0.249    -0.428    +0.243
         +0.697    -0.411    +0.500    +0.281
         -0.394    -0.446    +0.642    -0.226
         -0.083    +0.221    -0.084    -0.707
         -0.339    -0.039    +0.369    +0.150
         -0.099    -0.437    +0.403    -0.442
    
    z (8,4) =
            0:the |   +0.769    +0.189    +0.452    -1.976
            1:cat |   -0.980    -0.295    -1.011    -0.602
            2:sat |   -0.892    -0.466    -0.857    +0.340
             3:on |   +1.081    +1.149    -1.490    +0.842
            4:the |   +1.523    +0.439    -0.316    -0.898
            5:mat |   +0.894    +0.131    +0.294    -1.586
            6:and |   +0.262    +0.641    -1.165    +2.147
          7:slept |   -0.959    -0.645    +0.375    +2.083
    
      worked element  z (8,4)[0,0]  =  row 0 of x (8,8)  .  column 0 of W_down (8,4)
          row  0 of x (8,8) = [ 0.969  0.246 -0.784  0.887  0.459  1.717 -1.492 -0.463]
          col  0 of W_down (8,4) = [ 0.398  0.185  0.648  0.697 -0.394 -0.083 -0.339 -0.099]
          (+0.9692)*(+0.3976) + (+0.2463)*(+0.1849) + (-0.7844)*(+0.6481) + (+0.8868)*(+0.6968) + 
          (+0.4585)*(-0.3936) + (+1.7169)*(-0.0829) + (-1.4919)*(-0.3390) + (-0.4632)*(-0.0987)
          = +0.769182   |  from array: +0.769182   |  MATCH: True
    
    ==============================================================================
      ONE EXPERT, token 0, worked out
    ==============================================================================
    token 0 'the' -> expert E6 with p = 0.5608
      z (latent input) = [ 0.769  0.189  0.452 -1.976]
    
      W_g x        = [ 0.575 -1.474 -1.047  0.534 -0.241  0.707 -0.227 -0.902]
      b1*tanh(./b1)= [ 0.571 -1.411 -1.024  0.531 -0.24   0.699 -0.227 -0.887]   (b1=4.0)
      sigma(W_g x) = [0.64  0.186 0.26  0.63  0.44  0.67  0.444 0.289]
      gate branch  = [ 0.365 -0.263 -0.266  0.335 -0.106  0.468 -0.101 -0.256]
      W_u x        = [-0.172 -1.51  -1.103 -0.295 -1.29   0.151  1.327  1.476]
      up branch    = [-0.172 -1.508 -1.103 -0.295 -1.289  0.151  1.326  1.475]   (b2=25.0)
      product      = [-0.063  0.396  0.293 -0.099  0.136  0.071 -0.133 -0.378]
      E_down @ product = [-0.103 -0.144 -0.353  0.477]      (back to 4 dims)
      x p              = [-0.058 -0.081 -0.198  0.267]
    
      bound check: max |SiTU-GLU| = 0.3964  <=  b1*b2 = 100

    7.5 The RMSNorm before $W^{\uparrow}$ β€” the "Normalized LatentMoE" fix

    The original LatentMoE applies $W^{\uparrow}$ directly to the aggregate $u$. The problem is that $\|u\|$ varies with which experts happened to win and how the gate weights fell β€” two things that change from token to token and from step to step. That hands the up-projection an input whose scale is essentially random.

    K3 inserts an RMSNorm between aggregation and up-projection. Beyond stabilising training, the report notes it "consistently improves validation loss and downstream benchmarks" β€” it is not purely a numerical guard.

    The cell below shows the scale variation across just eight tokens.

    routed aggregate u, before normalisation
    
    u  (8, 4)
                     l0         l1         l2         l3
        0:the   +0.1999    -0.3091    -0.3016    +0.0541
        1:cat   +0.0447    -0.1444    +0.0252    +0.2425
        2:sat   +0.0424    -0.0268    -0.0535    +0.0358
         3:on   +0.0780    -0.8425    +0.4785    +0.3684
        4:the   +0.2508    -0.3155    +0.1232    +0.5136
        5:mat   +0.0195    -0.0471    -0.0379    +0.2216
        6:and   -0.4231    -0.1006    +0.6512    +0.0513
      7:slept   +0.3167    -0.3376    +0.2335    -0.0349
    
      0:the      experts (6, 0)  ||u|| = 0.47894
      1:cat      experts (1, 6)  ||u|| = 0.28685
      2:sat      experts (1, 0)  ||u|| = 0.08162
      3:on       experts (7, 5)  ||u|| = 1.03951
      4:the      experts (6, 5)  ||u|| = 0.66440
      5:mat      experts (6, 3)  ||u|| = 0.23052
      6:and      experts (7, 1)  ||u|| = 0.78472
      7:slept    experts (2, 7)  ||u|| = 0.51963
    
      min ||u|| = 0.08162   max ||u|| = 1.03951   ratio = 12.74x  across EIGHT tokens
    
    RMSNorm(u)  (8, 4)
                     l0         l1         l2         l3
        0:the   +0.8346    -1.2907    -1.2596    +0.2258
        1:cat   +0.3120    -1.0065    +0.1760    +1.6907
        2:sat   +1.0384    -0.6566    -1.3109    +0.8774
         3:on   +0.1500    -1.6210    +0.9207    +0.7087
        4:the   +0.7549    -0.9497    +0.3709    +1.5461
        5:mat   +0.1689    -0.4088    -0.3284    +1.9225
        6:and   -1.0784    -0.2563    +1.6596    +0.1306
      7:slept   +1.2189    -1.2995    +0.8986    -0.1344
    
      rms of each row now : [1. 1. 1. 1. 1. 1. 1. 1.]
      sqrt(m/(m+eps))     : [1. 1. 1. 1. 1. 1. 1. 1.]
      MATCH: True   (not exactly 1 - the eps inside the sqrt, as in section 3)
    
      What matters here is the SPREAD: ||u|| ranged over 12.7x across these eight
      tokens before the norm, and the rows are now on one scale. That is
      what stops the up-projection seeing wildly different input magnitudes
      depending on which experts a token happened to pick.
    
    ==============================================================================
      RMSNorm(u) (8,4) (8, 4)   @   W_up (4,8) (4, 8)   =   routed branch (8,8) (8, 8)
      shape rule: (8 x 4) @ (4 x 8) -> (8 x 8)   inner dim 4 cancels
    ==============================================================================
    
    RMSNorm(u) (8,4) =
            0:the |   +0.835    -1.291    -1.260    +0.226
            1:cat |   +0.312    -1.006    +0.176    +1.691
            2:sat |   +1.038    -0.657    -1.311    +0.877
             3:on |   +0.150    -1.621    +0.921    +0.709
            4:the |   +0.755    -0.950    +0.371    +1.546
            5:mat |   +0.169    -0.409    -0.328    +1.923
            6:and |   -1.078    -0.256    +1.660    +0.131
          7:slept |   +1.219    -1.300    +0.899    -0.134
    
    W_up (4,8) =
         +0.316    -0.004    +0.074    -0.164    -0.420    -0.467    +0.200    -0.317
         -0.687    -0.435    +0.596    +0.299    +0.114    -0.139    +0.450    -0.335
         +0.188    -0.690    +0.206    +0.668    +0.295    -0.599    +0.129    -0.259
         +0.594    -0.681    -0.690    -0.571    -0.227    +0.541    +0.091    +0.230
    
    routed branch (8,8) =
            0:the |   +1.048    +1.273    -1.123    -1.494    -0.920    +0.666    -0.556    +0.546
            1:cat |   +1.827    -0.837    -1.707    -1.200    -0.578    +0.803    -0.213    +0.581
            2:sat |   +1.054    +0.588    -1.190    -1.744    -1.096    +0.866    -0.177    +0.432
             3:on |   +1.755    -0.414    -1.254    -0.299    -0.137    -0.012    -0.515    +0.420
            4:the |   +1.879    -0.900    -1.501    -1.043    -0.667    +0.394    -0.087    +0.338
            5:mat |   +1.414    -0.906    -1.626    -1.467    -0.651    +1.214    -0.017    +0.610
            6:and |   +0.225    -1.119    +0.019    +1.135    +0.883    -0.383    -0.104    +0.027
          7:slept |   +1.368    +0.032    -0.406    +0.088    -0.364    -0.999    -0.236    -0.214
    
      worked element  routed branch (8,8)[0,0]  =  row 0 of RMSNorm(u) (8,4)  .  column 0 of W_up (4,8)
          row  0 of RMSNorm(u) (8,4) = [ 0.835 -1.291 -1.26   0.226]
          col  0 of W_up (4,8) = [ 0.317 -0.687  0.188  0.594]
          (+0.8346)*(+0.3165) + (-1.2907)*(-0.6870) + (-1.2596)*(+0.1882) + (+0.2258)*(+0.5935)
          = +1.047784   |  from array: +1.047784   |  MATCH: True
    
    ==============================================================================
      MoE output = shared (full width) + routed (via latent)      all three are (8, 8) - element-wise, no mixing
    ==============================================================================
    
           the | 
       shared (full width):   -0.567    -0.076    +0.484    -1.012    -0.023    -0.078    +0.658    -0.192
       routed (via latent):   +1.048    +1.273    -1.123    -1.494    -0.920    +0.666    -0.556    +0.546
             MoE output:   +0.480    +1.198    -0.639    -2.506    -0.942    +0.588    +0.102    +0.354
    
           cat | 
       shared (full width):   -0.204    -0.098    +0.317    -0.180    -0.059    -0.374    +0.371    +0.349
       routed (via latent):   +1.827    -0.837    -1.707    -1.200    -0.578    +0.803    -0.213    +0.581
             MoE output:   +1.623    -0.936    -1.391    -1.380    -0.636    +0.429    +0.158    +0.930
    
           sat | 
       shared (full width):   +0.064    -0.132    +0.218    +0.071    -0.249    -0.417    +0.526    +0.055
       routed (via latent):   +1.054    +0.588    -1.190    -1.744    -1.096    +0.866    -0.177    +0.432
             MoE output:   +1.118    +0.456    -0.973    -1.673    -1.345    +0.449    +0.349    +0.487
    
            on | 
       shared (full width):   -0.551    -0.663    +0.552    -0.261    +0.041    -0.101    +0.688    +0.015
       routed (via latent):   +1.755    -0.414    -1.254    -0.299    -0.137    -0.012    -0.515    +0.420
             MoE output:   +1.204    -1.077    -0.702    -0.561    -0.096    -0.114    +0.173    +0.435
    
           the | 
       shared (full width):   -0.148    -0.033    +0.432    -0.485    -0.213    +0.110    +0.726    -0.072
       routed (via latent):   +1.879    -0.900    -1.501    -1.043    -0.667    +0.394    -0.087    +0.338
             MoE output:   +1.731    -0.932    -1.069    -1.529    -0.880    +0.503    +0.640    +0.266
    
           mat | 
       shared (full width):   -0.495    -0.003    +0.464    -0.721    -0.156    -0.035    +0.777    -0.213
       routed (via latent):   +1.414    -0.906    -1.626    -1.467    -0.651    +1.214    -0.017    +0.610
             MoE output:   +0.919    -0.909    -1.162    -2.189    -0.807    +1.179    +0.760    +0.397
    
           and | 
       shared (full width):   -0.388    -0.275    +0.093    -0.582    +0.210    -0.023    +0.161    -0.099
       routed (via latent):   +0.225    -1.119    +0.019    +1.135    +0.883    -0.383    -0.104    +0.027
             MoE output:   -0.164    -1.394    +0.112    +0.552    +1.093    -0.407    +0.056    -0.071
    
         slept | 
       shared (full width):   +0.096    +0.247    -0.241    -0.383    -0.011    +0.165    +0.205    -0.022
       routed (via latent):   +1.368    +0.032    -0.406    +0.088    -0.364    -0.999    -0.236    -0.214
             MoE output:   +1.464    +0.279    -0.647    -0.295    -0.375    -0.834    -0.031    -0.236
    
      check element [0,0]: -0.567454 + +1.047784 = +0.480330   MATCH: True

    9. Quantile Balancing

    The load imbalance from Β§7.4, fixed exactly. This is the part of the paper with a real derivation behind it, so it is worth doing properly.

    9.1 From balanced assignment to a quantile

    Let $s \in \mathbb{R}^{m \times n}$ collect router scores for $m$ tokens over $n$ experts, and $x_{i,j} \in \{0,1\}$ indicate assignment. The maximum-score balanced assignment is

    $$\max_{x_{i,j} \in \{0,1\}} \sum_{i,j} x_{i,j}s_{i,j} \quad\text{s.t.}\quad \sum_j x_{i,j} = k, \qquad \sum_i x_{i,j} = \frac{mk}{n}$$

    Relaxing to $x_{i,j} \in [0,1]$ gives a linear program whose optimum is integral (bipartite $b$-matching), so the relaxation is exact. Introducing multipliers $\alpha_i$ (token side) and $\beta_j$ (expert side), applying the minimax theorem, and eliminating $x$ gives the convex dual

    $$\min_{\alpha,\beta}\; \mathcal{L}(\alpha,\beta) = \sum_{i,j}\max\big(0,\; s_{i,j} - \alpha_i - \beta_j\big) \;+\; k\sum_i \alpha_i \;+\; \frac{mk}{n}\sum_j \beta_j$$

    Each coordinate subproblem is piecewise linear, so its minimiser is an order statistic. For a fixed $\beta$, token $i$ solves $\min_\alpha k\alpha + \sum_j \max(0, s_{i,j}-\beta_j-\alpha)$, whose slope is $k$ minus the number of margins exceeding $\alpha$ β€” minimised when exactly $k$ margins lie above. Symmetrically for $\beta$. Both are the same quantile, along different axes:

    $$\alpha_i^* = \mathrm{quantile}_{1-k/n}\big(s_i - \beta\big), \qquad \beta_j^* = \mathrm{quantile}_{1-k/n}\big(s_{:,j} - \alpha\big)$$

    which is where the method gets its name.

    9.2 The update actually used

    $$\widehat b_j \leftarrow -\,\mathrm{quantile}_{1-k/n}\big(s_{:,j} - \alpha^{(t)}\big), \qquad b^{(t+1)} \leftarrow \widehat b - \mathrm{mean}(\widehat b)\,\mathbf{1}$$

    with $\alpha^{(t)}$ the Top-$(k{+}1)$ cutoff already computed during routing. Setting $-\widehat b_j$ to the $(q{+}1)$-th largest margin leaves exactly $q$ margins above the threshold β€” i.e. exactly $q$ tokens routed to expert $j$. Mean-centring is free: Top-$k$ is invariant to a common offset.

    9.3 The connection to what it replaces

    Differentiating the expert-side term:

    $$\frac{\partial \mathcal{L}}{\partial \beta_j} = \frac{mk}{n} - \sum_{i=1}^m \mathbb{1}\big[s_{i,j}-\alpha_i-\beta_j > 0\big] = \text{target load} - \text{observed load}$$

    A SignSGD step on this gradient is exactly the fixed-step sign update $b_j^{(t+1)} = b_j^{(t)} + \gamma\,\mathrm{sign}(\bar\ell - \ell_j)$ of DeepSeek-V3-style auxiliary-loss-free balancing. It keeps only the direction of the load error. QB jumps straight to the exact coordinate minimiser of the same dual objective.

    That explains both reported properties at once: no learning-rate-like $\gamma$ to tune, and equilibration within a few steps even for ~$10^3$ experts.

    (Causality: the new bias applies from the next step β€” a batch is never routed with a bias derived from itself. At inference the bias is frozen, so serving is a plain Top-$k$ with a constant vector.)

    m = 8 tokens, n = 8 experts, k = 2  ->  target load q = mk/n = 2
    
    margins  s[i,j] - alpha_i  (8, 8)
                     E0         E1         E2         E3         E4         E5         E6         E7
        0:the   +0.0524    -0.4334    -0.2457    +0.0000    -0.0692    -0.1663    +0.2271    -0.4060
        1:cat   +0.0000    +0.1084    -0.4920    -0.1822    -0.3328    -0.2285    +0.0096    -0.3342
        2:sat   +0.0785    +0.0886    -0.2192    -0.1221    -0.3930    -0.1840    +0.0000    -0.1834
         3:on   -0.2687    +0.0000    -0.0353    -0.3110    -0.3170    +0.0440    -0.3396    +0.3283
        4:the   +0.0000    -0.2818    -0.0989    -0.0298    -0.1679    +0.0498    +0.1740    -0.0131
        5:mat   +0.0000    -0.3682    -0.1909    +0.1127    -0.0551    -0.1157    +0.2231    -0.3249
        6:and   -0.4600    +0.0238    +0.0000    -0.4809    -0.4739    -0.1188    -0.6131    +0.1848
      7:slept   -0.1765    +0.0000    +0.0978    -0.3967    -0.3141    -0.1360    -0.4982    +0.0666
    
      a margin > 0 means the expert beat the cutoff, i.e. it was selected.
      positives per column: [2 3 1 1 0 2 4 3]   = the loads [2 3 1 1 0 2 4 3]
    
    each expert's margins, sorted downward
    
      rank        E0         E1         E2         E3         E4         E5         E6         E7
    -----------------------------------------------------------------------------------------------
         1   +0.0785    +0.1084    +0.0978    +0.1127    -0.0551    +0.0498    +0.2271    +0.3283
         2   +0.0524    +0.0886    +0.0000    +0.0000    -0.0692    +0.0440    +0.2231    +0.1848
         3   +0.0000    +0.0238    -0.0353    -0.0298    -0.1679    -0.1157    +0.1740    +0.0666  <== (q+1)
         4   +0.0000    +0.0000    -0.0989    -0.1221    -0.3141    -0.1188    +0.0096    -0.0131
         5   +0.0000    +0.0000    -0.1909    -0.1822    -0.3170    -0.1360    +0.0000    -0.1834
         6   -0.1765    -0.2818    -0.2192    -0.3110    -0.3328    -0.1663    -0.3396    -0.3249
         7   -0.2687    -0.3682    -0.2457    -0.3967    -0.3930    -0.1840    -0.4982    -0.3342
         8   -0.4600    -0.4334    -0.4920    -0.4809    -0.4739    -0.2285    -0.6131    -0.4060
    -----------------------------------------------------------------------------------------------
    
    b_hat = -(q+1)-th largest margin = [-0.    -0.024  0.035  0.03   0.168  0.116 -0.174 -0.067]
    mean  = +0.010551
    b_new = b_hat - mean            = [-0.011 -0.034  0.025  0.019  0.157  0.105 -0.185 -0.077]
    
    s + b_new  (what the NEXT step will route on)  (8, 8)
                     E0         E1         E2         E3         E4         E5         E6         E7
        0:the   +0.6210    +0.1114    +0.3582    +0.5984    +0.6673    +0.5180    +0.6218    +0.0960
        1:cat   +0.6473    +0.7319    +0.1906    +0.4949    +0.4824    +0.5345    +0.4829    +0.2464
        2:sat   +0.6479    +0.6342    +0.3856    +0.4771    +0.3443    +0.5011    +0.3954    +0.3194
         3:on   +0.2632    +0.5081    +0.5319    +0.2507    +0.3828    +0.6916    +0.0183    +0.7936
        4:the   +0.4714    +0.1658    +0.4078    +0.4714    +0.4714    +0.6369    +0.4714    +0.3916
        5:mat   +0.5101    +0.1182    +0.3546    +0.6526    +0.6229    +0.5101    +0.5593    +0.1186
        6:and   +0.2616    +0.7216    +0.7570    +0.2706    +0.4156    +0.7186    -0.0655    +0.8398
      7:slept   +0.5417    +0.6944    +0.8514    +0.3513    +0.5720    +0.6979    +0.0460    +0.7182
    
                       E0     E1     E2     E3     E4     E5     E6     E7   spread
         load before    2      3      1      1      0      2      4      3   4
          load after    2      2      2      1      3      2      1      3   2
              target    2      2      2      2      2      2      2      2
    
    exactly balanced after ONE update: False
    spread went 4 -> 2, but not all the way to 0.
    The next cell explains why -- and it is not a bug.

    Why one update is not a fixed point, and what "balanced" really means here

    Two separate things are going on, and it is worth pulling them apart.

    1. The update is one coordinate step, not the joint optimum. The margins were computed against $\alpha^{(t)}$, the cutoffs produced by routing with the old bias. Change $b$ and the cutoffs move too: demoting an expert frees a slot, which lowers the cutoff for the tokens that were using it, which changes every other expert's margins. Eq. 14 is therefore one block-coordinate step of an alternating minimisation. Algorithm 1 in Appendix C of the report is the full solver:

    $$\alpha \leftarrow \mathrm{desc\_sort}(s - \beta,\ \mathrm{axis}{=}1)_{[:,\,k:k+1]}, \qquad \beta \leftarrow \mathrm{desc\_sort}(s - \alpha,\ \mathrm{axis}{=}0)_{[mk/n\,:\,mk/n+1]}$$

    That this is deliberately not iterated during training is a causality requirement: the new bias takes effect only at the next step, so a batch is never routed with a bias derived from itself.

    2. With 8 tokens, perfect balance is not a meaningful target. The load is an integer. A target of $q = 2$ means one token moving changes an expert's load by 50%. The quantity that actually matters is the relative imbalance $(\ell_{\max} - \ell_{\min}) / q$, and that is what shrinks β€” the third cell below measures it as the batch grows.

    This is the regime QB is built for: in K3 a batch is millions of tokens against 896 experts, so $q$ is large and the granularity argument above disappears. The report's claim is that the histogram estimator leaves "no measurable residual load imbalance" at that scale β€” not that a batch of 8 can be split perfectly.

    What is genuinely absent, at any batch size, is a step size. The sign-update method QB replaces has a $\gamma$ trading adaptation speed against load oscillation. QB jumps to the exact coordinate minimiser, so there is nothing to tune β€” that is the claim the next cells test.

    ALGORITHM 1 -- the alternating solver  (bias b = -beta)
    
      iter 1:  loads = [3 2 2 1 2 2 1 3]   spread = 2
      iter 2:  loads = [2 2 2 2 2 2 1 3]   spread = 2
      iter 3:  loads = [2 2 2 2 2 2 1 3]   spread = 2
      iter 4:  loads = [2 2 2 2 2 3 1 2]   spread = 2
      iter 5:  loads = [2 2 2 2 2 3 1 2]   spread = 2
      iter 6:  loads = [2 2 2 2 2 3 1 2]   spread = 2
    
      converged bias b* = [-0.011 -0.035  0.025  0.042  0.157  0.105 -0.185 -0.098]
      loads             = [2 2 2 2 2 3 1 2]   (target 2 each, spread 2)
    
      It converges in ONE iteration and then stops moving -- but not to a perfect split.
      With q = 2, a single token is 50% of an expert's quota. See the scaling cell below.
    TRAINING-TIME VIEW -- one Eq.14 update per step, bias carried forward
    
      step 1: loads = [2 3 1 1 0 2 4 3]   spread = 4
      step 2: loads = [2 2 2 1 3 2 1 3]   spread = 2
      step 3: loads = [2 2 2 2 2 3 1 2]   spread = 2
      step 4: loads = [2 2 2 2 2 2 1 3]   spread = 2
      step 5: loads = [2 2 2 2 2 3 1 2]   spread = 2
      step 6: loads = [2 2 2 2 2 3 1 2]   spread = 2
      step 7: loads = [2 2 2 2 2 3 1 2]   spread = 2
      step 8: loads = [2 2 2 2 2 3 1 2]   spread = 2
    
      spread over steps: [4, 2, 2, 2, 2, 2, 2, 2]
      it settles after 2 step(s) and stays there --
      no oscillation, which is the failure mode a fixed-step sign update has to trade against.
    THE QUANTITY THAT ACTUALLY MATTERS: relative imbalance vs batch size
    
      m =     8  q =    2   no balancing:  2.000   with QB:  1.500   (absolute spread 3)
      m =    16  q =    4   no balancing:  1.250   with QB:  0.750   (absolute spread 3)
      m =    32  q =    8   no balancing:  0.875   with QB:  0.875   (absolute spread 7)
      m =    64  q =   16   no balancing:  0.438   with QB:  0.375   (absolute spread 6)
      m =   128  q =   32   no balancing:  0.344   with QB:  0.094   (absolute spread 3)
      m =   256  q =   64   no balancing:  0.422   with QB:  0.125   (absolute spread 8)
      m =   512  q =  128   no balancing:  0.203   with QB:  0.086   (absolute spread 11)
      m =  1024  q =  256   no balancing:  0.168   with QB:  0.035   (absolute spread 9)
      m =  4096  q = 1024   no balancing:  0.054   with QB:  0.017   (absolute spread 17)
    
      relative imbalance under QB: 1.50 at m=8  ->  0.0166 at m=4096
      K3 trains with m in the millions against 896 experts, far to the right of this plot.

    At real scale: the histogram estimator

    With $m$ in the millions, sharded across data-parallel ranks and gradient-accumulation steps, gathering $O(mn)$ margins for an exact quantile is not viable inside the training loop.

    The observation that makes it cheap: the update never needs the margins themselves, only their per-expert distribution. So each rank scatter-adds its local values into a per-expert count matrix $H \in \mathbb{N}^{n \times B}$ with no communication, and one integer all-reduce of $nB$ values pools them at the end of the step.

    Because counts are additive, the pooled histogram is exactly invariant to how tokens were sharded. The estimate is therefore the quantile of the true global batch up to one bin width β€” not an average of per-rank quantiles, which is a different number. With $B = 1000$ the error is a few $10^{-3}$, at a communication cost below 1% of the alternative.

    bins = 2000, ranks = 4  ->  one all-reduce of 8x2000 integers
    bin width w = 4.707e-04
    
    exact     b = [-0.011 -0.034  0.025  0.019  0.157  0.105 -0.185 -0.077]
    histogram b = [-0.011 -0.034  0.025  0.019  0.158  0.105 -0.185 -0.077]
    max |diff|  = 2.825e-04   <= bin width w = 4.707e-04:  True
    
    The error is bounded by the bin width because cumulative counts are exact at bin
    edges -- the true quantile and its estimate always land in the SAME bin.
    
    loads with the exact bias    : [2 2 2 1 3 2 1 3]
    loads with the histogram bias: [2 2 2 1 3 2 1 3]   identical: True
    the estimator changes nothing about the routing decision -- only how the
    quantile was obtained, at 1/100th of the communication cost.
    
    invariance check -- shard the same tokens differently:
      1 rank(s): b = [-0.011 -0.034  0.025  0.019  0.158  0.105 -0.185 -0.077]
      2 rank(s): b = [-0.011 -0.034  0.025  0.019  0.158  0.105 -0.185 -0.077]
      4 rank(s): b = [-0.011 -0.034  0.025  0.019  0.158  0.105 -0.185 -0.077]
      8 rank(s): b = [-0.011 -0.034  0.025  0.019  0.158  0.105 -0.185 -0.077]
      identical regardless of sharding, because counts are additive.

    10. Gated MLA β€” the one global layer per block

    Three KDA layers gave position-sensitive, recency-weighted mixing through a fixed-size state. The fourth layer of every block gives unrestricted content-based access to every earlier token. The report also places an extra Gated MLA at the very end of the backbone, so the final layer always performs global attention.

    10.1 The latent KV compression

    $$c_t = W_c x_t \in \mathbb{R}^{d_c}, \qquad k^h_t = W^h_{uk}c_t, \qquad v^h_t = W^h_{uv}c_t$$

    Only $c_t$ is cached. The per-head keys and values are reconstructed on the fly by the up-projections during attention. That is the whole MLA idea (from DeepSeek-V2): pay $d_c$ per token in cache instead of $2 H d_h$.

    10.2 NoPE

    Unlike Kimi K2, K3 applies no positional encoding to the MLA queries or keys. The separation of duties:

    • KDA layers β†’ position-sensitive, recency-aware sequence mixing
    • MLA layers β†’ unrestricted global content interaction

    The practical payoff is at context extension time. Going from 64K to 1M requires no RoPE frequency-base retuning and no YaRN interpolation, because there is no positional parameter to adjust.

    10.3 The gate

    $$y_t = W_o\Big[\sigma(W_g x_t) \odot \tilde o_t\Big]$$

    Same full-rank gate as KDA, letting each token modulate which channels it reads from global attention.

    STEP 1  compress every token to a latent -- THIS is the cache
    ==============================================================================
      x (8,8) (8, 8)   @   W_c (8,4) (8, 4)   =   c (8,4) (8, 4)
      shape rule: (8 x 8) @ (8 x 4) -> (8 x 4)   inner dim 8 cancels
    ==============================================================================
    
    x (8,8) =
            0:the |   +0.436    +1.087    -0.580    -2.273    -0.855    +0.534    +0.092    +0.321
            1:cat |   +1.545    -0.891    -1.324    -1.314    -0.606    +0.409    +0.151    +0.886
            2:sat |   +1.149    +0.469    -1.000    -1.720    -1.383    +0.461    +0.358    +0.501
             3:on |   +1.781    -1.593    -1.038    -0.829    -0.142    -0.168    +0.256    +0.643
            4:the |   +1.645    -0.886    -1.016    -1.453    -0.837    +0.478    +0.608    +0.253
            5:mat |   +0.799    -0.790    -1.010    -1.903    -0.701    +1.025    +0.661    +0.345
            6:and |   -0.242    -2.062    +0.166    +0.817    +1.617    -0.602    +0.084    -0.106
          7:slept |   +2.177    +0.415    -0.962    -0.438    -0.557    -1.239    -0.047    -0.351
    
    W_c (8,4) =
         +0.570    -0.611    +0.244    -0.039
         +0.251    -0.651    -0.635    -0.457
         +0.677    -0.280    +0.128    +0.684
         +0.290    -0.410    -0.107    -0.560
         -0.304    -0.280    -0.680    +0.484
         +0.561    +0.025    +0.191    +0.135
         +0.228    +0.699    +0.110    -0.506
         +0.273    +0.269    +0.501    +0.254
    
    c (8,4) =
            0:the |   +0.138    +0.524    +0.440    +0.056
            1:cat |   +0.070    +1.068    +1.864    +0.089
            2:sat |   +0.495    +0.761    +1.358    -0.641
             3:on |   -0.145    +0.966    +1.816    +0.357
            4:the |   +0.337    +1.191    +1.844    -0.124
            5:mat |   +0.055    +1.866    +1.690    +0.258
            6:and |   -1.145    +0.672    -0.075    +1.241
          7:slept |   -0.065    -1.154    +0.153    -1.190
    
      worked element  c (8,4)[0,0]  =  row 0 of x (8,8)  .  column 0 of W_c (8,4)
          row  0 of x (8,8) = [ 0.436  1.087 -0.58  -2.273 -0.855  0.534  0.092  0.321]
          col  0 of W_c (8,4) = [ 0.57   0.251  0.677  0.29  -0.304  0.561  0.228  0.273]
          (+0.4357)*(+0.5702) + (+1.0866)*(+0.2509) + (-0.5797)*(+0.6769) + (-2.2730)*(+0.2900) + 
          (-0.8548)*(-0.3043) + (+0.5336)*(+0.5613) + (+0.0924)*(+0.2280) + (+0.3210)*(+0.2727)
          = +0.137642   |  from array: +0.137642   |  MATCH: True
    
    STEP 2  rebuild K and V from the latent (never cached themselves)
    ==============================================================================
      c (8,4) (8, 4)   @   W_uk head0 (4,4) (4, 4)   =   K head0 (8,4) (8, 4)
      shape rule: (8 x 4) @ (4 x 4) -> (8 x 4)   inner dim 4 cancels
    ==============================================================================
    
    c (8,4) =
            0:the |   +0.138    +0.524    +0.440    +0.056
            1:cat |   +0.070    +1.068    +1.864    +0.089
            2:sat |   +0.495    +0.761    +1.358    -0.641
             3:on |   -0.145    +0.966    +1.816    +0.357
            4:the |   +0.337    +1.191    +1.844    -0.124
            5:mat |   +0.055    +1.866    +1.690    +0.258
            6:and |   -1.145    +0.672    -0.075    +1.241
          7:slept |   -0.065    -1.154    +0.153    -1.190
    
    W_uk head0 (4,4) =
         -0.306    -0.795    -0.090    +0.222
         +0.406    +0.296    +0.170    +0.181
         -0.462    -0.186    -0.142    -0.794
         +0.784    -0.721    +0.815    +0.507
    
    K head0 (8,4) =
            0:the |   +0.011    -0.076    +0.059    -0.195
            1:cat |   -0.380    -0.149    -0.019    -1.226
            2:sat |   -0.972    +0.042    -0.631    -1.155
             3:on |   -0.123    -0.194    +0.209    -1.118
            4:the |   -0.569    -0.169    -0.192    -1.236
            5:mat |   +0.161    +0.009    +0.281    -0.860
            6:and |   +1.629    +0.229    +1.239    +0.556
          7:slept |   -1.451    +0.539    -1.181    -0.948
    
      worked element  K head0 (8,4)[1,0]  =  row 1 of c (8,4)  .  column 0 of W_uk head0 (4,4)
          row  1 of c (8,4) = [0.07  1.068 1.864 0.089]
          col  0 of W_uk head0 (4,4) = [-0.306  0.406 -0.462  0.784]
          (+0.0696)*(-0.3058) + (+1.0680)*(+0.4056) + (+1.8643)*(-0.4619) + (+0.0886)*(+0.7836)
          = -0.379789   |  from array: -0.379789   |  MATCH: True
    STEP 3  scores, causal mask, softmax
    
    q k^T / sqrt(d_h)   (NaN = masked)  (8, 8)
                  0:the      1:cat      2:sat       3:on      4:the      5:mat      6:and    7:slept
        0:the    -0.051       +nan       +nan       +nan       +nan       +nan       +nan       +nan
        1:cat    -0.264     -1.800       +nan       +nan       +nan       +nan       +nan       +nan
        2:sat    -0.093     -1.022     -1.458       +nan       +nan       +nan       +nan       +nan
         3:on    -0.291     -1.693     -1.576     -1.545       +nan       +nan       +nan       +nan
        4:the    -0.239     -1.661     -1.830     -1.418     -1.772       +nan       +nan       +nan
        5:mat    -0.215     -1.816     -2.366     -1.418     -2.035     -0.793       +nan       +nan
        6:and    -0.131     -0.444     -0.126     -0.503     -0.409     -0.382     -0.129       +nan
      7:slept    -0.087     -0.278     +0.200     -0.424     -0.127     -0.530     -1.039     +0.584
    
    softmax over the unmasked entries  (8, 8)
                  0:the      1:cat      2:sat       3:on      4:the      5:mat      6:and    7:slept
        0:the    +1.000     +0.000     +0.000     +0.000     +0.000     +0.000     +0.000     +0.000
        1:cat    +0.823     +0.177     +0.000     +0.000     +0.000     +0.000     +0.000     +0.000
        2:sat    +0.606     +0.239     +0.155     +0.000     +0.000     +0.000     +0.000     +0.000
         3:on    +0.553     +0.136     +0.153     +0.158     +0.000     +0.000     +0.000     +0.000
        4:the    +0.508     +0.123     +0.104     +0.156     +0.110     +0.000     +0.000     +0.000
        5:mat    +0.427     +0.086     +0.050     +0.128     +0.069     +0.240     +0.000     +0.000
        6:and    +0.168     +0.123     +0.168     +0.116     +0.127     +0.131     +0.168     +0.000
      7:slept    +0.128     +0.106     +0.170     +0.091     +0.123     +0.082     +0.049     +0.250
    
    hand check, query = token 3 'on', attends to 4 positions:
      q            = [0.113 1.185 0.179 2.579]
      raw scores   = [-0.291 -1.693 -1.576 -1.545]
      exp(s - max) = [1.    0.246 0.277 0.285]
      denominator  = 1.808493
      softmax      = [0.553 0.136 0.153 0.158]
      array row    = [0.553 0.136 0.153 0.158]
      softmax row 3                                              max|diff| = 1.110e-16   MATCH: True
      row sums     = [1. 1. 1. 1. 1. 1. 1. 1.]   (all 1)
    STEP 4  concatenate heads, gate, project
    
    heads concatenated  (8, 8)
                   h0d0       h0d1       h0d2       h0d3       h1d0       h1d1       h1d2       h1d3
        0:the   +0.0050    +0.2369    +0.4749    -0.4853    -0.1908    +0.4247    +0.2952    -0.1233
        1:cat   -0.0104    +0.3700    +0.5414    -0.6320    +0.1431    +1.1545    +0.0963    -0.4165
        2:sat   +0.0155    +0.4728    +0.5751    -0.7426    +0.2417    +1.1950    +0.0700    -0.4886
         3:on   -0.0072    +0.5168    +0.5841    -0.7794    +0.2278    +1.1899    +0.0407    -0.4569
        4:the   -0.0152    +0.5668    +0.6302    -0.8462    +0.2429    +1.3390    +0.0683    -0.5106
        5:mat   -0.0101    +0.6016    +0.8453    -1.0188    +0.0082    +1.4159    +0.2929    -0.5357
        6:and   -0.0356    +0.5901    +0.7871    -0.9785    -0.0233    +1.0252    +0.2093    -0.3842
      7:slept   +0.0765    +0.4648    +0.2577    -0.5685    -0.2440    +1.0366    +0.2905    -0.3280
    
    sigma(W_g x)  (8, 8)
                   h0d0       h0d1       h0d2       h0d3       h1d0       h1d1       h1d2       h1d3
        0:the   +0.2090    +0.2213    +0.1755    +0.7123    +0.6322    +0.6869    +0.2557    +0.6770
        1:cat   +0.3882    +0.2962    +0.3771    +0.3974    +0.5264    +0.7904    +0.4802    +0.8012
        2:sat   +0.2568    +0.2010    +0.1459    +0.5452    +0.7282    +0.7832    +0.3935    +0.7915
         3:on   +0.4769    +0.3638    +0.4890    +0.3782    +0.4290    +0.7450    +0.5841    +0.7635
        4:the   +0.3385    +0.2924    +0.2899    +0.4499    +0.6330    +0.7435    +0.4642    +0.8439
        5:mat   +0.2987    +0.2715    +0.3535    +0.4394    +0.5377    +0.6654    +0.4398    +0.8320
        6:and   +0.7226    +0.7089    +0.8781    +0.3567    +0.1333    +0.3243    +0.7286    +0.3802
      7:slept   +0.4001    +0.4047    +0.1252    +0.7018    +0.6491    +0.8270    +0.4456    +0.6930
    
    ==============================================================================
      gated (8,8) (8, 8)   @   W_o (8,8) (8, 8)   =   MLA output (8,8) (8, 8)
      shape rule: (8 x 8) @ (8 x 8) -> (8 x 8)   inner dim 8 cancels
    ==============================================================================
    
    gated (8,8) =
            0:the |   +0.001    +0.052    +0.083    -0.346    -0.121    +0.292    +0.075    -0.083
            1:cat |   -0.004    +0.110    +0.204    -0.251    +0.075    +0.913    +0.046    -0.334
            2:sat |   +0.004    +0.095    +0.084    -0.405    +0.176    +0.936    +0.028    -0.387
             3:on |   -0.003    +0.188    +0.286    -0.295    +0.098    +0.886    +0.024    -0.349
            4:the |   -0.005    +0.166    +0.183    -0.381    +0.154    +0.996    +0.032    -0.431
            5:mat |   -0.003    +0.163    +0.299    -0.448    +0.004    +0.942    +0.129    -0.446
            6:and |   -0.026    +0.418    +0.691    -0.349    -0.003    +0.332    +0.152    -0.146
          7:slept |   +0.031    +0.188    +0.032    -0.399    -0.158    +0.857    +0.129    -0.227
    
    W_o (8,8) =
         +0.260    -0.170    +0.377    -0.171    +0.207    -0.125    -0.527    -0.598
         -0.327    +0.507    +0.000    +0.361    -0.031    +0.341    +0.473    +0.589
         +0.202    -0.120    -0.088    -0.291    +0.538    +0.418    +0.321    -0.297
         -0.567    +0.063    -0.356    +0.211    -0.532    -0.140    +0.249    -0.093
         +0.146    -0.231    -0.071    +0.495    +0.474    +0.346    -0.157    +0.285
         +0.073    -0.397    +0.557    -0.280    +0.028    +0.545    +0.215    +0.538
         +0.524    -0.065    -0.058    +0.142    +0.376    -0.291    +0.094    -0.561
         -0.389    -0.525    -0.290    -0.360    +0.407    -0.202    -0.484    -0.441
    
    MLA output (8,8) =
            0:the |   +0.272    -0.054    +0.307    -0.179    +0.172    +0.213    +0.094    +0.155
            1:cat |   +0.378    -0.192    +0.667    -0.164    +0.182    +0.735    +0.407    +0.664
            2:sat |   +0.475    -0.199    +0.758    -0.109    +0.221    +0.764    +0.333    +0.776
             3:on |   +0.389    -0.150    +0.665    -0.148    +0.242    +0.805    +0.456    +0.701
            4:the |   +0.476    -0.168    +0.785    -0.117    +0.232    +0.860    +0.447    +0.834
            5:mat |   +0.570    -0.130    +0.778    -0.206    +0.288    +0.810    +0.493    +0.684
            6:and |   +0.355    +0.047    +0.273    -0.140    +0.545    +0.649    +0.503    +0.246
          7:slept |   +0.375    -0.132    +0.698    -0.250    +0.135    +0.550    +0.316    +0.564
    
      worked element  MLA output (8,8)[0,0]  =  row 0 of gated (8,8)  .  column 0 of W_o (8,8)
          row  0 of gated (8,8) = [ 0.001  0.052  0.083 -0.346 -0.121  0.292  0.075 -0.084]
          col  0 of W_o (8,8) = [ 0.26  -0.327  0.202 -0.567  0.146  0.073  0.524 -0.389]
          (+0.0010)*(+0.2603) + (+0.0524)*(-0.3266) + (+0.0833)*(+0.2023) + (-0.3457)*(-0.5669) + 
          (-0.1206)*(+0.1461) + (+0.2917)*(+0.0726) + (+0.0755)*(+0.5244) + (-0.0835)*(-0.3891)
          = +0.271611   |  from array: +0.271611   |  MATCH: True
    
    ==============================================================================
      WHAT EACH MECHANISM STORES BETWEEN TOKENS
    ==============================================================================
    mechanism                       per token     at T=8       at T=1e6
    --------------------------------------------------------------------
    KDA recurrent state             0 (fixed)         32             32
    MLA latent cache c                      4         32        4000000
    full K,V (what MLA avoids)             16        128       16000000
    --------------------------------------------------------------------
    MLA compression ratio: 4.0x   (K3: 2*128*... -> d_c, far larger)
    And only 1 layer in 4 pays even that. The other 3 are constant in T.

    11. Assembling the full stack

    Everything above was a component in isolation. Now the wiring, exactly as in the architecture diagram:

    group_sum = 0
    for each sublayer i in block n:
          h  = AttnRes_read( w_i , [ b0 , b1 ... b_{n-1} ] + [ group_sum if i >= 2 ] )
          y  = Module( RMSNorm(h) )                    # KDA / MLA / MoE
          group_sum = group_sum + y                    # the "+" node in the diagram
    b_n = group_sum                                    # block summary
    

    Three things this makes explicit:

    Pre-norm. RMSNorm sits inside the sublayer, between the AttnRes read and the module. The thing added to the group sum is the raw module output. This is x + Sublayer(RMSNorm(x)) restructured so that the x is itself chosen by attention.

    The + node is the group sum, not a classical residual. In a standard Transformer the residual and the layer input are the same object. Here they are different: the group sum $b_n^i$ accumulates, while the layer input is a fresh attention read over $[b_0, \dots, b_{n-1}, b_n^i]$. The read can recover the classical residual by putting all its mass on $b_n^i$ β€” and Β§12 shows how much mass it actually puts there.

    Layer 1 is dense. Table 1 of the report lists Number of Dense Layers = 1: the FFN of layer 1 is an ordinary SiTU-GLU feed-forward at full width; layers 2–93 are LatentMoE. A common choice β€” the first layer's token distribution is close to the raw embedding distribution and routes poorly.

    #  blk  layer  sublayer  module | AttnRes sources            |   ||y|| ||group sum||
    ----------------------------------------------------------------------------------------------------
      1    1      1      attn     KDA | b0                         |   4.880         4.880
      2    1      1       ffn   Dense | b0, b1^1                   |   2.184         5.515
      3    1      2      attn     KDA | b0, b1^2                   |   4.324         6.023
      4    1      2       ffn     MoE | b0, b1^3                   |   7.193         9.407
      5    1      3      attn     KDA | b0, b1^4                   |   4.787        10.591
      6    1      3       ffn     MoE | b0, b1^5                   |   8.403        13.230
      7    1      4      attn     MLA | b0, b1^6                   |   6.202        12.982
      8    1      4       ffn     MoE | b0, b1^7                   |   7.594        13.879
      9    2      5      attn     KDA | b0, b1                     |   4.358         4.358
     10    2      5       ffn     MoE | b0, b1, b2^1               |   6.782         8.859
     11    2      6      attn     KDA | b0, b1, b2^2               |   4.584         9.680
     12    2      6       ffn     MoE | b0, b1, b2^3               |   6.306        11.635
     13    2      7      attn     KDA | b0, b1, b2^4               |   3.640        11.537
     14    2      7       ffn     MoE | b0, b1, b2^5               |   6.911        12.172
     15    2      8      attn     MLA | b0, b1, b2^6               |   2.605        12.431
     16    2      8       ffn     MoE | b0, b1, b2^7               |   5.773        14.640
    ----------------------------------------------------------------------------------------------------
    logits (8, 500)   (weight-tied to the embedding table, W_out = E^T)

    11.1 What the depth attention actually reads

    $\alpha$ is a real distribution over sources, and it is worth looking at. Two patterns to check for:

    • If a sublayer puts nearly all its mass on the current group sum, it has reproduced a classical residual connection.
    • If it puts mass on b0, it is reaching past every intervening layer straight back to the token embedding β€” the thing a summed residual stream cannot do selectively.
    read this as: a classical residual stream would be a solid red line at 1.0.
    anything below that is the model choosing to look further back than one step.

    12. Final Block AttnRes β†’ RMSNorm β†’ logits

    The last AttnRes read is different from all the others: its sources are the completed block summaries $[b_0, b_1, \dots, b_N]$, with no partial sum. This is the "Final Block AttnRes" node in the architecture diagram, and it is where the depth attention gets to weigh whole blocks against each other.

    Then a final RMSNorm and a linear projection to the vocabulary. As in the Transformer notebook, the output matrix is tied to the embedding table, $W_{out} = E^\top$.

    sources for the final read:
       0: b0 (embedding)
       1: b1 (block 1 summary)
       2: b2 (block 2 summary)
    
    final alpha  (8, 3)
                     b0         b1         b2
        0:the   +0.9347    +0.0373    +0.0280
        1:cat   +0.0444    +0.0527    +0.9029
        2:sat   +0.0687    +0.4179    +0.5134
         3:on   +0.2503    +0.2367    +0.5129
        4:the   +0.9458    +0.0222    +0.0320
        5:mat   +0.1364    +0.3004    +0.5632
        6:and   +0.0439    +0.0443    +0.9118
      7:slept   +0.4591    +0.0291    +0.5118
    
    aggregated h  (8, 8)
                     d0         d1         d2         d3         d4         d5         d6         d7
        0:the   -0.1556    +0.0535    -0.1102    +0.6490    -0.9138    +0.2654    +0.3940    -0.6652
        1:cat   -1.6809    -0.5936    -1.1608    -1.3958    -0.6903    -1.5513    +1.8863    -0.6001
        2:sat   +1.5543    -0.2203    -0.4328    +0.7340    +0.3178    -0.6682    +0.8691    -1.2611
         3:on   +0.7227    +0.3734    +0.3141    -0.3519    -0.1555    -2.4909    +1.4265    +0.9392
        4:the   -0.2163    +0.1728    +0.0566    +0.5643    -0.9203    +0.4821    +0.4381    -0.6639
        5:mat   +0.5086    -1.4015    -2.2879    +0.9578    +0.7752    -2.7580    -0.9333    +1.2334
        6:and   +0.2057    -2.2128    -0.1768    -2.0553    -0.7660    -0.1155    +1.3284    +1.1742
      7:slept   -0.3310    -0.2098    -0.7874    -0.0763    -0.9056    -0.6170    +1.0388    +0.3816
    
    RMSNorm(h)  (8, 8)
                     d0         d1         d2         d3         d4         d5         d6         d7
        0:the   -0.3140    +0.1081    -0.2224    +1.3100    -1.8444    +0.5358    +0.7952    -1.3427
        1:cat   -1.3049    -0.4608    -0.9012    -1.0835    -0.5359    -1.2043    +1.4644    -0.4658
        2:sat   +1.7824    -0.2526    -0.4963    +0.8417    +0.3645    -0.7662    +0.9966    -1.4461
         3:on   +0.6454    +0.3334    +0.2805    -0.3143    -0.1389    -2.2246    +1.2740    +0.8388
        4:the   -0.4212    +0.3364    +0.1102    +1.0987    -1.7918    +0.9387    +0.8530    -1.2926
        5:mat   +0.3303    -0.9100    -1.4856    +0.6219    +0.5033    -1.7908    -0.6060    +0.8009
        6:and   +0.1617    -1.7400    -0.1390    -1.6162    -0.6023    -0.0908    +1.0446    +0.9233
      7:slept   -0.5230    -0.3314    -1.2441    -0.1205    -1.4308    -0.9747    +1.6412    +0.6029
    
    logits = RMSNorm(h) @ E^T :  (8,8) @ (8,500) -> (8, 500)
    
    hand check logits[7, 83]  (score for the word 'the' after 'slept'):
      h    = [-0.523 -0.331 -1.244 -0.12  -1.431 -0.975  1.641  0.603]
      E[83] = [-0.252  0.038 -0.09   0.532 -0.937  0.386  0.441 -0.733]
      (-0.5230)*(-0.2520) + (-0.3314)*(+0.0376) + (-1.2441)*(-0.0901) + (-0.1205)*(+0.5317) + 
          (-1.4308)*(-0.9373) + (-0.9747)*(+0.3859) + (+1.6412)*(+0.4406) + (+0.6029)*(-0.7331)
      = +1.413344   array = +1.413344
      logit                                                      max|diff| = 0.000e+00   MATCH: True
    
    next-token distribution after the full sentence 'the cat sat on the mat and slept':
      1. id 499 <id 499>  p = 0.03542  #####################
      2. id 238 <id 238>  p = 0.01878  ###########
      3. id 394 <id 394>  p = 0.01525  #########
      4. id 360 <id 360>  p = 0.01420  ########
      5. id 171 <id 171>  p = 0.01146  ######
      6. id 435 <id 435>  p = 0.01138  ######
      7. id 107 <id 107>  p = 0.00908  #####
      8. id  40 <id 40>   p = 0.00852  #####
    
      entropy = 5.7333 nats   (uniform over 500 would be 6.2146)
      Random weights: the distribution is near-uniform and the argmax is meaningless.

    13. Autoregressive decoding β€” and the point of the whole architecture

    Everything so far ran on all 8 tokens at once. Generation runs one token at a time, and this is where the architecture pays off.

    What each mechanism has to do at step $t$

    softmax attention KDA Gated MLA
    what is stored $K, V$ for all $t$ tokens $S \in \mathbb{R}^{d_k\times d_v}$ $c$ for all $t$ tokens
    cost of step $t$ $O(t \cdot d)$ $O(d_k d_v)$ β€” independent of $t$ $O(t \cdot d_c)$
    memory growth linear in $t$ none linear, but $d_c$ per token

    For KDA the incremental step is literally equation (1) applied once. There is no approximation: the state after the recurrent step is bit-for-bit the state the chunkwise kernel would produce.

    One detail that is easy to forget: the ShortConv needs the previous token's projection, so each KDA layer also caches the previous sublayer input $x_{t-1}$ (4 or 8 numbers β€” negligible).

    The cell below implements incremental decoding and then verifies it against the parallel forward pass of Β§11.

    running the same 8 tokens ONE AT A TIME through the incremental path...
    
         token     max |parallel - incremental|
    ----------------------------------------------
    0:the                             3.331e-15
    1:cat                             1.607e-14
    2:sat                             7.883e-14
    3:on                              1.554e-14
    4:the                             1.255e-14
    5:mat                             3.642e-14
    6:and                             6.928e-14
    7:slept                           1.417e-13
    ----------------------------------------------
      ALL logits: parallel vs incremental                        max|diff| = 1.417e-13   MATCH: True
    
    The recurrent state carries EXACTLY the information the chunkwise kernel would
    have computed. Nothing about decoding is an approximation of training.
    
      layer 1 KDA : state = 2 heads x 4x4 =  32 numbers  (independent of T)
      layer 2 KDA : state = 2 heads x 4x4 =  32 numbers  (independent of T)
      layer 3 KDA : state = 2 heads x 4x4 =  32 numbers  (independent of T)
      layer 4 MLA : cache = 8 tokens x d_c=4 =  32 numbers  (grows with T)
      layer 5 KDA : state = 2 heads x 4x4 =  32 numbers  (independent of T)
      layer 6 KDA : state = 2 heads x 4x4 =  32 numbers  (independent of T)
      layer 7 KDA : state = 2 heads x 4x4 =  32 numbers  (independent of T)
      layer 8 MLA : cache = 8 tokens x d_c=4 =  32 numbers  (grows with T)
    greedy continuation (untrained -- the words are noise, the mechanism is not)
    
      prompt    : the cat sat on the mat and slept
      generated : <499> the the the the the
    
    with temperature sampling, same model, three different seeds:
      seed 0: <348> <152> <26> <14> <398> <451>
      seed 1: <279> <472> <80> <475> <162> <199>
      seed 2: <152> <135> <410> <62> <255> <373>
    
    cost accounting for the last generated token:
      KDA layers (6): each did ONE 4x4 state
          update -- the same work as at token 1, and the same as it would do at token 1e6
      MLA layers (2): each attended over 14 cached latents -- this is the only term that grows

    14. Summary β€” every shape, and what changes at 2.8T

    ====================================================================================
      SHAPES, END TO END
    ====================================================================================
      token ids                                    (8,)   8 integers
      embedding X0 = E[ids]                      (8, 8)   no PE added
    
    --- per sublayer ---
      AttnRes sources                    n_src x (8, 8)   n_src <= 1 + N_blocks + 1
      AttnRes alpha                          (8, n_src)   softmax over DEPTH
      sublayer input h                           (8, 8)   weighted sum of sources
      RMSNorm(h)                                 (8, 8)   pre-norm
    
    --- KDA ---
      Q, K per head                              (8, 4)   L2-normalised
      V per head                                 (8, 4)   not normalised
      alpha (decay)                              (8, 4)   channel-wise, in (e^-5, 1)
      beta                                         (8,)   scalar per token
      state S                                    (4, 4)   CONSTANT in T
      Gamma per chunk                            (4, 4)   cumprod of alpha
      A per chunk                                (4, 4)   Tril, diagonal kept
    
    --- Gated MLA ---
      latent c                                   (8, 4)   the entire KV cache
      K, V rebuilt per head                      (8, 4)   never cached
      attention probs                            (8, 8)   causal, NoPE
    
    --- LatentMoE ---
      router s                                   (8, 8)   sigmoid, not softmax
      top-k indices                              (8, 2)   from s + b
      cutoff alpha_i                               (8,)   the (k+1)-th entry
      latent z                                   (8, 4)   ell = d/2
      routed aggregate u                         (8, 4)   then RMSNorm
    
    --- output ---
      block summaries                        2 x (8, 8)   b_1 ... b_N
      logits                                   (8, 500)   W_out = E^T
    
    ====================================================================================
      PARAMETER COUNT OF THIS TOY
    ====================================================================================
      attention layers      3132
      FFN / MoE layers     12472   (80% of the backbone)
      AttnRes queries        136   (17 vectors of 8)
      embedding (tied)      4000
      TOTAL                19740
    
      activated per token: only 2 of 8 routed experts run in each MoE layer.

    What changes at the real scale

    Nothing structural. Every formula above is the formula in the report. These are the numbers that grow, and the systems work each one forces:

    component here Kimi K3 what it forces
    KDA state per head $4\times4$ $d_k \times d_v$, fixed the state is cheap to transfer β†’ KDA Context Parallelism, and prefix-cache checkpoints
    chunk serialism 2 chunks millions of tokens FlashKDA: a CUTLASS chunkwise kernel that overlaps intra-chunk matmul with cross-chunk state propagation
    cross-device recurrence n/a $P$ ranks KCP: because $S_t = M_t S_{t-1} + \beta_t k_t v_t^\top$ is affine, each segment decomposes into a cumulative transition $M$ and a from-zero state $\tilde S$, both locally computable, then combined by prefix scan with one fixed-size all-gather
    routed experts 8 896 MoonEP: perfect load balance with at most $E/R$ redundant experts per rank β€” proved, and proved tight
    active per token 2 16 the latent $\ell = d/2$ keeps all-to-all traffic flat as $k$ grows
    AttnRes sources ≀ 3 ≀ 9 cache-based pipeline communication; only newly generated blocks cross PP stages
    layers 8 93 PP with virtual stages + ZeRO-1 DP + Pipeline ZeRO-2 gradient sharding to CPU
    context 8 1M 8K→64K in pre-training, 256K→1M in cooldown; NoPE means nothing to rescale
    precision float64 MXFP4 weights / MXFP8 activations QAT through all of SFT and RL, so rollout and training share one quantisation scheme

    What is deliberately left out of this notebook

    • MoonViT-V2, the from-scratch vision tower. Its tokens would simply appear as extra rows in X0 β€” the backbone is unchanged, which is the entire point of "native multimodal".
    • Multi-token prediction / EAGLE-3 draft model. One detail is worth stating anyway: the draft fuses low-, mid- and high-level features through $W_{E3}$ initialised as $[\,0\;\;0\;\;I\,]$, so at step zero the fused representation is the high-level feature the MTP layer was pre-trained on. And it optimises $-\log\sum_x \min(p(x), q(x))$ β€” the acceptance rate itself β€” rather than KL, since minimising KL does not maximise acceptance for a capacity-limited draft.
    • Training: Muon (per-head), the RL pipeline, multi-teacher on-policy distillation.

    APPENDIX β€” every number, by hand

    Two traces, in the style of the Transformer notebook's appendix. Nothing here calls a helper that hides arithmetic: every sum is written out.

    TRACE A β€” token 4 ("the", the second occurrence) through block 1, layer 1

    A.1 The embedding, and the problem NoPE creates

    token 4 = 'the', id = 83
      X0[4] = E[83] = [-0.252  0.038 -0.09   0.532 -0.937  0.386  0.441 -0.733]
      X0[0]  = E[83] = [-0.252  0.038 -0.09   0.532 -0.937  0.386  0.441 -0.733]
      equal? True
    
      No positional encoding is added. At the input these two tokens are
      indistinguishable. A2-A6 show where they diverge.

    A.2 The AttnRes read for sublayer 1

    Block 1, sublayer 1 is the first sublayer of the first block, so its only source is $b_0$ β€” the embedding. With one source the softmax is degenerate: $\alpha = 1$, and $h = b_0$.

    This is worth seeing explicitly, because it shows the mechanism reduces correctly to "just take the input" when there is nothing to choose between.

    sources        : ['b0']
      alpha[4]      : [1.]   (one source -> softmax of a single logit = 1)
      h[4]          : [-0.252  0.038 -0.09   0.532 -0.937  0.386  0.441 -0.733]
      h == embedding                                             max|diff| = 0.000e+00   MATCH: True
    
      RMSNorm, written out:
        x        = [-0.252  0.038 -0.09   0.532 -0.937  0.386  0.441 -0.733]
        sum x^2  = -0.2520^2 + 0.0376^2 + -0.0901^2 + 0.5317^2 + -0.9373^2 + 0.3859^2 + 0.4406^2 + -0.7331^2
                 = 2.11464854
        mean     = 0.26433107
        rms      = 0.51413234
        x / rms  = [-0.49   0.073 -0.175  1.034 -1.823  0.751  0.857 -1.426]
      RMSNorm(h)                                                 max|diff| = 0.000e+00   MATCH: True

    A.3 KDA head 0 β€” the projections for this token

    q_proj[4] = X[4] @ Wq   -- one dot product per output channel
      channel k0: (-0.4902)*(+0.6712) + (+0.0732)*(+0.1125) + (-0.1753)*(+0.4054) + (+1.0343)*(-0.3265) + 
          (-1.8230)*(-0.2550) + (+0.7506)*(-0.0963) + (+0.8569)*(-0.1597) + (-1.4258)*(-0.6700)
          = +0.481397   array = +0.481397
      channel k1: (-0.4902)*(-0.6141) + (+0.0732)*(-0.3356) + (-0.1753)*(+0.1125) + (+1.0343)*(-0.4001) + 
          (-1.8230)*(-0.3414) + (+0.7506)*(+0.4080) + (+0.8569)*(+0.3194) + (-1.4258)*(+0.0811)
          = +0.929692   array = +0.929692
      channel k2: (-0.4902)*(-0.0312) + (+0.0732)*(-0.7061) + (-0.1753)*(-0.1986) + (+1.0343)*(+0.3615) + 
          (-1.8230)*(-0.3966) + (+0.7506)*(+0.4383) + (+0.8569)*(-0.5403) + (-1.4258)*(+0.2122)
          = +0.658812   array = +0.658812
      channel k3: (-0.4902)*(+0.2992) + (+0.0732)*(+0.5459) + (-0.1753)*(+0.6857) + (+1.0343)*(+0.0334) + 
          (-1.8230)*(+0.3816) + (+0.7506)*(+0.0529) + (+0.8569)*(-0.2665) + (-1.4258)*(-0.1980)
          = -0.794459   array = -0.794459
    
    ShortConv (needs token 3 = 'on'):
      c0 = [0.441 0.751 0.469 0.389]   c1 = [0.695 0.437 0.698 0.696]   (per-channel)
      k0: +0.4406*+0.4814 + +0.6946*+0.9251 = +0.854700   array = +0.854700
      k1: +0.7510*+0.9297 + +0.4369*+0.8297 = +1.060729   array = +1.060729
      k2: +0.4690*+0.6588 + +0.6979*-1.2076 = -0.533787   array = -0.533787
      k3: +0.3891*-0.7945 + +0.6955*+1.0938 = +0.451653   array = +0.451653
    
      <-- HERE is the first place the two copies of 'the' differ: position 0 had
          no predecessor, position 4 convolves with 'on'.
          q_conv[0] = [ 0.212  0.698  0.309 -0.309]
          q_conv[4] = [ 0.855  1.061 -0.534  0.452]
    
    Swish then L2Norm:
      swish   = [ 0.6    0.788 -0.197  0.276]
      ||.||   = sqrt(0.5996^2 + 0.7879^2 + -0.1973^2 + 0.2760^2) = 1.046654
      q       = [ 0.573  0.753 -0.189  0.264]
      Q                                                          max|diff| = 7.193e-10   MATCH: True

    A.4 The decay and write gates for this token

    W_down x      = [ 0.054 -1.276]   (rank-2 bottleneck)
      W_up (.)      = [ 0.932  0.731  0.322 -0.76 ]
      + b_alpha     = [-1.64  -2.39  -2.929 -2.164]
      z             = [-0.708 -1.659 -2.608 -2.923]
    
      k0: sigma(e^0 * -0.7077) = 0.330114  ->  g = -5*0.330114 = -1.650570  ->  alpha = e^-1.6506 = 0.191941
      k1: sigma(e^0 * -1.6592) = 0.159873  ->  g = -5*0.159873 = -0.799363  ->  alpha = e^-0.7994 = 0.449615
      k2: sigma(e^0 * -2.6075) = 0.068656  ->  g = -5*0.068656 = -0.343281  ->  alpha = e^-0.3433 = 0.709439
      k3: sigma(e^0 * -2.9232) = 0.051020  ->  g = -5*0.051020 = -0.255098  ->  alpha = e^-0.2551 = 0.774841
      alpha                                                      max|diff| = 2.776e-17   MATCH: True
    
      beta: W_b.x = +0.150727  ->  sigmoid = 1/(1+e^-0.150727) = 0.537611
      beta                                                       max|diff| = 0.000e+00   MATCH: True

    A.5 The state update at $t = 4$, entry by entry

    (1) DECAY  Diag(alpha) @ S_{t-1}   -- row j of S scaled by alpha_j
      S_3  (4, 4)
                     v0         v1         v2         v3
           k0   +0.0010    -0.0246    -0.0006    -0.0577
           k1   -0.0065    -0.0344    +0.0066    +0.0026
           k2   -0.2142    +0.0549    -0.1868    +0.3792
           k3   -0.0280    -0.0483    -0.0041    -0.0137
    
      row k0: 0.191941 * [ 0.001 -0.025 -0.001 -0.058] = [ 0.    -0.005 -0.    -0.011]
      row k1: 0.449615 * [-0.007 -0.034  0.007  0.003] = [-0.003 -0.015  0.003  0.001]
      row k2: 0.709439 * [-0.214  0.055 -0.187  0.379] = [-0.152  0.039 -0.133  0.269]
      row k3: 0.774841 * [-0.028 -0.048 -0.004 -0.014] = [-0.022 -0.037 -0.003 -0.011]
    
    (2) ERASE  P = I - beta k k^T,  beta = 0.537611,  k = [-0.196  0.077 -0.195  0.958]
      P  (4, 4)
                     k0         k1         k2         k3
           k0   +0.9794    +0.0081    -0.0205    +0.1008
           k1   +0.0081    +0.9968    +0.0080    -0.0395
           k2   -0.0205    +0.0080    +0.9796    +0.1003
           k3   +0.1008    -0.0395    +0.1003    +0.5065
      hand check P[0,1] = 0 - 0.537611*-0.195643*+0.076725 = +0.00806999   array = +0.00806999
    
      P @ decayed  (4, 4)
                     v0         v1         v2         v3
           k0   +0.0011    -0.0093    +0.0023    -0.0174
           k1   -0.0033    -0.0137    +0.0020    +0.0037
           k2   -0.1511    +0.0344    -0.1301    +0.2627
           k3   -0.0261    -0.0149    -0.0150    +0.0205
      hand check [0,0] = (+0.9794)*(+0.0002) + (+0.0081)*(-0.0029) + (-0.0205)*(-0.1519) + (+0.1008)*(-0.0217)
           = +0.00108279   array = +0.00108279
    
    (3) WRITE  + beta * k v^T,  v = [-0.278 -0.101  0.012  1.048]
      beta k v^T  (4, 4)
                     v0         v1         v2         v3
           k0   +0.0292    +0.0107    -0.0013    -0.1102
           k1   -0.0115    -0.0042    +0.0005    +0.0432
           k2   +0.0291    +0.0106    -0.0013    -0.1097
           k3   -0.1431    -0.0522    +0.0062    +0.5396
      hand check [1,2] = 0.537611 * +0.076725 * +0.012024 = +0.00049597   array = +0.00049597
    
      ==> S_4  (4, 4)
                     v0         v1         v2         v3
           k0   +0.0303    +0.0013    +0.0010    -0.1276
           k1   -0.0147    -0.0178    +0.0025    +0.0469
           k2   -0.1220    +0.0450    -0.1314    +0.1530
           k3   -0.1692    -0.0671    -0.0088    +0.5601
      S_4                                                        max|diff| = 0.000e+00   MATCH: True
    
    (4) READ  o = S^T q,  q = [ 0.573  0.753 -0.189  0.264]
      v0: (+0.0303)*(+0.5729) + (-0.0147)*(+0.7528) + (-0.1220)*(-0.1885) + (-0.1692)*(+0.2637)
          = -0.01536421   array = -0.01536421
      v1: (+0.0013)*(+0.5729) + (-0.0178)*(+0.7528) + (+0.0450)*(-0.1885) + (-0.0671)*(+0.2637)
          = -0.03882167   array = -0.03882167
      v2: (+0.0010)*(+0.5729) + (+0.0025)*(+0.7528) + (-0.1314)*(-0.1885) + (-0.0088)*(+0.2637)
          = +0.02493985   array = +0.02493985
      v3: (-0.1276)*(+0.5729) + (+0.0469)*(+0.7528) + (+0.1530)*(-0.1885) + (+0.5601)*(+0.2637)
          = +0.08099935   array = +0.08099935
    
      o[0] = [-0.011 -0.025  0.005  0.061]
      o[4] = [-0.015 -0.039  0.025  0.081]
      the two copies of 'the' now produce different outputs: True

    TRACE B β€” token 7 ("slept") β†’ the next-token probability

    B.1 Which experts this token was routed to, and why

    first MoE sublayer (sublayer 4, layer 2)
    
      router scores for token 7 ('slept'):
        E0: s = 0.309197
        E1: s = 0.288658
        E2: s = 0.334596
        E3: s = 0.728520  <== SELECTED
        E4: s = 0.847728  <== SELECTED
        E5: s = 0.596475  (cutoff)
        E6: s = 0.228453
        E7: s = 0.110529
    
      cutoff alpha_i (the (k+1)-th biased score) = 0.596475
    
      p normalisation over the 2 selected experts only:
        s[E4] + s[E3] = 0.847728 + 0.728520 = 1.576247
        p[E4] = 0.847728 / 1.576247 = 0.537814
        p[E3] = 0.728520 / 1.576247 = 0.462186
        sum p  = 1.000000
    
      note the other 6 experts contribute exactly 0 -- that is the sparsity.

    B.2 From the block summaries to a logit

    block summaries at token 7:
      b1 = [-1.419 -0.009  0.489 -3.448  0.681 -0.426  2.581 -0.114]   ||.|| = 4.63272
      b2 = [-0.833 -0.475 -1.156  0.216 -1.616 -1.157  1.748  0.619]   ||.|| = 3.11290
      b0 = [ 0.297  0.073 -0.457 -0.188 -0.215 -0.027  0.151  0.148]   ||.|| = 0.65528  (embedding)
    
      w_final          = [-0.05   0.326 -0.548 -0.27  -1.673  0.083  0.19  -0.308]
      RMSNorm'd keys   :
        b0: [ 1.283  0.317 -1.973 -0.812 -0.927 -0.118  0.65   0.638]
        b1: [-0.866 -0.006  0.298 -2.105  0.416 -0.26   1.576 -0.069]
        b2: [-0.757 -0.432 -1.051  0.196 -1.468 -1.051  1.588  0.563]
      logits           = [2.808 0.05  2.917]
      exp(l - max)     = [0.897 0.057 1.   ]
      sum              = 1.953889
      alpha            = [0.459 0.029 0.512]
      final alpha                                                max|diff| = 0.000e+00   MATCH: True
    
      h = alpha . V    = [-0.331 -0.21  -0.787 -0.076 -0.906 -0.617  1.039  0.382]
      h_final                                                    max|diff| = 0.000e+00   MATCH: True
      RMSNorm(h)       = [-0.523 -0.331 -1.244 -0.121 -1.431 -0.975  1.641  0.603]
    
      logit for token id 68 ('cat'):
        (-0.5230)*(-0.8739) + (-0.3314)*(-0.2817) + (-1.2441)*(+0.2040) + (-0.1205)*(-0.0718) + 
          (-1.4308)*(+0.1312) + (-0.9747)*(-0.2135) + (+1.6412)*(+0.0306) + (+0.6029)*(-0.0550)
        = +0.34271389   array = +0.34271389
      logit                                                      max|diff| = 0.000e+00   MATCH: True

    B.3 The softmax over 500 tokens β€” where the denominator comes from

    max logit        = +3.376301 at id 499 ('?')
      min logit        = -2.804575
      subtracting the max keeps exp() in range: exp(-6.1809) = 2.069e-03
      Z = sum exp(l - max) over all 500 tokens = 28.231461
      log Z + max      = 6.716738   (the log-partition function)
    
         id word           logit   exp(l-max)          p
      --------------------------------------------------
        499 -            3.37630     1.000000   0.035421
        238 -            2.74195     0.530279   0.018783
        394 -            2.53375     0.430610   0.015253
        360 -            2.46243     0.400968   0.014203
        171 -            2.24787     0.323541   0.011460
        435 -            2.24099     0.321321   0.011382
      --------------------------------------------------
      sum of ALL 500 probabilities = 1.0000000000
      softmax                                                    max|diff| = 0.000e+00   MATCH: True
    
      cross-entropy if the true next token were id 68 ('cat'):
        loss = -log p = -log(0.00170528) = 6.374024 nats
        equivalently  = logZ + max - logit = 3.340437 + 3.376301 - 0.342714 = 6.374024
      two forms of the loss                                      max|diff| = 8.882e-16   MATCH: True
    
        a uniform model would give 6.214608 nats. Untrained, we are at 6.3740.

    Every operation, counted

    For one forward pass over 8 tokens through 8 layers (16 sublayers):

    operation count note
    AttnRes reads 17 16 sublayers + 1 final block read
    KDA layers 6 3 per block Γ— 2 blocks
    KDA state updates 6 Γ— 2 heads Γ— 8 tokens = 96 each is a $4\times4$ matrix update
    chunkwise UT inversions 6 Γ— 2 Γ— 2 chunks = 24 one $4\times4$ solve each
    MLA layers 2 one per block
    softmax attention matrices 2 Γ— 2 heads = 4 the only $O(T^2)$ objects in the model
    MoE sublayers 7 layer 1 is dense
    expert forwards 7 Γ— 8 tokens Γ— 2 = 112 out of 7 Γ— 8 Γ— 8 = 448 possible
    experts skipped 336 75% of the width, never touched

    That last pair of rows is the whole argument for sparsity: the model has the parameters of 8 experts per layer and pays for 2.

    At K3's real scale the ratio is 16 of 896 β€” 98.2% of each MoE layer is skipped for every token.

    References

    1. Kimi K3: Open Frontier Intelligence β€” Kimi Team, Moonshot AI, 2026 (technical report)
    2. Kimi Linear: An Expressive, Efficient Attention Architecture β€” Moonshot AI, 2025
    3. Gated Delta Networks: Improving Mamba2 with Delta Rule β€” Yang et al., 2024
    4. DeepSeek-V2: A Strong, Economical, and Efficient Mixture-of-Experts Language Model (Multi-head Latent Attention) β€” DeepSeek-AI, 2024