Table of contents

Overview#

Let's talk about MLA.

We'll first get an overview of MLA and its benefits for long contexts, then work through KV compression, weight absorption, and decoupled RoPE. A reference implementation is available here.

MLA was first introduced in DeepSeek-V2's technical report. More recent models use MLA or its variants, including DeepSeek-V3.2 with DeepSeek Sparse Attention (DSA), Kimi K2.7 Code with MLA, and Kimi K3 with a hybrid of Kimi Delta Attention (KDA) and Gated MLA.

So what exactly is MLA?

It is a variation of Attention module in Transformer model (originally Multi-Head Attention, introduced in Attention is All You Need), where it saves the past KV values (KV Cache) in a low-rank latent space, by using a down projection weight WDKVW^{DKV} (which refers to Down projection Weight for KV) and conceptually reconstructs them during inference using up-projection matrices WUKW^{UK} and WUVW^{UV} (which refer to Up projection Weights for K and V).

MLA can require more computation than standard MHA, but reduces the size of the KV cache stored and loaded during decoding. This is because MLA caches one shared KV latent rather than separate keys and values for every head. We will compare this in more detail in the following section. So fundamentally speaking, MLA makes a tradeoff between memory bandwidth (KV Cache size) and compute (FLOPs to calculate), without losing performance.

Since MLA reduces the size of KV Cache per layer per token, we have two advantages where

  1. Loading KV Cache from memory is faster
  2. We can store more context in the given memory capacity

Now that we've gone through the "interview" answer, let's dive deeper.

Algorithm#

Notations#

dimensions#

Symbol Meaning Value
dd model hidden dimension 5120
nhn_h number of attention heads 128
ii head index 0 … 127
dhd_h per-head dimension 128
dcd_c KV latent (compression) dimension 512
dc′d'_c query latent dimension 1536
dhRd_h^R per-head dimension of the decoupled RoPE part 64
dkd_k query/key dimension used for attention-score scaling dh=128d_h = 128 without RoPE; dh+dhR=192d_h + d_h^R = 192 with decoupled RoPE
ss query token position —
tt key token position —
TT number of cached tokens attended to, including the current token —

matrices#

Symbol Shape Meaning
XX [1×d][1 \times d] input hidden state of one token (during decode)
WDQW^{DQ} [d×dc′][d \times d'_c] query down-projection
cQc^Q [1×dc′][1 \times d'_c] query latent, cQ=XWDQc^Q = XW^{DQ}
WiUQW_i^{UQ} [dc′×dh][d'_c \times d_h] head ii's content-query up-projection
WiQRW_i^{QR} [dc′×dhR][d'_c \times d_h^R] head ii's query projection for the decoupled RoPE part
WKRW^{KR} [d×dhR][d \times d_h^R] head-shared key projection for the decoupled RoPE part
WOW^O [nhdh×d][n_h d_h \times d] output projection of concatenated head outputs
WDKVW^{DKV} [d×dc][d \times d_c] KV down-projection
cc [1×dc][1 \times d_c] KV latent of one token, c=XWDKVc = X W^{DKV} (the thing we cache; cKVc^{KV} in the paper)
CC [T×dc][T \times d_c] all cached latents stacked, one row cc per token
WUK,WUVW^{UK}, W^{UV} [dc×dhnh][d_c \times d_h n_h] key / value up-projections
WiUK,WiUVW^{UK}_i, W^{UV}_i [dc×dh][d_c \times d_h] head ii's column block of WUKW^{UK} / WUVW^{UV}
kC,vCk^C, v^C [1×dhnh][1 \times d_h n_h] keys / values rebuilt from the latent, cWUKc W^{UK} and cWUVc W^{UV}
Ki,ViK_i, V_i [T×dh][T \times d_h] head ii's keys / values for all cached tokens, CWiUKC W^{UK}_i and CWiUVC W^{UV}_i
qiq_i [1×dh][1 \times d_h] head ii's query for the current token
q~i\tilde{q}_i [1×dc][1 \times d_c] absorbed query, qiWiUK⊤q_i W^{UK\top}_i
aia_i [1×T][1 \times T] head ii's attention weights, softmax(scoresi)\text{softmax}(\text{scores}_i)
a~i\tilde{a}_i [1×dc][1 \times d_c] attention-weighted latent, aiCa_i C
oio_i [1×dh][1 \times d_h] head ii's attention output
kRk^R [1×dhR][1 \times d_h^R] decoupled RoPE key, shared across heads and also cached
R(t)R(t) [dh×dh][d_h \times d_h] RoPE rotation matrix at position tt, built from frequencies θj\theta_j

1. Low-Rank KV Joint Compression#

MLA uses a shared KV latent instead of separate per-head keys and values.
Figure 1. MLA uses a shared KV latent instead of separate per-head keys and values. [1]

As explained, MLA uses a shared low-rank latent to represent keys and values, with separate up-projections producing each head's keys and values.

KV Down Projection#

During down projection, we project the entire hidden dimension dd into latent dimension dcd_c without separating heads (notice there is no ii in the equation below).

The hidden state XX of one token is projected by WDKVW^{DKV} (we will talk about WQW^Q later): KV Down Projection: cKV=XWDKV[1×d]×[d×dc]=[1×dc]\text{KV Down Projection: } c^{KV} = X W^{DKV} \\ \\ \qquad [1 \times d] \times [d \times d_c] = [1 \times d_c] In DeepSeek-V2, this is [1×5120]→[1×512][1 \times 5120] \rightarrow [1 \times 512].

From now on we will call cKVc^{KV} as KV latent interchangeably. KV Latent is stored as KV cache for each token, for each layer.

Also keep in mind this is a KV "joint" compression, where we don't save latents for K and V separately but in one KV latent instead.

KV Up Projection#

For attention operation, we need to load the cached latent CC, which is every past token's cc stacked as rows ([T×512][T \times 512]) and expand each one back into keys and values.

Unlike the down projection, which used a single matrix WDKVW^{DKV}, the up projection has a separate pair of matrices for each head. Every head uses the same latent cKVc^{KV} but each has its own pair of up-projection matrices, WiUKW^{UK}_i and WiUVW^{UV}_i. You can think of it as each head reading the same compressed vector but in different ways.

K Up Projection: Ki=CWiUKV Up Projection: Vi=CWiUV[T×dc]×[dc×dh]=[T×dh] for each head\text{K Up Projection: } K_{i} = C W^{UK}_i \\ \\ \text{V Up Projection: } V_{i} = C W^{UV}_i \\ \\ [T \times d_c] \times [d_c \times d_h] = [T \times d_h] \text{ for each head}

In DeepSeek-V2, each head turns the [1×512][1 \times 512] latent into a [1×128][1 \times 128] key and a [1×128][1 \times 128] value. Across all 128 heads, the up projections produce 16,38416{,}384 (128×128128 \times 128) dimensional keys and 16,38416{,}384 dimensional values per token, while caching only the shared 512512 dimensional KV latent.

Q Projection#

MLA also compresses the query, projecting the hidden state XX into a query latent cQc^Q before up-projecting it into each head's query. These projections run during both training and inference. While query compression does not reduce the KV cache, it is used to reduce activation memory during training.

The DeepSeek-V2 report introduces query compression to reduce training activation memory.
Figure 2. The DeepSeek-V2 report introduces query compression to reduce training activation memory. [2]

As described in Section 2.1.2 of the paper,[2] query compression reduces activation memory during training and uses a separate compression dimension dc′d'_c, independent of dcd_c. DeepSeek-V2 uses dc=512d_c = 512 and dc′=1,536d'_c = 1{,}536.

Comparing Projection Shapes in MHA vs MLA#

Now that we've gone through how MLA compresses and projects its Q, K, and V, let's compare the Q, K, V projections in MHA and MLA.

In MHA, each head projects directly from the hidden states. In MLA, all heads share a query down-projection and a joint KV down-projection, then use their own up-projection matrices.

Projection MHA MLA
Query qi=XWiQq_i = XW_i^Q qi=(XWDQ)WiUQq_i = (XW^{DQ})W_i^{UQ}
Key Ki=XWiKK_i = XW_i^K Ki=(XWDKV)WiUKK_i = (XW^{DKV})W_i^{UK}
Value Vi=XWiVV_i = XW_i^V Vi=(XWDKV)WiUVV_i = (XW^{DKV})W_i^{UV}

The weight shapes are:

Weight MHA MLA
Query WiQ:[d×dh]W_i^Q: [d \times d_h] WDQ:[d×dc′]W^{DQ}: [d \times d'_c], WiUQ:[dc′×dh]W_i^{UQ}: [d'_c \times d_h]
Key WiK:[d×dh]W_i^K: [d \times d_h] WDKV:[d×dc]W^{DKV}: [d \times d_c], WiUK:[dc×dh]W_i^{UK}: [d_c \times d_h]
Value WiV:[d×dh]W_i^V: [d \times d_h] The same WDKVW^{DKV} as the key, then WiUV:[dc×dh]W_i^{UV}: [d_c \times d_h]

This gives the following shape changes for each head:

Projection MHA MLA
Query [1×d]→[1×dh][1 \times d] \rightarrow [1 \times d_h] [1×d]→[1×dc′]→[1×dh][1 \times d] \rightarrow [1 \times d'_c] \rightarrow [1 \times d_h]
Key [T×d]→[T×dh][T \times d] \rightarrow [T \times d_h] [T×d]→[T×dc]→[T×dh][T \times d] \rightarrow [T \times d_c] \rightarrow [T \times d_h]
Value [T×d]→[T×dh][T \times d] \rightarrow [T \times d_h] [T×d]→[T×dc]→[T×dh][T \times d] \rightarrow [T \times d_c] \rightarrow [T \times d_h]
As seen here, the final shapes are the same for both cases: [1×dh][1 \times d_h] for the query and [T×dh][T \times d_h] for the cached keys and values.

Here is a visual explanation on Q compression / projection and KV compression / projection from a YouTube video I watched which I found helpful:

A shared KV latent is up-projected into keys and values.
Figure 3. A shared KV latent is up-projected into keys and values. [5]

Naive MLA reconstructs keys and values before computing attention.
Figure 4. Naive MLA reconstructs keys and values before computing attention. [5]

2. Weight Absorption, or Attention Without K/V Reconstruction#

Naive decoding#

As mentioned above, cache CC refers to every past token's KV Latent cc stacked as rows ([T×512][T \times 512]). When a new token comes in, its query is split into 128 heads where head ii's query qiq_i is [1×128][1 \times 128].

So how do we compute attention score?

The most intuitive way is to rebuild the keys from the cache, then calculate the attention score SiS_i against them:

Si=qiKi⊤dk=qi(CWiUK)⊤dkS_i = \frac{q_i K_i^\top}{\sqrt{d_k}} = \frac{q_i \left(C W^{UK}_i\right)^\top}{\sqrt{d_k}}

We can break this down into two steps:

  1. Projected KiK_i: Ki=CWiUKK_i = C W^{UK}_i: [T×512][512×128]=[T×128][T \times 512] [512 \times 128] = [T \times 128], costing 131,072T131{,}072 T
  2. Attention score: Si=qiKi⊤S_i = q_i K_i^\top: [1×128][128×T]=[1×T][1 \times 128] [128 \times T] = [1 \times T], costing 256T256 T

We can see where the cost comes from. Attention score computation is relatively cheap, but to get there we push every row of the cache through WiUKW^{UK}_i, and we do this again for every decoding step. The longer the context (TT) gets, the worse it becomes.

It turns out there is a much cheaper way to get the exact same result.

Weight Absorption#

What if we don't up-project K right away?

Let's come back to the attention score equation.

Si=qiKi⊤dk=qi(CWiUK)⊤dkS_i = \frac{q_i K_i^\top}{\sqrt{d_k}} = \frac{q_i \left(C W^{UK}_i\right)^\top}{\sqrt{d_k}}

What we did above was to do Ki=CWiUKK_i = C W^{UK}_i first (in the parentheses) and then do matrix multiplication with qiq_i. However, since matrix multiplication is associative, we are free to choose which product to compute first.

Using the transpose rule (AB)T=BTAT(AB)^T = B^TA^T, we can reverse the order of the matrices inside the transpose as:

Si=qiKi⊤dk=qi(CWiUK)⊤dk=qiWiUK⊤C⊤dkS_i = \frac{q_i K_i^\top}{\sqrt{d_k}} = \frac{q_i \left(C W^{UK}_i\right)^\top}{\sqrt{d_k}} = \frac{q_i W^{UK\top}_iC^\top}{\sqrt{d_k}}

Then, by the associative property of matrix multiplication, we can compute (qiWiUK⊤q_i W_i^{UK\top}) first and multiply the result by (CTC^T). Intuitively, you can think of q~i\tilde{q}_i as each query head projected into the latent space.

So instead of applying WiUKW^{UK}_i to all TT rows of the cache CC, we can apply it once to the query:

  1. Absorbed query q~i=qiWiUK⊤\tilde{q}_i = q_i W^{UK\top}_i: [1×128][128×512]=[1×512][1 \times 128] [128 \times 512] = [1 \times 512]. This costs 131,072131{,}072, independent of TT.
  2. Attention score scoresi=q~iC⊤\text{scores}_i = \tilde{q}_i C^\top: [1×512][512×T]=[1×T][1 \times 512] [512 \times T] = [1 \times T]. This costs 1,024T1{,}024 T.

Interestingly, while the result is mathematically identical, the cost of compute is very different:

FLOPs
Naive (Non-absorbed) 131,328T131{,}328 T
Absorbed 131,072+1,024T131{,}072 + 1{,}024 T

Absorption reduces the TT-dependent cost from 131,328T131{,}328 T to 1,024T1{,}024 T, while adding 131,072131{,}072 FLOPs to transform the query. For integer T≥2T \ge 2, this uses fewer FLOPs than naive reconstruction and avoids materializing KiK_i during decode.

Weight absorption computes attention from the cached latent without reconstructing full keys and values.
Figure 5. Weight absorption computes attention from the cached latent without reconstructing full keys and values. [5]

The same kind of optimization can be applied during value aggregation, where we compute the weighted sum of the values with attention weights.

Each head's output before the final projection WOW^O can be expressed as:

oi=aiVi=ai(CWiUV)=(aiC)WiUVwhere ai=softmax(Si).o_i = a_i V_i = a_i(C W_i^{UV}) = (a_i C)W_i^{UV} \\ \\ \text{where } a_i = \mathrm{softmax}(S_i).

Again, by associativity, there are two ways to get the same output.

Naive (Non-absorbed): Up project the cached latents first, then compute their weighted sum.

  1. Value Projection: Vi=CWiUVV_i = C W_i^{UV}: [T×512][512×128]=[T×128][T \times 512][512 \times 128] = [T \times 128], costing 131,072T131{,}072T FLOPs.
  2. Weighted Value Aggregation: oi=aiVio_i = a_i V_i: [1×T][T×128]=[1×128][1 \times T][T \times 128] = [1 \times 128], costing 256T256T FLOPs.

Absorbed: Compute the weighted sum of the cached latents first, then up-project the result.

  1. Weighted Latent Aggregation: a~i=aiC\tilde{a}_i = a_i C: [1×T][T×512]=[1×512][1 \times T][T \times 512] = [1 \times 512], costing 1,024T1{,}024T FLOPs.
  2. Value Projection: oi=a~iWiUVo_i = \tilde{a}_i W_i^{UV}: [1×512][512×128]=[1×128][1 \times 512][512 \times 128] = [1 \times 128], costing 131,072131{,}072 FLOPs.

The value projection now operates on one aggregated latent instead of all TT cached latents. We never materialize the full [T×128][T \times 128] value matrix ViV_i.

The cost per head, excluding the common final output projection, is:

FLOPs
Naive (Non-absorbed) 131,328T131{,}328 T
Absorbed 1,024T+131,0721{,}024 T + 131{,}072

In both paths, we concatenate the [1×128][1 \times 128] head outputs and apply the same output projection:

O=Concat(o0,…,onh−1)WOO = \mathrm{Concat}(o_0,\ldots,o_{n_h-1})W^O

3. Decoupled RoPE (Rotary Positional Embedding)#

Decoupled RoPE is the final part to understand to draw the full picture of MLA.

It turns out RoPE is incompatible with weight absorption, which we've just been talking about! Or to be more precise, we can use weight absorption but that won't lead to the optimization we've seen when RoPE is applied.

Why is so? Let's first recall how RoPE is calculated. RoPE rotates Q and K matrices after their projection. This can be expressed as a [2×2][2 \times 2] rotation matrix, where for every pair of dimensions at token position tt:

[x2j′x2j+1′]=[x2jx2j+1][cos⁡tθjsin⁡tθj−sin⁡tθjcos⁡tθj]\begin{bmatrix} x'_{2j} & x'_{2j+1} \end{bmatrix} = \begin{bmatrix} x_{2j} & x_{2j+1} \end{bmatrix} \begin{bmatrix} \cos t\theta_j & \sin t\theta_j \\ -\sin t\theta_j & \cos t\theta_j \end{bmatrix} where

  • x′x' denotes the rotated vector
  • j∈{0,…,dh/2−1}j \in \{0, \dots, d_h/2 - 1\} indexes the dimension pair
  • θj=10000−2j/dh\theta_j = 10000^{-2j/d_h} is the rotation frequency for that pair

Let ss be the query position and tt the key position. The score becomes:

(qR(s))(kR(t))⊤=qR(s−t)k⊤(q R(s))(k R(t))^{\top} = q R(s-t) k^{\top}

Now let's put this into MLA. Without RoPE, the score against token tt is what we saw in weight absorption:

scorei(s,t) (without RoPE)=(qi(s)WiUK⊤)ct⊤\text{score}_i(s,t) \text{ (without RoPE)} = \big(q_i(s) W^{UK\top}_i\big) c_t^{\top}

Applying RoPE, the rotated key of token tt is ki(t)=ctWiUKR(t)k_i(t) = c_t W^{UK}_i R(t), so R(s−t)R(s-t) lands right in the middle:

scorei(s,t) (with RoPE)=(qi(s)R(s−t)WiUK⊤)ct⊤\text{score}_i(s,t) \text{ (with RoPE)} = \big(q_i(s) R(s-t) W^{UK\top}_i\big) c_t^{\top}

As seen in the equation, if we apply RoPE, the absorbed query q~i(s,t)=qi(s)R(s−t)WiUK⊤\tilde{q}_i(s,t) = q_i(s) R(s-t) W^{UK\top}_i contains R(s−t)R(s-t) in the middle which differs for each past token. So, while keeping only the latent cache, we need a separate q~i(s,t)\tilde{q}_i(s,t) per token: the same [1×128]×[128×512][1 \times 128] \times [128 \times 512] product TT times.

Applying RoPE to the full key makes the absorbed query depend on each key position.
Figure 6. Applying RoPE to the full key makes the absorbed query depend on each key position.

RoPE Projection#

In order to avoid this scenario and use weight absorption, MLA decouples RoPE from the KV latent. Intuitively, you can think of this as splitting up content information and position information explicitly.

Each query and key is split into:

  1. a content part that is never rotated (what we've seen in Sections 1–2)
  2. a small RoPE part (dhR=64d_h^R = 64) in charge of position information

Looking at just the RoPE part: the query side projects the compressed query cQc^Q [1×1536][1 \times 1536] with WiQRW^{QR}_i [1536×64][1536 \times 64], one per head, so 128×[1536×64]128 \times [1536 \times 64] in total. The key side projects the raw hidden state XX [1×5120][1 \times 5120] with WKRW^{KR} [5120×64][5120 \times 64]. Unlike WQRW^{QR}, WKRW^{KR} is shared across all heads, so we only cache one 64-dim RoPE key per token.

qiR=RoPE(cQWiQR),kR=RoPE(XWKR)q^R_i = \text{RoPE}\big(c^Q W^{QR}_i\big), \qquad k^R = \text{RoPE}\big(X W^{KR}\big)

The score is now the sum of the two parts:

scorei(s,t)=(qi(s)WiUK⊤)ct⊤+qiR(s)(ktR)⊤\text{score}_i(s,t) = \big(q_i(s) W^{UK\top}_i\big) c_t^\top + q^R_i(s) (k_t^R)^\top

Since the first term involves no rotation, weight absorption works exactly as before. In code, the content components of the query and key are often labeled nope, short for “no positional embedding,” because RoPE is not applied to them.

The second term has the rotation, but kRk^R never goes through cc or WUKW^{UK}, so we can cache it already rotated and use it directly. In total, the cache holds 512+64=576512 + 64 = 576 elements per token.

For the same query-head count, per-head dimension, and cache dtype, the KV cache sizes per token per layer are:

Method Cache elements per token per layer
MHA 2nhdh2n_h d_h
GQA 2nkvdh2n_{kv}d_h
MQA 2dh2d_h
MLA dc+dhR=576d_c + d_h^R = 576

Here, nkvn_{kv} is the number of KV heads in GQA.

With BF16, MLA stores 576×2=1,152576 \times 2 = 1{,}152 bytes per token per layer. For 60 layers, batch size 1, and T=32,768T = 32{,}768, the total KV cache payload is 1,152×60×32,768=2,264,924,1601{,}152 \times 60 \times 32{,}768 = 2{,}264{,}924{,}160 bytes, or about 2.11 GiB.

Putting the FLOPs together#

We can now put the pieces together. For one decode token, both paths create the query, cache the new token's KV latent and RoPE key, and apply the final output projection. Absorption only changes the computation between these projections.

We count an [m×k][m \times k] by [k×n][k \times n] matrix multiplication as 2mnk2mnk FLOPs, with one fused multiply-add (FMA) counting as 2 FLOPs. All counts below are for batch size 1, one decode token, and one layer, across all 128 heads.

Using the dimensions above, the matrix-multiplication costs are:

Stage Naive Absorbed
Query down- and up-projections, including the RoPE query 91.23M 91.23M
New token's KV latent and RoPE key projections 5.90M 5.90M
Content attention, as derived in Section 2 33,619,968T33{,}619{,}968T 33,554,432+262,144T33{,}554{,}432 + 262{,}144T
RoPE attention scores 16,384T16{,}384T 16,384T16{,}384T
Final output projection 167.77M 167.77M

The shared projections cost about 264.90M FLOPs per decode step, independent of context length. Adding attention gives:

Fnaive(T)=264,896,512+33,636,352T,Fabsorbed(T)=298,450,944+278,528T.\begin{aligned} F_{\text{naive}}(T) &= 264{,}896{,}512 + 33{,}636{,}352T,\\ F_{\text{absorbed}}(T) &= 298{,}450{,}944 + 278{,}528T. \end{aligned}

Context length TT Naive Absorbed
1 298.53M 298.73M
1,024 34.71G 583.66M
32,768 1.10T 9.43G

We can see a clear win as context length TT grows, since absorption removes the need to reconstruct every cached key and value during decode step.

Is Weight Absorption Always the Best Choice?#

On precomputing merged query weights#

DeepSeek describes both under weight absorption. Here, we use "merged weights" specifically for weight products that are precomputed and stored.

I didn't mention this when explaining weight absorption but we can also consider precomputing WiQK=WiUQWiUK⊤W_i^{QK} = W_i^{UQ} W_i^{UK\top}, combining the query up projection with the key up projection. In this case, instead of computing qiq_i and transforming it into the latent space, we project cQc^Q directly using the merged weights.

The DeepSeek-V2 report describes absorbing key and value up-projections into query and output projections.
Figure 7. The DeepSeek-V2 report describes absorbing key and value up-projections into query and output projections. [2]

Since the merged weights can be reused while the model parameters remain fixed, we need only one matrix multiplication cQWiQKc^Q W_i^{QK} instead of two separate projections.

However, after doing some research myself, I found that most inference implementations do not precompute merged weights, contrary to the description of the paper. For example, vLLM removed the materialized query/key and value/output weight products in favor of sequential matrix multiplications, citing lower memory usage and FLOPs.

vLLM replaces materialized weight products with sequential matrix multiplications.
Figure 8. vLLM replaces materialized weight products with sequential matrix multiplications. [3]

DeepSeek-V3's public inference implementation also evaluates the absorbed computation through sequential operations. It directly projects wq_b (query up projection weight) right after wq_a (query down projection weight) and normalization, instead of merged weights.

DeepSeek-V3 applies query down-projection, normalization, and up-projection sequentially.
Figure 9. DeepSeek-V3 applies query down-projection, normalization, and up-projection sequentially. [4]

The relevant codes can be found in https://github.com/junuxyz/transformer-lab/blob/mla_absorbed/src/components/attention/mla.py.

Thanks for reading and feel free to reach out if there's anything misleading or unclear.

References#

  1. Sebastian Raschka. Multi-Head Latent Attention (MLA).
  2. DeepSeek-AI. DeepSeek-V2 technical report, Section 2.1.2.
  3. vLLM. MLA get rid of materialization, PR #14770.
  4. DeepSeek-AI. DeepSeek-V3 inference implementation.
  5. Welch Labs. How DeepSeek Rewrote the Transformer [MLA].