---
title: action_module
---

<div class="python-reference">
<a id="fastvideo.models.dits.matrixgame2.action_module-classes"></a>
<a id="fastvideo.models.dits.matrixgame2.action_module.ActionModule-functions"></a>
<a id="fastvideo.models.dits.matrixgame2.action_module"></a>

<h2 id="fastvideo.models.dits.matrixgame2.action_module.LocalAttention"><code>fastvideo.models.dits.matrixgame2.action_module.LocalAttention</code></h2>
<pre class="doc-signature"><code>LocalAttention(num_heads: int, head_size: int, num_kv_heads: int | None = None, softmax_scale: float | None = None, causal: bool = False, supported_attention_backends: tuple&#91;AttentionBackendEnum, ...&#93; | None = None, default_backend: AttentionBackendEnum | None = None, **extra_impl_args)</code></pre>
<p>Attention layer.</p>

<h3 id="fastvideo.models.dits.matrixgame2.action_module.LocalAttention.forward"><code>fastvideo.models.dits.matrixgame2.action_module.LocalAttention.forward</code></h3>
<pre class="doc-signature"><code>forward(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, freqs_cis: tuple&#91;torch.Tensor, torch.Tensor&#93; | None = None) -&gt; torch.Tensor</code></pre>
<p>Apply local attention between query, key and value tensors.</p>
<h4>Parameters</h4>
<dl>
<dt><code>q: torch.Tensor</code></dt><dd><p>Query tensor of shape [batch_size, seq_len, num_heads, head_dim]</p></dd>
<dt><code>k: torch.Tensor</code></dt><dd><p>Key tensor of shape [batch_size, seq_len, num_heads, head_dim] </p></dd>
<dt><code>v: torch.Tensor</code></dt><dd><p>Value tensor of shape [batch_size, seq_len, num_heads, head_dim]</p></dd>
</dl>
<h4>Returns</h4>
<dl>
<dt><code>torch.Tensor</code></dt><dd><p>torch.Tensor: Output tensor after local attention</p></dd>
</dl>
<h2 id="fastvideo.models.dits.matrixgame2.action_module.ReplicatedLinear"><code>fastvideo.models.dits.matrixgame2.action_module.ReplicatedLinear</code></h2>
<pre class="doc-signature"><code>ReplicatedLinear(input_size: int, output_size: int, bias: bool = True, skip_bias_add: bool = False, params_dtype: torch.dtype | None = None, quant_config: QuantizationConfig | None = None, prefix: str = &#x27;&#x27;)</code></pre>
<p>Replicated linear layer.</p>
<h3>Parameters</h3>
<dl>
<dt><code>input_size: int</code></dt><dd><p>input dimension of the linear layer.</p></dd>
<dt><code>output_size: int</code></dt><dd><p>output dimension of the linear layer.</p></dd>
<dt><code>bias: bool</code></dt><dd><p>If true, add bias.</p></dd>
<dt><code>skip_bias_add: bool</code></dt><dd><p>If true, skip adding bias but instead return it.</p></dd>
<dt><code>params_dtype: torch.dtype | None</code></dt><dd><p>Data type for the parameters.</p></dd>
<dt><code>quant_config: QuantizationConfig | None</code></dt><dd><p>Quantization configure.</p></dd>
<dt><code>prefix: str</code></dt><dd><p>The name of the layer in the state dict, including all parents
            (e.g. model.layers.0.qkv_proj)</p></dd>
</dl>

<h3 id="fastvideo.models.dits.matrixgame2.action_module.ReplicatedLinear.weight_loader"><code>fastvideo.models.dits.matrixgame2.action_module.ReplicatedLinear.weight_loader</code></h3>
<pre class="doc-signature"><code>weight_loader(param: Parameter, loaded_weight: torch.Tensor) -&gt; None</code></pre>

<h3 id="fastvideo.models.dits.matrixgame2.action_module.ReplicatedLinear.forward"><code>fastvideo.models.dits.matrixgame2.action_module.ReplicatedLinear.forward</code></h3>
<pre class="doc-signature"><code>forward(x: torch.Tensor) -&gt; tuple&#91;torch.Tensor, Parameter | None&#93;</code></pre>

<h3 id="fastvideo.models.dits.matrixgame2.action_module.ReplicatedLinear.extra_repr"><code>fastvideo.models.dits.matrixgame2.action_module.ReplicatedLinear.extra_repr</code></h3>
<pre class="doc-signature"><code>extra_repr() -&gt; str</code></pre>

<h3 id="fastvideo.models.dits.matrixgame2.action_module.ReplicatedLinear.get_shape_mapping"><code>fastvideo.models.dits.matrixgame2.action_module.ReplicatedLinear.get_shape_mapping</code></h3>
<pre class="doc-signature"><code>get_shape_mapping() -&gt; dict</code></pre>
<p>Get the mapping from (input_shape, output_shape) to layer types.</p>
<h3 id="fastvideo.models.dits.matrixgame2.action_module.ReplicatedLinear.reset_shape_tracking"><code>fastvideo.models.dits.matrixgame2.action_module.ReplicatedLinear.reset_shape_tracking</code></h3>
<pre class="doc-signature"><code>reset_shape_tracking() -&gt; None</code></pre>
<p>Clear tracked shapes and layer type mappings.</p>
<h3 id="fastvideo.models.dits.matrixgame2.action_module.ReplicatedLinear.print_shape_summary"><code>fastvideo.models.dits.matrixgame2.action_module.ReplicatedLinear.print_shape_summary</code></h3>
<pre class="doc-signature"><code>print_shape_summary() -&gt; None</code></pre>
<p>Log a summary of all unique shapes and their layer types.</p>
<h2 id="fastvideo.models.dits.matrixgame2.action_module.FP32LayerNorm"><code>fastvideo.models.dits.matrixgame2.action_module.FP32LayerNorm</code></h2>
<pre class="doc-signature"><code>FP32LayerNorm()</code></pre>

<h3 id="fastvideo.models.dits.matrixgame2.action_module.FP32LayerNorm.forward"><code>fastvideo.models.dits.matrixgame2.action_module.FP32LayerNorm.forward</code></h3>
<pre class="doc-signature"><code>forward(inputs: torch.Tensor) -&gt; torch.Tensor</code></pre>

<h2 id="fastvideo.models.dits.matrixgame2.action_module.RMSNorm"><code>fastvideo.models.dits.matrixgame2.action_module.RMSNorm</code></h2>
<pre class="doc-signature"><code>RMSNorm(hidden_size: int, eps: float = 1e-06, dtype: torch.dtype = torch.float32, var_hidden_size: int | None = None, has_weight: bool = True)</code></pre>
<p>Root mean square normalization.</p>
<p>Computes x -&gt; w * x / sqrt(E[x^2] + eps) where w is the learned weight.
Refer to https://arxiv.org/abs/1910.07467</p>

<h3 id="fastvideo.models.dits.matrixgame2.action_module.RMSNorm.forward_native"><code>fastvideo.models.dits.matrixgame2.action_module.RMSNorm.forward_native</code></h3>
<pre class="doc-signature"><code>forward_native(x: torch.Tensor, residual: torch.Tensor | None = None) -&gt; torch.Tensor | tuple&#91;torch.Tensor, torch.Tensor&#93;</code></pre>
<p>PyTorch-native implementation equivalent to forward().</p>
<h3 id="fastvideo.models.dits.matrixgame2.action_module.RMSNorm.extra_repr"><code>fastvideo.models.dits.matrixgame2.action_module.RMSNorm.extra_repr</code></h3>
<pre class="doc-signature"><code>extra_repr() -&gt; str</code></pre>

<h2 id="fastvideo.models.dits.matrixgame2.action_module.AttentionBackendEnum"><code>fastvideo.models.dits.matrixgame2.action_module.AttentionBackendEnum</code></h2>
<pre class="doc-signature"><code>AttentionBackendEnum()</code></pre>

<h2 id="fastvideo.models.dits.matrixgame2.action_module.retain_kv_with_sink"><code>fastvideo.models.dits.matrixgame2.action_module.retain_kv_with_sink</code></h2>
<pre class="doc-signature"><code>retain_kv_with_sink(k: torch.Tensor, v: torch.Tensor, target_len: int, sink_size: int) -&gt; tuple&#91;torch.Tensor, torch.Tensor&#93;</code></pre>

<h2 id="fastvideo.models.dits.matrixgame2.action_module.ActionModule"><code>fastvideo.models.dits.matrixgame2.action_module.ActionModule</code></h2>
<pre class="doc-signature"><code>ActionModule(mouse_dim_in: int = 2, keyboard_dim_in: int = 6, hidden_size: int = 128, img_hidden_size: int = 1536, keyboard_hidden_dim: int = 1024, mouse_hidden_dim: int = 1024, vae_time_compression_ratio: int = 4, windows_size: int = 3, heads_num: int = 16, patch_size: list | None = None, qk_norm: bool = True, qkv_bias: bool = False, rope_dim_list: list | None = None, rope_theta = 256, mouse_qk_dim_list: list | None = None, enable_mouse = True, enable_keyboard = True, local_attn_size = 6, sink_size = 0, blocks: list | None = None)</code></pre>
<p>action module from https://arxiv.org/pdf/2501.08325</p>

<details><summary>Source</summary><pre><code>class ActionModule(nn.Module):
    &quot;&quot;&quot;
    action module from https://arxiv.org/pdf/2501.08325
    &quot;&quot;&quot;

    def __init__(
        self,
        mouse_dim_in: int = 2,
        keyboard_dim_in: int = 6,
        hidden_size: int = 128,
        img_hidden_size: int = 1536,
        keyboard_hidden_dim: int = 1024,
        mouse_hidden_dim: int = 1024,
        vae_time_compression_ratio: int = 4,
        windows_size: int = 3,
        heads_num: int = 16,
        patch_size: list | None = None,
        qk_norm: bool = True,
        qkv_bias: bool = False,
        rope_dim_list: list | None = None,
        rope_theta=256,
        mouse_qk_dim_list: list | None = None,
        enable_mouse=True,
        enable_keyboard=True,
        local_attn_size=6,
        sink_size=0,
        blocks: list | None = None,
    ):
        super().__init__()
        # Initialize mutable defaults
        patch_size = patch_size if patch_size is not None else &#91;1, 2, 2&#93;
        rope_dim_list = (rope_dim_list if rope_dim_list is not None else &#91;8, 28, 28&#93;)
        mouse_qk_dim_list = (mouse_qk_dim_list if mouse_qk_dim_list is not None else &#91;8, 28, 28&#93;)
        blocks = blocks if blocks is not None else &#91;&#93;
        self.local_attn_size = local_attn_size
        self.sink_size = sink_size
        self.enable_mouse = enable_mouse
        self.enable_keyboard = enable_keyboard

        self.rope_dim_list = rope_dim_list
        self.rope_theta = rope_theta
        if self.enable_keyboard:
            self.keyboard_embed = nn.Sequential(
                nn.Linear(keyboard_dim_in, hidden_size, bias=True),
                nn.SiLU(),
                nn.Linear(hidden_size, hidden_size, bias=True),
            )

        self.mouse_qk_dim_list = mouse_qk_dim_list
        self.heads_num = heads_num
        if self.enable_mouse:
            c = mouse_hidden_dim
            self.mouse_mlp = nn.Sequential(
                nn.Linear(
                    mouse_dim_in * vae_time_compression_ratio * windows_size + img_hidden_size,
                    c,
                    bias=True,
                ),
                nn.GELU(approximate=&quot;tanh&quot;),
                nn.Linear(c, c),
                FP32LayerNorm(c, elementwise_affine=True),
            )

            head_dim = c // heads_num
            self.t_qkv = ReplicatedLinear(c, c * 3, bias=qkv_bias)
            self.img_attn_q_norm = (RMSNorm(head_dim, eps=1e-6) if qk_norm else nn.Identity())
            self.img_attn_k_norm = (RMSNorm(head_dim, eps=1e-6) if qk_norm else nn.Identity())
            self.proj_mouse = ReplicatedLinear(c, img_hidden_size, bias=qkv_bias)

        if self.enable_keyboard:
            head_dim_key = keyboard_hidden_dim // heads_num
            self.key_attn_q_norm = (RMSNorm(head_dim_key, eps=1e-6) if qk_norm else nn.Identity())
            self.key_attn_k_norm = (RMSNorm(head_dim_key, eps=1e-6) if qk_norm else nn.Identity())

            self.mouse_attn_q = ReplicatedLinear(img_hidden_size, keyboard_hidden_dim, bias=qkv_bias)
            self.keyboard_attn_kv = ReplicatedLinear(
                hidden_size * windows_size * vae_time_compression_ratio,
                keyboard_hidden_dim * 2,
                bias=qkv_bias,
            )
            self.proj_keyboard = ReplicatedLinear(keyboard_hidden_dim, img_hidden_size, bias=qkv_bias)

        self.mouse_attn_layer = (LocalAttention(
            num_heads=heads_num,
            head_size=mouse_hidden_dim // heads_num,
            causal=False,
            supported_attention_backends=(
                AttentionBackendEnum.FLASH_ATTN,
                AttentionBackendEnum.TORCH_SDPA,
            ),
        ) if self.enable_mouse else None)

        self.keyboard_attn_layer = (LocalAttention(
            num_heads=heads_num,
            head_size=keyboard_hidden_dim // heads_num,
            causal=False,
            supported_attention_backends=(
                AttentionBackendEnum.FLASH_ATTN,
                AttentionBackendEnum.TORCH_SDPA,
            ),
        ) if self.enable_keyboard else None)

        self.vae_time_compression_ratio = vae_time_compression_ratio
        self.windows_size = windows_size
        self.patch_size = patch_size
        # Lazy initialization: freqs will be created on first forward pass
        self._freqs_cos = None
        self._freqs_sin = None

    def patchify(self, x, patch_size):
        &quot;&quot;&quot;
        x : (N C T H W)
        &quot;&quot;&quot;
        pt, ph, pw = self.patch_size
        t, h, w = x.shape&#91;2&#93; // pt, x.shape&#91;3&#93; // ph, x.shape&#91;4&#93; // pw
        c = x.shape&#91;1&#93;
        x = x.reshape(shape=(x.shape&#91;0&#93;, c, t, pt, h, ph, w, pw))
        x = torch.einsum(&quot;nctohpwq-&gt;nthwcopq&quot;, x)
        x = x.reshape(shape=(x.shape&#91;0&#93;, t * h * w, c * pt * ph * pw))
        return x

    def unpatchify(self, x, t, h, w, patch_size):
        &quot;&quot;&quot;
        x: (N, T, patch_size**2 * C)
        imgs: (N, H, W, C)
        &quot;&quot;&quot;
        c = x.shape&#91;2&#93; // patch_size  # self.unpatchify_channels
        pt, ph, pw = self.patch_size
        assert t * h * w == x.shape&#91;1&#93;

        x = x.reshape(shape=(x.shape&#91;0&#93;, t, h, w, c, pt, ph, pw))
        x = torch.einsum(&quot;nthwcopq-&gt;nctohpwq&quot;, x)
        imgs = x.reshape(shape=(x.shape&#91;0&#93;, c, t * pt, h * ph, w * pw))

        return imgs

    def get_rotary_pos_embed(
        self,
        video_length,
        height,
        width,
        head_dim,
        rope_dim_list=None,
        start_offset=0,
    ):
        target_ndim = 3
        ndim = 5 - 2

        latents_size = &#91;video_length + start_offset, height, width&#93;

        if isinstance(self.patch_size, int):
            assert all(s % self.patch_size == 0 for s in latents_size), (
                f&quot;Latent size(last &#123;ndim&#125; dimensions) should be divisible by patch size(&#123;self.patch_size&#125;), &quot;
                f&quot;but got &#123;latents_size&#125;.&quot;)
            rope_sizes = &#91;s // self.patch_size for s in latents_size&#93;
        elif isinstance(self.patch_size, list):
            assert all(s % self.patch_size&#91;idx&#93; == 0 for idx, s in enumerate(latents_size)), (
                f&quot;Latent size(last &#123;ndim&#125; dimensions) should be divisible by patch size(&#123;self.patch_size&#125;), &quot;
                f&quot;but got &#123;latents_size&#125;.&quot;)
            rope_sizes = &#91;s // self.patch_size&#91;idx&#93; for idx, s in enumerate(latents_size)&#93;

        if len(rope_sizes) != target_ndim:
            rope_sizes = &#91;1&#93; * (target_ndim - len(rope_sizes)) + rope_sizes  # time axis

        if rope_dim_list is None:
            rope_dim_list = &#91;head_dim // target_ndim for _ in range(target_ndim)&#93;
        assert sum(rope_dim_list) == head_dim, (&quot;sum(rope_dim_list) should equal to head_dim of attention layer&quot;)
        # Use Matrix-Game wrapper for FastVideo&#x27;s function
        freqs_cos, freqs_sin = _get_nd_rotary_pos_embed_matrixgame2(
            rope_dim_list,
            rope_sizes,
            theta=self.rope_theta,
            theta_rescale_factor=1,
        )
        return freqs_cos&#91;-video_length * rope_sizes&#91;1&#93; * rope_sizes&#91;2&#93; //
                         self.patch_size&#91;0&#93;:&#93;, freqs_sin&#91;-video_length * rope_sizes&#91;1&#93; * rope_sizes&#91;2&#93; //
                                                         self.patch_size&#91;0&#93;:&#93;

    def _forward_mouse(
        self,
        hidden_states: torch.Tensor,
        mouse_condition: torch.Tensor,
        *,
        is_causal: bool,
        kv_cache_mouse: dict&#91;str, torch.Tensor&#93; | None,
        pad_t: int,
        num_frame_per_block: int,
        block_mask_mouse: BlockMask | None,
        start_frame: int,
        freqs_cis: tuple&#91;torch.Tensor, torch.Tensor&#93;,
        N_feats: int,
        B: int,
        C: int,
        tt: int,
        th: int,
        tw: int,
    ) -&gt; torch.Tensor:
        pad = mouse_condition&#91;:, 0:1, :&#93;.expand(-1, pad_t, -1)
        mouse_condition = torch.cat(&#91;pad, mouse_condition&#93;, dim=1)
        if is_causal and kv_cache_mouse is not None:
            mouse_condition = mouse_condition&#91;
                :,
                self.vae_time_compression_ratio * (N_feats - num_frame_per_block - self.windows_size) + pad_t:,
                :,
            &#93;
            group_mouse = &#91;
                mouse_condition&#91;
                    :,
                    self.vae_time_compression_ratio * (i - self.windows_size) +
                    pad_t:i * self.vae_time_compression_ratio + pad_t,
                    :,
                &#93; for i in range(num_frame_per_block)
            &#93;
        else:
            local_num_frames = tt
            group_mouse = &#91;
                mouse_condition&#91;
                    :,
                    self.vae_time_compression_ratio * (i - self.windows_size) +
                    pad_t:i * self.vae_time_compression_ratio + pad_t,
                    :,
                &#93; for i in range(local_num_frames)
            &#93;

        group_mouse = torch.stack(group_mouse, dim=1)
        actual_num_frames = group_mouse.shape&#91;1&#93;  # Use actual stacked frame count

        S = th * tw
        group_mouse = group_mouse.unsqueeze(-1).expand(B, actual_num_frames, pad_t, C, S)
        group_mouse = group_mouse.permute(0, 4, 1, 2, 3).reshape(B * S, actual_num_frames, pad_t * C)

        group_mouse = torch.cat(&#91;hidden_states, group_mouse&#93;, dim=-1)
        group_mouse = self.mouse_mlp(group_mouse)
        # qkv
        mouse_qkv, _ = self.t_qkv(group_mouse)
        q, k, v = rearrange(mouse_qkv, &quot;B L (K H D) -&gt; K B L H D&quot;, K=3, H=self.heads_num)  # BHW F H C
        q = self.img_attn_q_norm(q).to(v)
        k = self.img_attn_k_norm(k).to(v)
        # rope embd

        # freqs_cis = (self.freqs_cos, self.freqs_sin)

        q, k = _apply_rotary_emb_qk(q, k, freqs_cis&#91;0&#93;, freqs_cis&#91;1&#93;, start_offset=start_frame)
        ## TODO: adding cache here
        if is_causal:
            if kv_cache_mouse is None:
                assert (q.shape&#91;0&#93; == k.shape&#91;0&#93; and q.shape&#91;0&#93; % S == 0)  # == 880, f&quot;&#123;q.shape&#91;0&#93;&#125;,&#123;k.shape&#91;0&#93;&#125;&quot;
                padded_length = math.ceil(q.shape&#91;1&#93; / 32) * 32 - q.shape&#91;1&#93;
                padded_q = _padding_q_k_v(q, padded_length)
                padded_k = _padding_q_k_v(k, padded_length)
                padded_v = _padding_q_k_v(v, padded_length)
                attn = flex_attention(
                    query=padded_q.transpose(2, 1),  # after: B, HW, F, C
                    key=padded_k.transpose(2, 1),
                    value=padded_v.transpose(2, 1),
                    block_mask=block_mask_mouse,
                )&#91;:, :, :-padded_length&#93;.transpose(2, 1)
            else:
                attn = _update_kv_cache_and_attend(
                    q,
                    k,
                    v,
                    kv_cache_mouse,
                    self.mouse_attn_layer,
                    start_frame,
                    num_frame_per_block,
                    self.local_attn_size,
                    self.sink_size,
                    use_k_for_num_tokens=False,
                )
        else:
            attn = self.mouse_attn_layer(q, k, v)
        # Compute cu_squlens and max_seqlen for flash attention
        # qk norm
        attn = rearrange(attn, &quot;(b S) T h d -&gt; b (T S) (h d)&quot;, b=B)
        attn, _ = self.proj_mouse(attn)
        return attn

    def _forward_keyboard(
        self,
        hidden_states: torch.Tensor,
        keyboard_condition: torch.Tensor,
        *,
        is_causal: bool,
        use_rope_keyboard: bool,
        kv_cache_keyboard: dict&#91;str, torch.Tensor&#93; | None,
        pad_t: int,
        num_frame_per_block: int,
        block_mask_keyboard: BlockMask | None,
        start_frame: int,
        freqs_cis: tuple&#91;torch.Tensor, torch.Tensor&#93;,
        N_feats: int,
        B: int,
        tt: int,
        th: int,
        tw: int,
    ) -&gt; torch.Tensor:
        pad = keyboard_condition&#91;:, 0:1, :&#93;.expand(-1, pad_t, -1)
        keyboard_condition = torch.cat(&#91;pad, keyboard_condition&#93;, dim=1)
        if is_causal and kv_cache_keyboard is not None:
            keyboard_condition = keyboard_condition&#91;
                :,
                self.vae_time_compression_ratio * (N_feats - num_frame_per_block - self.windows_size) + pad_t:,
                :,
            &#93;  # keyboard_condition&#91;:, self.vae_time_compression_ratio*(start_frame - self.windows_size) + pad_t:start_frame * self.vae_time_compression_ratio + pad_t,:&#93;
            keyboard_condition = self.keyboard_embed(keyboard_condition)
            group_keyboard = &#91;
                keyboard_condition&#91;
                    :,
                    self.vae_time_compression_ratio * (i - self.windows_size) +
                    pad_t:i * self.vae_time_compression_ratio + pad_t,
                    :,
                &#93; for i in range(num_frame_per_block)
            &#93;
        else:
            keyboard_condition = self.keyboard_embed(keyboard_condition)
            local_num_frames = tt
            group_keyboard = &#91;
                keyboard_condition&#91;
                    :,
                    self.vae_time_compression_ratio * (i - self.windows_size) +
                    pad_t:i * self.vae_time_compression_ratio + pad_t,
                    :,
                &#93; for i in range(local_num_frames)
            &#93;
        group_keyboard = torch.stack(group_keyboard, dim=1)  # B F RW C
        group_keyboard = group_keyboard.reshape(shape=(group_keyboard.shape&#91;0&#93;, group_keyboard.shape&#91;1&#93;, -1))
        # apply cross attn
        mouse_q, _ = self.mouse_attn_q(hidden_states)
        keyboard_kv, _ = self.keyboard_attn_kv(group_keyboard)

        B, L, HD = mouse_q.shape
        D = HD // self.heads_num
        q = mouse_q.view(B, L, self.heads_num, D)

        B, L, KHD = keyboard_kv.shape
        k, v = keyboard_kv.view(B, L, 2, self.heads_num, D).permute(2, 0, 1, 3, 4)

        # Compute cu_squlens and max_seqlen for flash attention
        # qk norm

        q = self.key_attn_q_norm(q).to(v)
        k = self.key_attn_k_norm(k).to(v)
        S = th * tw
        # assert S == 880
        # position embed
        if use_rope_keyboard:
            B, TS, H, D = q.shape
            T_ = TS // S
            q = q.view(B, T_, S, H, D).transpose(1, 2).reshape(B * S, T_, H, D)
            q, k = _apply_rotary_emb_qk(q, k, freqs_cis&#91;0&#93;, freqs_cis&#91;1&#93;, start_offset=start_frame)

            k = k.repeat_interleave(S, dim=0)
            v = v.repeat_interleave(S, dim=0)

            if is_causal:
                if kv_cache_keyboard is None:
                    assert q.shape&#91;0&#93; == k.shape&#91;0&#93; and q.shape&#91;0&#93; % S == 0

                    padded_length = math.ceil(q.shape&#91;1&#93; / 32) * 32 - q.shape&#91;1&#93;
                    padded_q = _padding_q_k_v(q, padded_length)
                    padded_k = _padding_q_k_v(k, padded_length)
                    padded_v = _padding_q_k_v(v, padded_length)
                    attn = flex_attention(
                        query=padded_q.transpose(2, 1),  # after: B, HW, F, C
                        key=padded_k.transpose(2, 1),
                        value=padded_v.transpose(2, 1),
                        block_mask=block_mask_keyboard,
                    )&#91;:, :, :-padded_length&#93;.transpose(2, 1)
                else:
                    assert (k.shape&#91;0&#93; == S)  # BS == 1 or the cache should not be saved/ load method should be modified
                    attn = _update_kv_cache_and_attend(
                        q,
                        k,
                        v,
                        kv_cache_keyboard,
                        self.keyboard_attn_layer,
                        start_frame,
                        num_frame_per_block,
                        self.local_attn_size,
                        self.sink_size,
                        use_k_for_num_tokens=True,
                        store_first_only=True,
                        repeat_factor=S,
                    )
            else:
                attn = self.keyboard_attn_layer(q, k, v)
            attn = rearrange(attn, &quot;(B S) T H D -&gt; B (T S) (H D)&quot;, S=S)
        else:
            if is_causal:
                if kv_cache_keyboard is None:
                    padded_length = math.ceil(q.shape&#91;1&#93; / 32) * 32 - q.shape&#91;1&#93;
                    padded_q = _padding_q_k_v(q, padded_length)
                    padded_k = _padding_q_k_v(k, padded_length)
                    padded_v = _padding_q_k_v(v, padded_length)
                    attn = flex_attention(
                        query=padded_q.transpose(2, 1),  # after: B, HW, F, C
                        key=padded_k.transpose(2, 1),
                        value=padded_v.transpose(2, 1),
                        block_mask=block_mask_keyboard,
                    )&#91;:, :, :-padded_length&#93;.transpose(2, 1)
                else:
                    attn = _update_kv_cache_and_attend(
                        q,
                        k,
                        v,
                        kv_cache_keyboard,
                        self.keyboard_attn_layer,
                        start_frame,
                        num_frame_per_block,
                        self.local_attn_size,
                        self.sink_size,
                        use_k_for_num_tokens=True,
                    )
            else:
                attn = self.keyboard_attn_layer(q, k, v)
            attn = rearrange(attn, &quot;B L H D -&gt; B L (H D)&quot;)
        attn, _ = self.proj_keyboard(attn)
        return attn

    def forward(
        self,
        x: torch.Tensor,
        tt: int,
        th: int,
        tw: int,
        mouse_condition: torch.Tensor | None = None,
        keyboard_condition: torch.Tensor | None = None,
        block_mask_mouse: BlockMask | None = None,
        block_mask_keyboard: BlockMask | None = None,
        is_causal: bool = False,
        kv_cache_mouse: dict&#91;str, torch.Tensor&#93; | None = None,
        kv_cache_keyboard: dict&#91;str, torch.Tensor&#93; | None = None,
        start_frame: int = 0,
        use_rope_keyboard: bool = True,
        num_frame_per_block: int = 3,
    ):
        &quot;&quot;&quot;
        hidden_states: B, tt*th*tw, C
        mouse_condition: B, N_frames, C1
        keyboard_condition: B, N_frames, C2
        &quot;&quot;&quot;
        assert use_rope_keyboard

        target_device = x.device
        target_dtype = x.dtype
        if mouse_condition is not None:
            mouse_condition = mouse_condition.to(device=target_device, dtype=target_dtype)
        if keyboard_condition is not None:
            keyboard_condition = keyboard_condition.to(device=target_device, dtype=target_dtype)
        else:
            return x

        B, N_frames, C = keyboard_condition.shape
        assert tt * th * tw == x.shape&#91;1&#93;
        assert ((N_frames - 1) + self.vae_time_compression_ratio) % self.vae_time_compression_ratio == 0
        N_feats = int((N_frames - 1) / self.vae_time_compression_ratio) + 1

        # Lazy initialization of freqs on first forward pass
        if self._freqs_cos is None or self._freqs_sin is None:
            self._freqs_cos, self._freqs_sin = self.get_rotary_pos_embed(
                7500,
                self.patch_size&#91;1&#93;,
                self.patch_size&#91;2&#93;,
                64,
                self.mouse_qk_dim_list,
                start_offset=0,
            )

        # Defined freqs_cis early so it&#x27;s available for both mouse and keyboard
        freqs_cis = (self._freqs_cos, self._freqs_sin)

        if is_causal:
            assert (N_feats == tt and kv_cache_mouse is None) or ((N_frames - 1) // self.vae_time_compression_ratio + 1
                                                                  == start_frame + num_frame_per_block)
        # For non-causal (training), we trust that the caller provides correctly shaped inputs

        if self.enable_mouse and mouse_condition is not None:
            hidden_states = rearrange(x, &quot;B (T S) C -&gt; (B S) T C&quot;, T=tt,
                                      S=th * tw)  # 65*272*480 -&gt; 17*(272//16)*(480//16) -&gt; 8670
            B, N_frames, C = mouse_condition.shape
        else:
            hidden_states = x
        # padding

        pad_t = self.vae_time_compression_ratio * self.windows_size
        if self.enable_mouse and mouse_condition is not None:
            attn = self._forward_mouse(
                hidden_states,
                mouse_condition,
                is_causal=is_causal,
                kv_cache_mouse=kv_cache_mouse,
                pad_t=pad_t,
                num_frame_per_block=num_frame_per_block,
                block_mask_mouse=block_mask_mouse,
                start_frame=start_frame,
                freqs_cis=freqs_cis,
                N_feats=N_feats,
                B=B,
                C=C,
                tt=tt,
                th=th,
                tw=tw,
            )
            hidden_states = rearrange(x, &quot;(B S) T C -&gt; B (T S) C&quot;, B=B)

            hidden_states = hidden_states + attn

        if self.enable_keyboard and keyboard_condition is not None:
            attn = self._forward_keyboard(
                hidden_states,
                keyboard_condition,
                is_causal=is_causal,
                use_rope_keyboard=use_rope_keyboard,
                kv_cache_keyboard=kv_cache_keyboard,
                pad_t=pad_t,
                num_frame_per_block=num_frame_per_block,
                block_mask_keyboard=block_mask_keyboard,
                start_frame=start_frame,
                freqs_cis=freqs_cis,
                N_feats=N_feats,
                B=B,
                tt=tt,
                th=th,
                tw=tw,
            )
            hidden_states = hidden_states + attn
        return hidden_states</code></pre></details>
<h3 id="fastvideo.models.dits.matrixgame2.action_module.ActionModule.patchify"><code>fastvideo.models.dits.matrixgame2.action_module.ActionModule.patchify</code></h3>
<pre class="doc-signature"><code>patchify(x, patch_size)</code></pre>
<p>x : (N C T H W)</p>
<details><summary>Source</summary><pre><code>def patchify(self, x, patch_size):
    &quot;&quot;&quot;
    x : (N C T H W)
    &quot;&quot;&quot;
    pt, ph, pw = self.patch_size
    t, h, w = x.shape&#91;2&#93; // pt, x.shape&#91;3&#93; // ph, x.shape&#91;4&#93; // pw
    c = x.shape&#91;1&#93;
    x = x.reshape(shape=(x.shape&#91;0&#93;, c, t, pt, h, ph, w, pw))
    x = torch.einsum(&quot;nctohpwq-&gt;nthwcopq&quot;, x)
    x = x.reshape(shape=(x.shape&#91;0&#93;, t * h * w, c * pt * ph * pw))
    return x</code></pre></details>
<h3 id="fastvideo.models.dits.matrixgame2.action_module.ActionModule.unpatchify"><code>fastvideo.models.dits.matrixgame2.action_module.ActionModule.unpatchify</code></h3>
<pre class="doc-signature"><code>unpatchify(x, t, h, w, patch_size)</code></pre>
<p>x: (N, T, patch_size**2 * C)
imgs: (N, H, W, C)</p>
<details><summary>Source</summary><pre><code>def unpatchify(self, x, t, h, w, patch_size):
    &quot;&quot;&quot;
    x: (N, T, patch_size**2 * C)
    imgs: (N, H, W, C)
    &quot;&quot;&quot;
    c = x.shape&#91;2&#93; // patch_size  # self.unpatchify_channels
    pt, ph, pw = self.patch_size
    assert t * h * w == x.shape&#91;1&#93;

    x = x.reshape(shape=(x.shape&#91;0&#93;, t, h, w, c, pt, ph, pw))
    x = torch.einsum(&quot;nthwcopq-&gt;nctohpwq&quot;, x)
    imgs = x.reshape(shape=(x.shape&#91;0&#93;, c, t * pt, h * ph, w * pw))

    return imgs</code></pre></details>
<h3 id="fastvideo.models.dits.matrixgame2.action_module.ActionModule.get_rotary_pos_embed"><code>fastvideo.models.dits.matrixgame2.action_module.ActionModule.get_rotary_pos_embed</code></h3>
<pre class="doc-signature"><code>get_rotary_pos_embed(video_length, height, width, head_dim, rope_dim_list = None, start_offset = 0)</code></pre>

<details><summary>Source</summary><pre><code>def get_rotary_pos_embed(
    self,
    video_length,
    height,
    width,
    head_dim,
    rope_dim_list=None,
    start_offset=0,
):
    target_ndim = 3
    ndim = 5 - 2

    latents_size = &#91;video_length + start_offset, height, width&#93;

    if isinstance(self.patch_size, int):
        assert all(s % self.patch_size == 0 for s in latents_size), (
            f&quot;Latent size(last &#123;ndim&#125; dimensions) should be divisible by patch size(&#123;self.patch_size&#125;), &quot;
            f&quot;but got &#123;latents_size&#125;.&quot;)
        rope_sizes = &#91;s // self.patch_size for s in latents_size&#93;
    elif isinstance(self.patch_size, list):
        assert all(s % self.patch_size&#91;idx&#93; == 0 for idx, s in enumerate(latents_size)), (
            f&quot;Latent size(last &#123;ndim&#125; dimensions) should be divisible by patch size(&#123;self.patch_size&#125;), &quot;
            f&quot;but got &#123;latents_size&#125;.&quot;)
        rope_sizes = &#91;s // self.patch_size&#91;idx&#93; for idx, s in enumerate(latents_size)&#93;

    if len(rope_sizes) != target_ndim:
        rope_sizes = &#91;1&#93; * (target_ndim - len(rope_sizes)) + rope_sizes  # time axis

    if rope_dim_list is None:
        rope_dim_list = &#91;head_dim // target_ndim for _ in range(target_ndim)&#93;
    assert sum(rope_dim_list) == head_dim, (&quot;sum(rope_dim_list) should equal to head_dim of attention layer&quot;)
    # Use Matrix-Game wrapper for FastVideo&#x27;s function
    freqs_cos, freqs_sin = _get_nd_rotary_pos_embed_matrixgame2(
        rope_dim_list,
        rope_sizes,
        theta=self.rope_theta,
        theta_rescale_factor=1,
    )
    return freqs_cos&#91;-video_length * rope_sizes&#91;1&#93; * rope_sizes&#91;2&#93; //
                     self.patch_size&#91;0&#93;:&#93;, freqs_sin&#91;-video_length * rope_sizes&#91;1&#93; * rope_sizes&#91;2&#93; //
                                                     self.patch_size&#91;0&#93;:&#93;</code></pre></details>
<h3 id="fastvideo.models.dits.matrixgame2.action_module.ActionModule.forward"><code>fastvideo.models.dits.matrixgame2.action_module.ActionModule.forward</code></h3>
<pre class="doc-signature"><code>forward(x: torch.Tensor, tt: int, th: int, tw: int, mouse_condition: torch.Tensor | None = None, keyboard_condition: torch.Tensor | None = None, block_mask_mouse: BlockMask | None = None, block_mask_keyboard: BlockMask | None = None, is_causal: bool = False, kv_cache_mouse: dict&#91;str, torch.Tensor&#93; | None = None, kv_cache_keyboard: dict&#91;str, torch.Tensor&#93; | None = None, start_frame: int = 0, use_rope_keyboard: bool = True, num_frame_per_block: int = 3)</code></pre>
<p>hidden_states: B, tt<em>th</em>tw, C
mouse_condition: B, N_frames, C1
keyboard_condition: B, N_frames, C2</p>
<details><summary>Source</summary><pre><code>def forward(
    self,
    x: torch.Tensor,
    tt: int,
    th: int,
    tw: int,
    mouse_condition: torch.Tensor | None = None,
    keyboard_condition: torch.Tensor | None = None,
    block_mask_mouse: BlockMask | None = None,
    block_mask_keyboard: BlockMask | None = None,
    is_causal: bool = False,
    kv_cache_mouse: dict&#91;str, torch.Tensor&#93; | None = None,
    kv_cache_keyboard: dict&#91;str, torch.Tensor&#93; | None = None,
    start_frame: int = 0,
    use_rope_keyboard: bool = True,
    num_frame_per_block: int = 3,
):
    &quot;&quot;&quot;
    hidden_states: B, tt*th*tw, C
    mouse_condition: B, N_frames, C1
    keyboard_condition: B, N_frames, C2
    &quot;&quot;&quot;
    assert use_rope_keyboard

    target_device = x.device
    target_dtype = x.dtype
    if mouse_condition is not None:
        mouse_condition = mouse_condition.to(device=target_device, dtype=target_dtype)
    if keyboard_condition is not None:
        keyboard_condition = keyboard_condition.to(device=target_device, dtype=target_dtype)
    else:
        return x

    B, N_frames, C = keyboard_condition.shape
    assert tt * th * tw == x.shape&#91;1&#93;
    assert ((N_frames - 1) + self.vae_time_compression_ratio) % self.vae_time_compression_ratio == 0
    N_feats = int((N_frames - 1) / self.vae_time_compression_ratio) + 1

    # Lazy initialization of freqs on first forward pass
    if self._freqs_cos is None or self._freqs_sin is None:
        self._freqs_cos, self._freqs_sin = self.get_rotary_pos_embed(
            7500,
            self.patch_size&#91;1&#93;,
            self.patch_size&#91;2&#93;,
            64,
            self.mouse_qk_dim_list,
            start_offset=0,
        )

    # Defined freqs_cis early so it&#x27;s available for both mouse and keyboard
    freqs_cis = (self._freqs_cos, self._freqs_sin)

    if is_causal:
        assert (N_feats == tt and kv_cache_mouse is None) or ((N_frames - 1) // self.vae_time_compression_ratio + 1
                                                              == start_frame + num_frame_per_block)
    # For non-causal (training), we trust that the caller provides correctly shaped inputs

    if self.enable_mouse and mouse_condition is not None:
        hidden_states = rearrange(x, &quot;B (T S) C -&gt; (B S) T C&quot;, T=tt,
                                  S=th * tw)  # 65*272*480 -&gt; 17*(272//16)*(480//16) -&gt; 8670
        B, N_frames, C = mouse_condition.shape
    else:
        hidden_states = x
    # padding

    pad_t = self.vae_time_compression_ratio * self.windows_size
    if self.enable_mouse and mouse_condition is not None:
        attn = self._forward_mouse(
            hidden_states,
            mouse_condition,
            is_causal=is_causal,
            kv_cache_mouse=kv_cache_mouse,
            pad_t=pad_t,
            num_frame_per_block=num_frame_per_block,
            block_mask_mouse=block_mask_mouse,
            start_frame=start_frame,
            freqs_cis=freqs_cis,
            N_feats=N_feats,
            B=B,
            C=C,
            tt=tt,
            th=th,
            tw=tw,
        )
        hidden_states = rearrange(x, &quot;(B S) T C -&gt; B (T S) C&quot;, B=B)

        hidden_states = hidden_states + attn

    if self.enable_keyboard and keyboard_condition is not None:
        attn = self._forward_keyboard(
            hidden_states,
            keyboard_condition,
            is_causal=is_causal,
            use_rope_keyboard=use_rope_keyboard,
            kv_cache_keyboard=kv_cache_keyboard,
            pad_t=pad_t,
            num_frame_per_block=num_frame_per_block,
            block_mask_keyboard=block_mask_keyboard,
            start_frame=start_frame,
            freqs_cis=freqs_cis,
            N_feats=N_feats,
            B=B,
            tt=tt,
            th=th,
            tw=tw,
        )
        hidden_states = hidden_states + attn
    return hidden_states</code></pre></details>
</div>
