Skip to content

Commit 9b30925

Browse files
ltx2: add Sparse VideoGen (SVG) attention to LTX2 attention and transformer
1 parent 0a943b7 commit 9b30925

3 files changed

Lines changed: 613 additions & 16 deletions

File tree

‎src/maxdiffusion/models/ltx2/attention_ltx2.py‎

Lines changed: 124 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import jax.numpy as jnp
2323
from ... import common_types
2424
from ..attention_flax import NNXAttentionOp
25+
from ..wan.transformers import svg_attention
2526
from .logical_sharding_ltx2 import get_sharding_specs, LTX2DiTShardingSpecs
2627

2728
Array = common_types.Array
@@ -352,7 +353,58 @@ def __init__(
352353
use_base2_exp: bool = False,
353354
use_experimental_scheduler: bool = False,
354355
enable_jax_named_scopes: bool = False,
356+
attention_config: Optional[dict] = None,
355357
):
358+
attention_config = {
359+
"use_base2_exp": use_base2_exp,
360+
"use_experimental_scheduler": use_experimental_scheduler,
361+
"ulysses_shards": ulysses_shards,
362+
"ulysses_attention_chunks": ulysses_attention_chunks,
363+
"use_svg_attention": False,
364+
"svg_implementation": "official_svg",
365+
"svg_spatial_density": 0.25,
366+
"svg_sample_max_row": 10000,
367+
"svg_profile_query_count": 64,
368+
"svg_profile_seed": 0,
369+
"svg_dense_layer_fraction": 0.0,
370+
"svg_dense_timestep_fraction": 0.0,
371+
"svg_active_start_step": -1,
372+
"svg_active_end_step": -1,
373+
"svg_active_start_layer": -1,
374+
"svg_active_end_layer": -1,
375+
"svg_num_train_timesteps": 1000,
376+
"svg_num_layers": 48,
377+
"svg_include_first_frame": True,
378+
"svg_global_stride": 0,
379+
"svg_global_offset": 0,
380+
"svg_high_noise_density": -1.0,
381+
"svg_low_noise_density": -1.0,
382+
"svg_flash_block_sizes": None,
383+
**(attention_config or {}),
384+
}
385+
386+
self.is_self_attention = context_dim is None
387+
self.use_svg_attention = bool(attention_config["use_svg_attention"]) and self.is_self_attention
388+
self.svg_implementation = attention_config["svg_implementation"]
389+
self.svg_spatial_density = attention_config["svg_spatial_density"]
390+
self.svg_sample_max_row = attention_config["svg_sample_max_row"]
391+
self.svg_profile_query_count = attention_config["svg_profile_query_count"]
392+
self.svg_profile_seed = attention_config["svg_profile_seed"]
393+
self.svg_dense_layer_fraction = attention_config["svg_dense_layer_fraction"]
394+
self.svg_dense_timestep_fraction = attention_config["svg_dense_timestep_fraction"]
395+
self.svg_active_start_step = attention_config["svg_active_start_step"]
396+
self.svg_active_end_step = attention_config["svg_active_end_step"]
397+
self.svg_active_start_layer = attention_config["svg_active_start_layer"]
398+
self.svg_active_end_layer = attention_config["svg_active_end_layer"]
399+
self.svg_num_train_timesteps = attention_config["svg_num_train_timesteps"]
400+
self.svg_num_layers = attention_config["svg_num_layers"]
401+
self.svg_include_first_frame = attention_config["svg_include_first_frame"]
402+
self.svg_global_stride = attention_config["svg_global_stride"]
403+
self.svg_global_offset = attention_config["svg_global_offset"]
404+
self.svg_high_noise_density = attention_config["svg_high_noise_density"]
405+
self.svg_low_noise_density = attention_config["svg_low_noise_density"]
406+
self.svg_flash_block_sizes = attention_config["svg_flash_block_sizes"]
407+
356408
self.heads = heads
357409
self.rope_type = rope_type
358410
self.dim_head = dim_head
@@ -542,10 +594,22 @@ def __call__(
542594
k_rotary_emb: Optional[Tuple[Array, Array]] = None,
543595
perturbation_mask: Optional[Array] = None,
544596
cached_kv: Optional[Tuple[Array, Array]] = None,
597+
deterministic: bool = True,
598+
spatiotemporal_shape: Optional[Tuple[int, int, int]] = None,
599+
svg_layer_index: Optional[int | jax.Array] = None,
600+
svg_timestep: Optional[int | float | jax.Array] = None,
601+
svg_step_index: Optional[int | jax.Array] = None,
545602
) -> Array:
546603
# Determine context (Self or Cross)
604+
is_self_attention = encoder_hidden_states is None
547605
context = encoder_hidden_states if encoder_hidden_states is not None else hidden_states
548606

607+
if self.use_svg_attention and is_self_attention:
608+
if not deterministic:
609+
raise ValueError("SVG attention supports deterministic inference only.")
610+
if spatiotemporal_shape is None:
611+
raise ValueError("SVG attention requires spatiotemporal_shape.")
612+
549613
# 1. Project and Norm
550614
with self.named_scope("QKV Projection"):
551615
query = self.to_q(hidden_states)
@@ -586,8 +650,66 @@ def __call__(
586650

587651
with self.named_scope("Attention and Output Project"):
588652
# 4. Attention
589-
# NNXAttentionOp expects flattened input [B, S, InnerDim] for flash kernel
590-
attn_output = self.attention_op.apply_attention(query=query, key=key, value=value, attention_mask=attention_mask)
653+
if self.use_svg_attention and is_self_attention and spatiotemporal_shape is not None:
654+
is_active = svg_attention.is_svg_active(
655+
step_index=svg_step_index,
656+
layer_index=svg_layer_index,
657+
timestep=svg_timestep,
658+
start_step=self.svg_active_start_step,
659+
end_step=self.svg_active_end_step,
660+
start_layer=self.svg_active_start_layer,
661+
end_layer=self.svg_active_end_layer,
662+
dense_layer_fraction=self.svg_dense_layer_fraction,
663+
dense_timestep_fraction=self.svg_dense_timestep_fraction,
664+
num_train_timesteps=self.svg_num_train_timesteps,
665+
num_layers=self.svg_num_layers,
666+
)
667+
668+
def run_dense(_):
669+
return self.attention_op.apply_attention(
670+
query=query,
671+
key=key,
672+
value=value,
673+
attention_mask=attention_mask,
674+
)
675+
676+
def run_sparse_svg(_):
677+
execution_band_width = svg_attention.svg_execution_band_width(
678+
spatiotemporal_shape,
679+
self.svg_spatial_density,
680+
)
681+
sparse_config = {
682+
"use_svg_attention": True,
683+
"mask_type": "svg_spatial",
684+
"band_width": execution_band_width,
685+
"include_first_frame": self.svg_include_first_frame,
686+
"global_stride": self.svg_global_stride,
687+
"global_offset": self.svg_global_offset,
688+
"profile_query_count": self.svg_profile_query_count,
689+
"profile_seed": self.svg_profile_seed,
690+
"sample_max_row": self.svg_sample_max_row,
691+
"custom_flash_block_sizes": self.svg_flash_block_sizes,
692+
"svg_step_index": svg_step_index,
693+
"svg_layer_index": svg_layer_index,
694+
"svg_timestep": svg_timestep,
695+
}
696+
return self.attention_op.apply_attention(
697+
query=query,
698+
key=key,
699+
value=value,
700+
attention_mask=attention_mask,
701+
spatiotemporal_shape=spatiotemporal_shape,
702+
sparse_config_override=sparse_config,
703+
)
704+
705+
with self.named_scope("apply_attention"):
706+
if isinstance(is_active, bool):
707+
attn_output = run_sparse_svg(None) if is_active else run_dense(None)
708+
else:
709+
attn_output = jax.lax.cond(is_active, run_sparse_svg, run_dense, operand=None)
710+
else:
711+
with self.named_scope("apply_attention"):
712+
attn_output = self.attention_op.apply_attention(query=query, key=key, value=value, attention_mask=attention_mask)
591713

592714
if perturbation_mask is not None:
593715
# value is [B, S, InnerDim]

0 commit comments

Comments
 (0)