dwehr's picture
Migrate action viewer to local Cosmos generation
9f818c5
Raw
History Blame Contribute Delete
32.9 kB
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: OpenMDW-1.1
"""
Imaginaire4 Attention Subpackage:
Unified implementation for all Attention implementations.
Frontend APIs
"""
import torch
from torch import Tensor
from cosmos_framework.model.attention.backends import choose_backend, choose_multi_dim_backend
from cosmos_framework.model.attention.checks import (
attention_param_checks,
attention_tensor_checks,
multi_dim_attention_param_checks,
multi_dim_attention_param_filter,
multi_dim_attention_tensor_checks,
universal_tensor_checks,
varlen_tensor_checks,
)
from cosmos_framework.model.attention.flash2 import flash2_attention
from cosmos_framework.model.attention.flash3 import flash3_attention
from cosmos_framework.model.attention.masks import CausalType
from cosmos_framework.model.attention.natten import natten_attention, natten_multi_dim_attention
from cosmos_framework.model.attention.utils.environment import filter_attention_merge_backends
from cosmos_framework.model.attention.utils.safe_ops import log
# Map backend names to their frontend attention API
BACKEND_MAP = {
"natten": natten_attention,
"flash2": flash2_attention,
"flash3": flash3_attention,
}
MULTI_DIM_BACKEND_MAP = {
"natten": natten_multi_dim_attention,
}
def attention(
query: Tensor, # [B,S_Q,H,D]
key: Tensor, # [B,S_KV,H_KV,D]
value: Tensor, # [B,S_KV,H_KV,D_V]
is_causal: bool = False,
causal_type: CausalType | None = None,
scale: float | None = None,
# varlen parameters
seqlens_Q: Tensor | None = None, # [B]
seqlens_KV: Tensor | None = None, # [B]
cumulative_seqlen_Q: Tensor | None = None, # [B+1]
cumulative_seqlen_KV: Tensor | None = None, # [B+1]
max_seqlen_Q: int | None = None,
max_seqlen_KV: int | None = None,
# backend & misc parameters
backend: str | None = None,
return_lse: bool = False,
backend_kwargs: dict | None = None,
deterministic: bool = False,
) -> Tensor | tuple[Tensor, Tensor]: # [B,S_Q,H,D_V] or ([B,S_Q,H,D_V], [B,S_Q,H,1])
"""
Runs Attention on given operands (Q, K, V) with the heads-last contiguous layout
(`[batch, seqlen, heads, head_dim]`).
Varlen Attention is only supported for the sequence-packed layout: QKV tensors have batch size
1, and tokens from different batches are concatenated without any padding along the sequence
dimension. Sequence lengths for different batches can be provided in two ways:
1. `seqlens_Q` and `seqlens_KV` (less efficient): only provide the sequence lengths as
integer tensors (must be on the same device as QKV), and cumulative and maximum sequence
lengths are recomputed on each call.
2. `cumulative_seqlen_{Q,KV}` and `max_seqlen_{Q,KV}` (more efficient):
compute cumulative and maximum sequence lengths. `cumulative_seqlen_{Q,KV}` are integer
tensors on the same device as QKV containing the cumulative sum of `seqlens_{Q,KV}`,
with an additional `0` element in the beginning, therefore sized `batch+1`.
`max_seqlen_{Q,KV}` are integers (not Tensors) that represent the maximum sequence
lengths for Q and KV among all sequence batches.
You can use `generate_varlen_parameters` to generate these
parameters:
```python3
from cosmos_framework.model.attention.varlen import generate_varlen_parameters
(
cumulative_seqlen_Q,
cumulative_seqlen_KV,
max_seqlen_Q,
max_seqlen_KV,
) = generate_varlen_parameters(q, k, v, seqlens_Q, seqlens_KV)
```
Parameters:
query (Tensor): 4-D query tensor, with the heads-last contiguous layout
(`[batch, seqlen_q, heads, head_dim]`)
key (Tensor): 4-D key tensor, with the heads-last contiguous layout
(`[batch, seqlen_kv, heads_kv, head_dim]`)
value (Tensor): 4-D value tensor, with heads-last contiguous layout
(`[batch, seqlen_kv, heads_kv, head_dim_v]`)
is_causal (bool): whether or not causal masking is enabled. Default is False.
causal_type (CausalType): causal masking mode. Choices: `CausalType.TopLeft`,
`CausalType.BottomRight`, `CausalType.DontCare` (only valid when seqlen_q == seqlen_kv).
Required when `is_causal = True`.
scale (float | None): Dot product scale (attention scale). Defaults to head_dim ** -0.5.
seqlens_Q (Tensor | None): (varlen) Optional 1-D tensor with size `batch`
indicating the number of query tokens in each batch. Must be passed together with
`seqlens_KV`.
seqlens_KV (Tensor | None): (varlen) Optional 1-D tensor with size `batch`
indicating the number of key/value tokens in each batch. Must be passed together with
`seqlens_Q`.
cumulative_seqlen_Q (Tensor | None): (varlen) Optional 1-D tensor with size `batch + 1`
indicating the cumulative sum of number of query tokens in each batch, with an
additional 0 element in the beginning. Must be passed together with
`cumulative_seqlen_KV` and `max_seqlen_{Q,KV}`.
cumulative_seqlen_KV (Tensor | None): (varlen) Optional 1-D tensor with size `batch + 1`
indicating the cumulative sum of number of key/value tokens in each batch, with an
additional 0 element in the beginning. Must be passed together with
`cumulative_seqlen_Q` and `max_seqlen_{Q,KV}`.
max_seqlen_Q (int | None): (varlen) Optional integer indicating the maximum query
sequence length in all batches. Must be passed together with `cumulative_seqlen_{Q,KV}`
and `max_seqlen_KV`.
max_seqlen_KV (int | None): (varlen) Optional integer indicating the maximum key/value
sequence length in all batches. Must be passed together with `cumulative_seqlen_{Q,KV}`
and `max_seqlen_Q`.
Other Parameters:
backend (str | None): Backend to run with. If unspecified (default), it will try to
select the best available.
return_lse (bool): Whether to return the logsumexp values. Default is False.
backend_kwargs (dict | None): Key-value pair for passing arguments specific to the backend's
attention operator, if any. Only valid when a specific backend is selected (backend is
not None).
deterministic (bool): Whether to enforce deterministic backward pass. Default is False.
When True, backends are selected based on deterministic support.
Returns:
output (Tensor): 4-D output tensor, with the heads-last contiguous layout
(`[batch, seqlen_q, heads, head_dim_v]`).
logsumexp (Tensor): logsumexp tensor, with the heads-last contiguous layout
(`[batch, seqlen_q, heads, 1]`). Only returned when return_lse is True.
NOTE: this tensor is not guaranteed to be contiguous with some backends and it should
not be made contiguous unless we can guarantee its results aren't merged via
`merge_attentions`.
"""
assert universal_tensor_checks(query=query, key=key, value=value, raise_error=True)
assert attention_tensor_checks(
query_shape=query.shape,
key_shape=key.shape,
value_shape=value.shape,
dtype=query.dtype,
requires_grad=query.requires_grad or key.requires_grad or value.requires_grad,
raise_error=True,
)
attention_param_checks(
query_shape=query.shape,
key_shape=key.shape,
value_shape=value.shape,
is_causal=is_causal,
causal_type=causal_type,
)
(
cumulative_seqlen_Q,
cumulative_seqlen_KV,
max_seqlen_Q,
max_seqlen_KV,
) = varlen_tensor_checks(
query=query,
key=key,
value=value,
seqlens_Q=seqlens_Q,
seqlens_KV=seqlens_KV,
cumulative_seqlen_Q=cumulative_seqlen_Q,
cumulative_seqlen_KV=cumulative_seqlen_KV,
max_seqlen_Q=max_seqlen_Q,
max_seqlen_KV=max_seqlen_KV,
)
is_varlen = cumulative_seqlen_Q is not None
scale = scale if scale is not None else query.shape[-1] ** -0.5
if backend is None and backend_kwargs is not None:
backend_kwargs = None
log.debug("A backend was not specified, but got backend_kwargs. Ignoring... ")
if backend is not None and backend not in BACKEND_MAP:
raise ValueError(f"Selected {backend=}, but available choices are {BACKEND_MAP.keys()}. ")
compatible_backend = choose_backend(
query_shape=query.shape,
key_shape=key.shape,
value_shape=value.shape,
dtype=query.dtype,
device=query.device,
requires_grad=query.requires_grad or key.requires_grad or value.requires_grad,
is_causal=is_causal,
causal_type=causal_type,
is_varlen=is_varlen,
deterministic=deterministic,
backend=backend,
raise_error=False,
)
# Either incompatible backend specified by user, or no compatible backends found
if compatible_backend is None and backend is None:
raise ValueError(
"Could not find a compatible Attention backend for this use case / device. "
"Try running with debug logs to find out why."
)
elif compatible_backend is None:
raise ValueError(
f"Selected Attention backend {backend} is incompatible with this use case / device. "
"Try running with debug logs to find out why."
)
assert compatible_backend in BACKEND_MAP
return BACKEND_MAP[compatible_backend](
query=query,
key=key,
value=value,
is_causal=is_causal,
causal_type=causal_type,
scale=scale,
cumulative_seqlen_Q=cumulative_seqlen_Q,
cumulative_seqlen_KV=cumulative_seqlen_KV,
max_seqlen_Q=max_seqlen_Q,
max_seqlen_KV=max_seqlen_KV,
return_lse=return_lse,
backend_kwargs=backend_kwargs,
deterministic=deterministic,
)
def multi_dimensional_attention(
query: Tensor, # [B,*token_layout_shape,H,D]
key: Tensor, # [B,*token_layout_shape,H_KV,D]
value: Tensor, # [B,*token_layout_shape,H_KV,D_V]
window_size: tuple | int = -1,
stride: tuple | int = 1,
dilation: tuple | int = 1,
is_causal: tuple | bool = False,
scale: float | None = None,
# backend & misc parameters
backend: str | None = None,
return_lse: bool = False,
backend_kwargs: dict | None = None,
deterministic: bool = False,
) -> (
Tensor | tuple[Tensor, Tensor]
): # [B,*token_layout_shape,H,D_V] or ([B,*token_layout_shape,H,D_V], [B,*token_layout_shape,H])
"""
Runs Multi-Dimensional Attention on given operands (Q, K, V) with the heads-last contiguous
layout (`[batch, *, heads, head_dim]`). Supports up to and including 3 dimensions:
* 1-D: `[batch, X, heads, head_dim]`, with masking arguments expecting tuples of size 1.
* 2-D: `[batch, X, Y, heads, head_dim]`, with masking arguments expecting tuples of size 2.
* 3-D: `[batch, X, Y, Z, heads, head_dim]`, with masking arguments expecting tuples of size 3.
The dimensions here refer to the layout of tokens; that is the arrangement of tokens for each
batch/head, or the `[X]`, `[X, Y]`, `[X, Y, Z]` part of the input shape.
We refer to these as the "token layout shape".
For now, it is always expected that Q, K, and V match in the sizes of those dimensions.
Masking arguments, all of which can be set uniformly across all dimensions or per dimension, are:
* `window_size`: determines the sliding window size. -1 is interpreted as the maximum window
size. Must be either -1 or at least 2 and at most the token layout shape.
For example, if inputs are `[batch, X, Y, Z, heads_{q,kv}, head_dim_{qk,v}]`,
`window_size` must be either an integer == -1 or an integer <= `min(X, Y, Z)`,
or a tuple of size 3 corresponding to the three dimensions / axes, where:
* `window_size[0] == -1 or 2 <= window_size[0] <= X`
* `window_size[1] == -1 or 2 <= window_size[1] <= Y`
* `window_size[2] == -1 or 2 <= window_size[2] <= Z`
When `window_size` is set to the maximum for any dimension, we're effectively performing
self attention (no sparsity) along that dimension.
Default is -1 (self attention).
* `stride`: determines the step size of the sliding window. Only matters when the
corresponding `window_size` is not -1 / maximum (self attention).
Default is 1, indicating the smallest sliding window delay.
Larger values trade off translational equivariance for potentially improved efficiency.
Maximum value for `stride` along each dimension is the corresponding `window_size`.
If `stride == window_size` along any dimension, it is equivalent to blocked / windowed
attention (from works such as Swin Transformer, SAM, ViTDet, etc) along that dimension,
meaning no overlap between windows.
For more details, please refer to the GNA paper:
https://arxiv.org/abs/2504.16922
* `dilation`: introduces gaps between tokens in a sliding window, similarly to dilated
convolution.
Default is 1, indicating no gaps.
Maximum value is the largest positive integer that satisfies
`window_size * dilation <= token_layout_shape` along that dimension.
Higher dilation means more sparse and global context. Lower dilation means more
locality.
For more details, please refer to the DiNAT paper:
https://arxiv.org/abs/2209.15001
* `is_causal`: per-dimension causal mask.
Parameters:
query (Tensor): 4-D, 5-D, or 6-D query tensor, with the heads-last contiguous layout
(`[batch, *token_layout_shape, heads, head_dim]`)
key (Tensor): 4-D, 5-D, or 6-D key tensor, with the heads-last contiguous layout
(`[batch, *token_layout_shape, heads_kv, head_dim]`)
value (Tensor): 4-D, 5-D, or 6-D value tensor, with heads-last contiguous layout
(`[batch, *token_layout_shape, heads_kv, head_dim_v]`)
window_size (tuple | int): Attention window (kernel) size / shape. If an
integer, it will be repeated for all dimensions. For example `window_size=3`, when
`len(token_layout_shape) == 3`, is interpreted as `window_size=(3, 3, 3)`.
`-1`s are replaced with the corresponding `token_layout_shape`.
Final window size must satisfy `2 <= window_size <= token_layout_shape`.
Default is -1 (no sparsity).
stride (tuple | int): Sliding window step size/shape. If an integer, it will be repeated
for all dimensions. For example `stride=2`, when `len(token_layout_shape) == 3`, is
interpreted as `stride=(2, 2, 2)`.
Final stride must satisfy `1 <= stride <= window_size`.
Default is 1.
dilation (tuple | int): Dilation step size/shape. If an integer, it will be repeated for
all dimensions. For example `dilation=4`, when `len(token_layout_shape) == 3`, is
interpreted as `dilation=(4, 4, 4)`.
Final dilation must satisfy `2 <= dilation * window_size <= token_layout_shape`.
Default is 1.
is_causal (tuple | bool): Toggle causal masking. If a boolean, it will be repeated for all
dimensions. For example `is_causal=True`, when `len(token_layout_shape) == 3`, is
interpreted as `is_causal=(True, True, True)`.
Default is False.
scale (float | None): Dot product scale (attention scale). Defaults to head_dim ** -0.5.
Other Parameters:
backend (str | None): Backend to run with. If unspecified (default), it will try to
select the best available.
return_lse (bool): Whether to return the logsumexp values. Default is False.
backend_kwargs (dict | None): Key-value pair for passing arguments specific to the backend's
multi-dim / sparse attention operator, if any. Only valid when a specific backend is
selected (backend is not None).
deterministic (bool): Whether to enforce deterministic backward pass. Default is False.
When True, backends are selected based on deterministic support.
Returns:
output (Tensor): 4-D, 5-D, or 6-D output tensor, with the heads-last contiguous layout
(`[batch, *token_layout_shape, heads, head_dim_v]`).
logsumexp (Tensor): logsumexp tensor, with the heads-last contiguous layout
(`[batch, *token_layout_shape, heads]`). Only returned when return_lse is True.
"""
assert universal_tensor_checks(query=query, key=key, value=value, raise_error=True)
assert multi_dim_attention_tensor_checks(
query_shape=query.shape,
key_shape=key.shape,
value_shape=value.shape,
dtype=query.dtype,
requires_grad=query.requires_grad or key.requires_grad or value.requires_grad,
raise_error=True,
)
token_layout_shape, window_size, stride, dilation, is_causal = multi_dim_attention_param_filter(
query,
window_size=window_size,
stride=stride,
dilation=dilation,
is_causal=is_causal,
)
num_dims = len(token_layout_shape)
# Automatic transformation for 1s in token layout
# I.e. Attention over a (1, 16, 32) token layout is identical to over a (16, 32)
token_layout_ones = [i for i in range(num_dims) if token_layout_shape[i] == 1]
if len(token_layout_ones) > 0:
token_layout_t = tuple(s for i, s in enumerate(token_layout_shape) if i not in token_layout_ones)
window_size_t = tuple(w for i, w in enumerate(window_size) if i not in token_layout_ones)
stride_t = tuple(s for i, s in enumerate(stride) if i not in token_layout_ones)
dilation_t = tuple(d for i, d in enumerate(dilation) if i not in token_layout_ones)
is_causal_t = tuple(c for i, c in enumerate(is_causal) if i not in token_layout_ones)
assert all(x >= 2 for x in token_layout_t)
assert all(w >= 2 for w in window_size_t)
query_t = query.reshape(
query.shape[0], *token_layout_t, query.shape[-2], query.shape[-1]
) # [B,*token_layout_t,H,D]
key_t = key.reshape(key.shape[0], *token_layout_t, key.shape[-2], key.shape[-1]) # [B,*token_layout_t,H_KV,D]
value_t = value.reshape(
value.shape[0], *token_layout_t, value.shape[-2], value.shape[-1]
) # [B,*token_layout_t,H_KV,D_V]
output_shape = [x for x in query.shape[:-1]] + [value.shape[-1]]
if not torch.compiler.is_compiling():
log.debug(
"This Multi-Dimensional Attention problem has 1s in the token layout, which can be simplified from "
f"<{token_layout_shape=}, {window_size=}, {stride=}, {dilation=}, {is_causal=}> into "
f"<{token_layout_t=}, {window_size_t=}, {stride_t=}, {dilation_t=}, {is_causal_t=}>."
)
output_t, lse_t = multi_dimensional_attention(
query=query_t,
key=key_t,
value=value_t,
window_size=window_size_t,
stride=stride_t,
dilation=dilation_t,
is_causal=is_causal_t,
scale=scale,
backend=backend,
return_lse=True,
backend_kwargs=backend_kwargs,
deterministic=deterministic,
)
output = output_t.reshape(*output_shape) # [B,*token_layout_shape,H,D_V]
lse = lse_t.reshape(*output_shape[:-1]) # [B,*token_layout_shape,H]
if return_lse:
return output, lse
return output
multi_dim_attention_param_checks(
query,
window_size=window_size,
stride=stride,
dilation=dilation,
is_causal=is_causal,
)
# Fast path for self attention problems
if all(x == w for x, w in zip(token_layout_shape, window_size)) and (
not any(c for c in is_causal) or num_dims == 1
):
if not torch.compiler.is_compiling():
log.debug(
"This Multi-Dimensional Attention problem is implementable with standard Attention: "
f"{token_layout_shape=}, {window_size=}, {is_causal=}."
)
if backend is not None:
log.debug(f"Ignoring {backend=} and backend args...")
query_1d = query.flatten(1, num_dims) # [B,S,H,D]
key_1d = key.flatten(1, num_dims) # [B,S,H_KV,D]
value_1d = value.flatten(1, num_dims) # [B,S,H_KV,D_V]
is_causal_1d = is_causal[0]
output_shape = [x for x in query.shape[:-1]] + [value.shape[-1]]
output_1d, lse_1d = attention(
query_1d,
key_1d,
value_1d,
scale=scale,
is_causal=is_causal_1d,
causal_type=CausalType.DontCare,
return_lse=True,
deterministic=deterministic,
)
output = output_1d.reshape(*output_shape) # [B,*token_layout_shape,H,D_V]
lse = lse_1d.reshape(*output_shape[:-1]) # [B,*token_layout_shape,H]
if return_lse:
return output, lse
return output
scale = scale if scale is not None else query.shape[-1] ** -0.5
if backend is None and backend_kwargs is not None:
backend_kwargs = None
log.debug("A backend was not specified, but got backend_kwargs. Ignoring... ")
backend = choose_multi_dim_backend(
query_shape=query.shape,
key_shape=key.shape,
value_shape=value.shape,
dtype=query.dtype,
device=query.device,
requires_grad=query.requires_grad or key.requires_grad or value.requires_grad,
deterministic=deterministic,
backend=backend,
)
if backend not in MULTI_DIM_BACKEND_MAP:
raise ValueError(f"Selected {backend=}, but available choices are {MULTI_DIM_BACKEND_MAP.keys()}. ")
return MULTI_DIM_BACKEND_MAP[backend](
query=query,
key=key,
value=value,
window_size=window_size,
stride=stride,
dilation=dilation,
is_causal=is_causal,
scale=scale,
return_lse=return_lse,
backend_kwargs=backend_kwargs,
deterministic=deterministic,
)
def multi_dimensional_attention_varlen(
query: Tensor, # [1,S_total,H,D]
key: Tensor, # [1,S_total,H_KV,D]
value: Tensor, # [1,S_total,H_KV,D_V]
metadata: dict,
scale: float | None = None,
backend: str | None = None,
return_lse: bool = False,
backend_kwargs: dict | None = None,
deterministic: bool = False,
) -> Tensor | tuple[Tensor, Tensor]: # [1,S_total,H,D_V] or ([1,S_total,H,D_V], [1,S_total,H])
"""
Runs Variable-Length Multi-Dimensional Attention on sequence-packed QKV tensors.
This operation performs sparse/multi-dimensional attention on variable-length sequences
where tokens from different samples with different spatial layouts are concatenated
along the sequence dimension. Each sample can have its own spatial dimensions
(e.g., different height/width for 2D layouts).
The metadata should be pre-computed using `configure_varlen_metadata` and reused
across forward/backward passes for efficiency.
**Requires NATTEN >= 0.21.9.dev0 and either Hopper or Blackwell DC-class architecture**
Parameters:
query (Tensor): 4-D query tensor with sequence-packed layout
(`[1, seqlen_total, heads, head_dim]`)
key (Tensor): 4-D key tensor with sequence-packed layout
(`[1, seqlen_total, heads_kv, head_dim]`)
value (Tensor): 4-D value tensor with sequence-packed layout
(`[1, seqlen_total, heads_kv, head_dim_v]`)
metadata (dict): Pre-computed varlen metadata from `imaginaire.varlen.generate_multi_dim_varlen_parameters`.
scale (float | None): Attention scale. Defaults to head_dim ** -0.5.
Other Parameters:
backend (str | None): Backend to run with. If unspecified (default), it will try to
select the best available.
return_lse (bool): Whether to return logsumexp values. Default is False.
backend_kwargs (dict | None): Backend-specific arguments.
deterministic (bool): Whether to enforce deterministic backward pass. Default is False.
Not supported for this operation (Hopper and Blackwell FNA backends do not support determinism).
Returns:
output (Tensor): 4-D output tensor with sequence-packed layout
(`[1, seqlen_total, heads, head_dim_v]`).
logsumexp (Tensor): logsumexp tensor (`[1, seqlen_total, heads]`).
Only returned when return_lse is True.
"""
# For now, NATTEN is the only backend that supports varlen multi-dimensional attention
from cosmos_framework.model.attention.natten import natten_supported
if not natten_supported():
raise RuntimeError("merge_attentions requires NATTEN. Please upgrade NATTEN to use attention merging.")
if backend is not None and backend != "natten":
raise ValueError(
f"multi_dimensional_attention_varlen currently only supports 'natten' backend, got {backend=}."
)
# Import NATTEN's varlen function
from cosmos_framework.model.attention.natten.functions import natten_multi_dim_attention_varlen
return natten_multi_dim_attention_varlen(
query=query,
key=key,
value=value,
metadata=metadata,
scale=scale,
return_lse=return_lse,
backend_kwargs=backend_kwargs,
deterministic=deterministic,
)
def spatio_temporal_attention(
query: Tensor, # [B,T,H_spatial,W_spatial,H,D]
key: Tensor, # [B,T,H_spatial,W_spatial,H_KV,D]
value: Tensor, # [B,T,H_spatial,W_spatial,H_KV,D_V]
window_size: tuple | int = -1,
stride: tuple | int = 1,
dilation: tuple | int = 1,
scale: float | None = None,
# backend & misc parameters
backend: str | None = None,
return_lse: bool = False,
backend_kwargs: dict | None = None,
deterministic: bool = False,
) -> (
Tensor | tuple[Tensor, Tensor]
): # [B,T,H_spatial,W_spatial,H,D_V] or ([B,T,H_spatial,W_spatial,H,D_V], [B,T,H_spatial,W_spatial,H])
"""
Runs Spatio-Temporal Attention on unflattened QKV with the heads-last contiguous layout
(`[batch, T, H, W, heads, head_dim]`).
For now, it is always expected that Q, K, and V match in their shapes.
Parameters:
query (Tensor): 6-D query tensor, with the heads-last contiguous layout
(`[batch, T, H, W, heads, head_dim]`)
key (Tensor): 6-D key tensor, with the heads-last contiguous layout
(`[batch, T, H, W, heads_kv, head_dim]`)
value (Tensor): 6-D value tensor, with heads-last contiguous layout
(`[batch, T, H, W, heads_kv, head_dim_v]`)
window_size (tuple | int): Attention window (kernel) size / shape. If an
integer, it will be repeated for all dimensions. For example `window_size=3` is
interpreted as `window_size=(3, 3, 3)`.
`-1`s are replaced with the corresponding value in `(T, H, W)`.
Default is -1 (no sparsity).
stride (tuple | int): Sliding window step size/shape. If an integer, it will be repeated
for all dimensions. For example `stride=2` is interpreted as `stride=(2, 2, 2)`.
Final stride must satisfy `1 <= stride <= window_size`.
Default is 1.
dilation (tuple | int): Dilation step size/shape. If an integer, it will be repeated for
all dimensions. For example `dilation=4` is interpreted as `dilation=(4, 4, 4)`.
Final dilation must satisfy `2 <= dilation * window_size <= (T, H, W)`.
Default is 1.
scale (float | None): Dot product scale (attention scale). Defaults to head_dim ** -0.5.
Other Parameters:
backend (str | None): Backend to run with. If unspecified (default), it will try to
select the best available.
return_lse (bool): Whether to return the logsumexp values. Default is False.
backend_kwargs (dict | None): Key-value pair for passing arguments specific to the backend's
multi-dim / sparse attention operator, if any. Only valid when a specific backend is
selected (backend is not None).
deterministic (bool): Whether to enforce deterministic backward pass. Default is False.
When True, backends are selected based on deterministic support.
Returns:
output (Tensor): 6-D output tensor, with the heads-last contiguous layout
(`[batch, T, H, W, heads, head_dim_v]`).
logsumexp (Tensor): logsumexp tensor, with the heads-last contiguous layout
(`[batch, T, H, W, heads]`). Only returned when return_lse is True.
"""
if query.dim() != 6:
raise ValueError(
"Spatio-Temporal Attention requires 6-D input tensors ([batch, T, H, W, heads, head_dim]), "
f"got {query.shape=})."
)
return multi_dimensional_attention(
query=query,
key=key,
value=value,
window_size=window_size,
stride=stride,
dilation=dilation,
is_causal=(True, False, False),
scale=scale,
backend=backend,
return_lse=return_lse,
backend_kwargs=backend_kwargs,
deterministic=deterministic,
)
def merge_attentions(
outputs: list[Tensor], # list of [B,S,H,D_V]
lse_tensors: list[Tensor], # list of [B,S,H]
torch_compile: bool = False,
) -> tuple[Tensor, Tensor]: # ([B,S,H,D_V], [B,S,H])
"""
Merges multiple attention outputs that share the same query.
**NOTE: the user is responsible for ensuring ALL output and LSE tensors have the same data
pointer as the outputs from the corresponding Attention operations for correct backpropagation!**
**NOTE: requires NATTEN**
**NOTE: for backpropagation, only two outputs can be merged for now.**
Takes multiple attention outputs computed from the same set of query but w.r.t. different
key/value pairs, and merges them as if all key/value pairs had been concatenated.
This enables patterns like:
- Combining local and global attention (e.g., sparse + dense context)
- Pipelined context parallelism
The NATTEN backend can be controlled via environment variable filtering.
See `filter_attention_merge_backends` for details.
Parameters:
outputs (list[Tensor]): List of 4-D attention output tensors, with the heads-last layout
(`[batch, seqlen, heads, head_dim]`). Must contain at least 2 tensors.
lse_tensors (list[Tensor]): List of 3-D logsumexp tensors, with the heads-last layout
(`[batch, seqlen, heads]`). Must match length of `outputs`.
torch_compile (bool): Attempt to use `torch.compile` to fuse the underlying elementwise
operations. Default is False.
Returns:
output (Tensor): Merged attention output tensor (`[batch, seqlen, heads, head_dim]`).
logsumexp (Tensor): Updated logsumexp tensor (`[batch, seqlen, heads]`).
"""
# For now, NATTEN is the only backend that provides merge_attentions
# Check if NATTEN is allowed by environment variable
allowed_backends = filter_attention_merge_backends(["natten"])
if "natten" not in allowed_backends:
raise RuntimeError(
"merge_attentions requires NATTEN backend, but it has been disabled via environment variable. "
"NATTEN is currently the only supported backend for merge_attentions."
)
from cosmos_framework.model.attention.natten import natten_supported
if not natten_supported():
raise RuntimeError("merge_attentions requires NATTEN. Please upgrade NATTEN to use attention merging.")
# Import and use NATTEN's merge_attentions
from natten.functional import merge_attentions as natten_merge_attentions
return natten_merge_attentions(
outputs=outputs,
lse_tensors=lse_tensors,
torch_compile=torch_compile,
use_autograd_fix=True, # Always use autograd fix for correct backprop
)