The useful part, up front

What you’ll take away

  • The first row of a new query block can represent a later position in the sequence.
  • For the original suffix fixture, intended causal rows allow five, six and seven keys; upper-left rows allow only one, two and three.
  • For contiguous ranges, the causal offset is the query origin minus the key origin.
  • Aligned sequence endings justify the lower-right offset S − L; irregular retained positions need their actual mapping.
  • A validity mask removes unused slots but cannot repair an incorrect causal origin for real keys.
  • Square masks, final-row checks and coincidentally equal outputs can miss alignment mistakes.
01

The first row of a new query block need not be the first token

A causal attention mask decides which earlier or current token positions a model may use when processing a particular position. The first row in a fresh batch of queries can be misleading: row zero might represent the fifth token in an existing sequence. If the mask treats it as the first token instead, it can hide valid earlier context even though every array has an acceptable size.

Caching makes that distinction useful. Hugging Face describes storing earlier keys and values and combining them with newly computed ones, so the new query block can be shorter than the available key sequence [4]. A query asks which stored information to use; keys help assign attention scores, and values supply the information being combined.

Our original seven-position example holds the scores and values fixed while changing only the allowed positions. It shows a wrong result from an incorrectly aligned mask, then follows the same reasoning through a single query, missing key positions and unused storage slots. These are exact teaching calculations, not results from running a language model.

Identical dimensions can hide different allowed positions. Original 3×7 relation. Key columns are positions 0 through 6; each 1 permits that key and each 0 excludes it. Both masks have the same shape.
Original 3×7 relation. Key columns are positions 0 through 6; each 1 permits that key and each 0 excludes it. Both masks have the same shape. · Ameer Journal — original explanatory diagramOriginal editorial illustration; no third-party artwork reused.
02

Label the positions before drawing the mask

Assume four earlier positions, numbered zero through three, already have cached keys and values. We now process three new positions, four, five and six. Their new keys join the earlier ones, making seven key positions in total. The new query block has only three rows, corresponding to positions four, five and six in that order.

Use an inclusive rule: a query may see keys at its own position or earlier, but none from later positions. Position four may therefore use keys zero through four: five choices. Position five has six choices, and position six has seven. Including the current position means including the input currently being processed; it does not give that input access to the following position.

For this example, assign the seven values 2, 5, 11, 17, 23, 31 and 47. They are invented numbers with no claimed relationship to words or model predictions. We will use them to make different allowed sets produce visibly different outputs, while preserving the same seven stored values throughout the first comparisons.

03

Upper-left and lower-right describe different alignments

An upper-left triangular mask starts by allowing just the first key, then the first two, then the first three. Across our three query rows and seven key columns, its allowed sets are zero; zero and one; then zero, one and two. That would be appropriate for queries at positions zero, one and two. Our queries are at four, five and six, so those sets hide valid context.

PyTorch's documented upper-left construction has this first-row alignment. Its lower-right construction instead shifts the diagonal by the number of columns minus the number of rows [1][2]. Here seven minus three is four. The allowed sets become zero through four, zero through five, and zero through six, matching our declared positions.

Neither corner name is a universal quality label. The same three-by-seven dimensions could describe queries at the beginning or the end of a longer key sequence. The correct answer comes from what the rows and columns represent. Merely checking that the mask has three rows and seven columns cannot choose the right alignment.

04

Use equal scores to expose the changed context

Attention normally gives different allowed values different weights. Our teaching case deliberately gives every allowed key the same score. After normalizing those equal scores, the output is the ordinary average of the allowed values. This removes score preference from the comparison and lets the mask's selection do all the work.

For query position four, the intended sum is 2+5+11+17+23=58, divided by five, giving 11.6. Position five includes 31 as well, giving 89 divided by six, about 14.8333. Position six also includes 47, giving 136 divided by seven, about 19.4286. These are three different averages because each query may see a different prefix.

The upper-left mask instead produces 2, then 3.5, then 6. It has kept only the first one, two and three values. The array sizes still fit, and every result is an ordinary finite number. A successful calculation therefore would not by itself prove that the intended context was used. Checking allowed identities reveals an error that a shape check cannot.

Equal scores turn changed membership into changed averages. Original values 2,5,11,17,23,31,47; all allowed finite logits are zero and dropout is absent. Fractions are exact; decimals are rounded.
Original values 2,5,11,17,23,31,47; all allowed finite logits are zero and dropout is absent. Fractions are exact; decimals are rounded. · Ameer Journal — original explanatory diagramOriginal editorial illustration; no third-party artwork reused.
05

The final row can hide mistakes in earlier rows

What happens if every key is allowed for all three queries? Every output becomes 136 divided by seven. The last row is correct because position six really may see all seven keys. The first two rows are wrong: position four can now use future positions five and six, and position five can use future position six.

Checking only the final output would miss that failure in the three-query block. This matters to the design of a small example: it needs an earlier query with genuinely later keys, not just the last row where everything happens to be available. A mask can get one boundary case right while expressing the wrong rule elsewhere.

Now process only the query at position six against the same seven keys. Its intended mask permits all seven, while an upper-left one-row mask permits only key zero. The exact outputs are about 19.4286 and 2. In this particular single-query case, allowing every valid key is causally acceptable because none lies in the query's future. That conclusion depends on the positions, not merely on there being one row.

06

The offset comes from sequence origins

Suppose the retained keys occupy consecutive positions, beginning at some position we call the key origin. The queries also occupy consecutive positions, beginning at the query origin. A local row number tells us how far to move from the query origin, while a column number tells us how far to move from the key origin.

Our causal question is then simple: has this key's actual position been reached by this query? The difference between the two origins tells us how far to shift the triangle. In the seven-key example, the key origin is zero and the query origin is four, so the shift is four. Counting only rows would throw away that information.

Removing some earliest keys does not automatically break lower-right alignment. Retain consecutive keys at positions three, four, five and six, while keeping queries at four, five and six. Both spans still end at six. Four key columns minus three query rows gives a shift of one, correctly allowing two, three and four keys. The relevant condition is the alignment of the represented spans, not whether any old key was removed.

07

A gap in the retained positions changes the problem

Consider a different hypothetical selection of keys: positions zero, two, four and six. Keep the same queries at four, five and six. This is a deliberately constructed gathered set, not a claim that a named cache normally discards these particular positions. The column numbers zero through three now stand for four nonconsecutive sequence positions.

The query at four may use keys zero, two and four, so it has three choices. The query at five has those same three choices because there is no retained key at five. The query at six may use all four. The intended counts are therefore three, three and four.

A length-only lower-right triangle uses four minus three as its shift and instead allows two, three and four keys. Its first row wrongly hides position four. Using the corresponding values 2,11,23,47, the intended first average is 12, while the mistaken first average is 6.5. The remaining two outputs agree. Again, agreement in some rows does not validate the rule for every row.

The position mapping decides whether a corner rule fits. Original mappings, with inclusive causal access. Lower-right length offset assumes the contiguous represented ranges end together.
Original mappings, with inclusive causal access. Lower-right length offset assumes the contiguous represented ranges end together. · Ameer Journal — original explanatory diagramOriginal editorial illustration; no third-party artwork reused.
08

Unused storage slots cannot define the causal origin

Storage capacity introduces another length. Imagine nine allocated key slots, but only seven contain real keys at positions zero through six. The final two are unused. Our three queries still represent positions four, five and six. A separate validity check can mark the two unused slots unavailable.

If we calculate the triangular shift from all nine slots, nine minus three gives six. The first query row then allows real keys zero through six. Removing the two unused slots does nothing to fix that error, because future positions five and six are real, valid keys. The same problem affects query position five.

The correct rows still allow five, six and seven real keys, with both unused slots excluded throughout. Hugging Face documents fixed-length cache storage as one supported approach [4]; this invented example does not claim that its implementation makes our mistake. It shows why a validity rule and a causal-position rule answer different questions. Marking storage as occupied is not the same as establishing that its content is in a particular query's past.

09

A high-level input mask and an attention matrix have different shapes

There is another reason not to copy a mask shape from one example into a different call. A model's generation interface may accept a row of flags covering its input and cached positions. A lower-level attention operation must ultimately decide separately for each query which keys it may use. Those interfaces are describing related information at different stages.

Hugging Face's cited cache guide describes an attention mask covering the combined past and current length for each batch item [4]. PyTorch's scaled-dot-product attention reference describes a mask that can expand to the dimensions of its attention weights, including query and key axes [3]. These statements do not mean that either interface accepts every shape accepted by the other.

In our construction, a seven-entry validity row cannot by itself express why query four excludes two real keys while query six excludes none. A causal rule supplies the query-dependent part. Record the meaning of the flags and the stage that adds this rule, instead of assuming that a successfully expanded array already contains the full intended temporal relationship.

Missing key positions can defeat a length-only triangle. Original gathered keys at 0,2,4,6 with values 2,11,23,47. Query positions stay 4,5,6. This is a mathematical selection, not a named cache’s eviction policy.
Original gathered keys at 0,2,4,6 with values 2,11,23,47. Query positions stay 4,5,6. This is a mathematical selection, not a named cache’s eviction policy. · Ameer Journal — original explanatory diagramOriginal editorial illustration; no third-party artwork reused.
10

Read the documented alignment before selecting the API option

The PyTorch 2.15 reference describes its is_causal option as upper-left aligned for a nonsquare score matrix [3]. That matters in our suffix-query example: the word causal does not tell the call where the new queries begin in the old sequence. The lower-right utility expresses a different alignment, and PyTorch's tutorial shows these bias objects being supplied to the attention operation [1][5].

The same reference says an explicit attention mask cannot be combined with is_causal set to true [3]. This article reports that documented interface boundary; it does not run a call to test a particular installation. A proposed implementation needs the documentation for its actual version and the chosen backend's supported inputs.

The important decision comes before the API spelling. Write the allowed position pairs, then identify which documented option represents them. For contiguous suffix queries ending with the key sequence, a lower-right relation fits our example. For the gathered or partly filled layouts, preserve their extra position and validity information. A familiar option name cannot supply information that its inputs never received.

11

Compare complete position relations before comparing rounded outputs

A small check should contain a rectangular case, an earlier query with a future key, and different values at positions that one candidate mask hides or exposes. Our three-by-seven example supplies all three. A square seven-by-seven case is also useful, but upper-left and lower-right triangles are identical there, so that case alone cannot distinguish their alignment.

We calculated fifty-four suffix layouts using zero through eight past positions and one through six new positions. In every constructed layout, the position-based mask matched the lower-right relation and the corresponding rows taken from a full-sequence causal mask. Upper-left matched those suffix cases only when there were no past positions. This is a finite mathematical check, not a performance result.

There is also an empty-row boundary. If a query precedes every retained key, it has no allowed value to average in this simple reference. We leave that average undefined instead of inventing a zero. A real implementation's handling needs its own documented behavior; the teaching calculation does not decide how a particular kernel responds.

12

A useful cache record explains what each row and column means

For a cache-related attention result, keep the query positions, retained key positions and slot-validity information together. State whether the current block's keys are included, and whether keys belong to one sequence. Then record the intended past-and-current rule and the API option used to represent it. These details explain why the mask is correct; an array size alone cannot.

The seven-key construction gives a clear comparison. Intended suffix attention averages five, six and seven values. An upper-left triangle averages only one, two and three. An all-allowed mask gets the final row right while leaking later positions into the earlier rows. The gathered and allocated-storage cases show why even the familiar lower-right length formula has assumptions.

The practical outcome is a precise question for any new cache layout: which sequence position does this local index represent? Answering it lets you derive the allowed pairs, identify the necessary validity checks and compare the complete relation with a reference. It establishes the mask's position accounting without pretending to measure model intelligence, answer quality or hardware speed.

A validity mask cannot repair the wrong causal origin. Original nine-slot allocation with seven real keys 0–6 and two unused slots. Queries are 4,5,6. The wrong offset uses 9−3=6 before removing unused slots.
Original nine-slot allocation with seven real keys 0–6 and two unused slots. Queries are 4,5,6. The wrong offset uses 9−3=6 before removing unused slots. · Ameer Journal — original explanatory diagramOriginal editorial illustration; no third-party artwork reused.
Follow the evidence

Sources & further reading

Explore the references behind this guide. External pages may change after our review.

  1. torch.nn.attention.bias.causal_lower_right — PyTorch 2.15 documentation
    PyTorch · Accessed 7 Nov 2026
  2. torch.nn.attention.bias.causal_upper_left — PyTorch 2.15 documentation
    PyTorch · Accessed 7 Nov 2026
  3. torch.nn.functional.scaled_dot_product_attention — PyTorch 2.15 documentation
    PyTorch · Accessed 7 Nov 2026
  4. How caching works · Hugging Face
    Hugging Face · Accessed 7 Nov 2026
  5. (Beta) Implementing High-Performance Transformers with Scaled Dot Product Attention (SDPA) — PyTorch Tutorials 2.15.0+cu132 documentation
    PyTorch · Accessed 7 Nov 2026

Article updated 7 Nov 2026.

Found something we should revisit?

Good explanations get better with thoughtful feedback.

Suggest a correction