Transform input sequences into memory-organized chunks with capacity constraints. Processes input sequences by routing tokens to designated memory states according to routing_mask, sorts tokens by memory assignments, handles token truncation/padding based on memory capacity, an
(x: torch.Tensor, routing_mask: torch.Tensor, num_memories: int, selected_memories: torch.Tensor, capacity: float)
| 15 | fused_recurrent_linear_attn) |
| 16 | |
| 17 | def transform(x: torch.Tensor, routing_mask: torch.Tensor, num_memories: int, selected_memories: torch.Tensor, capacity: float): |
| 18 | ''' |
| 19 | Transform input sequences into memory-organized chunks with capacity constraints. |
| 20 | |
| 21 | Processes input sequences by routing tokens to designated memory states according to routing_mask, |
| 22 | sorts tokens by memory assignments, handles token truncation/padding based on memory capacity, |
| 23 | and returns memory-aligned tensors for parallel processing. |
| 24 | |
| 25 | Key operations: |
| 26 | 1. Expands input tensors when multiple memories are selected per token (top-k routing) |
| 27 | 2. Sorts tokens globally by (batch_idx, memory_idx) to group memory-assigned tokens |
| 28 | 3. Applies capacity-aware truncation (left-truncate oldest tokens when exceeding capacity) |
| 29 | 4. Pads memory chunks to uniform length for tensorization |
| 30 | |
| 31 | Args: |
| 32 | x: Input hidden states |
| 33 | Shape: (batch_size, seq_len, hidden_size) |
| 34 | routing_mask: Binary mask indicating active memory assignments |
| 35 | Shape: (batch_size, seq_len, num_memories) |
| 36 | num_memories: Total number of memories per batch |
| 37 | selected_memories: Memory indices assigned to each token. When using top-k routing, |
| 38 | this contains k memory indices per token (k >= 1) |
| 39 | Shape: (batch_size, seq_len) for k=1 or (batch_size, seq_len, topk) for k>1 |
| 40 | capacity: Scaling factor for memory capacity calculation. Actual capacity per memory is |
| 41 | ceil(seq_len * capacity), maintaining proportional capacity to sequence length |
| 42 | |
| 43 | Returns: |
| 44 | transformed_x: Memory-organized tensor with zero-padded capacity alignment |
| 45 | Shape: (num_memories, batch_size, capacity_len, hidden_size) |
| 46 | truncation_indices: Original indices used for gathering tokens after capacity truncation |
| 47 | Shape: (batch*num_memories, max_len) |
| 48 | sorted_indices: Sorting indices used to group tokens by memory assignments |
| 49 | Shape: (batch_size*seq_len*topk) |
| 50 | max_len: Maximum tokens per memory |
| 51 | mask: Boolean mask indicating valid (non-padded) positions in transformed_x |
| 52 | Shape: (batch*num_memories, max_len) |
| 53 | ''' |
| 54 | if selected_memories.dim() == 3: |
| 55 | # (batch, seq, topk) |
| 56 | topk = selected_memories.shape[2] |
| 57 | # x (batch, seq, hidden) |
| 58 | x = x.repeat_interleave(topk, dim=1) |
| 59 | # x (batch, seq * topk, hidden) |
| 60 | # (batch, seq, topk) |
| 61 | selected_memories = selected_memories.reshape(selected_memories.shape[0], -1) |
| 62 | # (batch, seq * topk) |
| 63 | |
| 64 | b, s, d = x.shape |
| 65 | x_flat = x.reshape(b * s, d) # [b*s, d] |
| 66 | |
| 67 | with torch.no_grad(): |
| 68 | batch_indices = torch.arange(b, device=x.device).unsqueeze(-1) |
| 69 | batch_indices = batch_indices.expand(b, s).reshape(-1) |
| 70 | # (b * s) |
| 71 | memories_flat = selected_memories.reshape(-1) # [b*s] |
| 72 | |
| 73 | combined = batch_indices * (memories_flat.max() + 1) + memories_flat |
| 74 | sorted_indices = combined.argsort() |