mainline777 eyad-silx commited on
Commit
41865df
·
0 Parent(s):

Duplicate from silx-ai/Quasar-Preview

Browse files

Co-authored-by: Eyad Gomaa <eyad-silx@users.noreply.huggingface.co>

This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +37 -0
  2. README.md +710 -0
  3. chat_template.jinja +1 -0
  4. config.json +116 -0
  5. configuration_quasar_long.py +140 -0
  6. engram.py +611 -0
  7. fla/__init__.py +8 -0
  8. fla/distributed_compat.py +57 -0
  9. fla/layers/__init__.py +3 -0
  10. fla/layers/abc.py +231 -0
  11. fla/layers/attn.py +176 -0
  12. fla/layers/based.py +93 -0
  13. fla/layers/bitattn.py +162 -0
  14. fla/layers/comba.py +328 -0
  15. fla/layers/delta_net.py +287 -0
  16. fla/layers/deltaformer.py +152 -0
  17. fla/layers/forgetting_attn.py +133 -0
  18. fla/layers/gated_deltanet.py +316 -0
  19. fla/layers/gated_deltaproduct.py +287 -0
  20. fla/layers/gla.py +304 -0
  21. fla/layers/gsa.py +237 -0
  22. fla/layers/hgrn.py +174 -0
  23. fla/layers/hgrn2.py +210 -0
  24. fla/layers/kda.py +277 -0
  25. fla/layers/lightnet.py +238 -0
  26. fla/layers/linear_attn.py +196 -0
  27. fla/layers/log_linear_mamba2.py +684 -0
  28. fla/layers/mamba.py +395 -0
  29. fla/layers/mamba2.py +649 -0
  30. fla/layers/mesa_net.py +221 -0
  31. fla/layers/mla.py +225 -0
  32. fla/layers/mom.py +831 -0
  33. fla/layers/multiscale_retention.py +303 -0
  34. fla/layers/nsa.py +137 -0
  35. fla/layers/path_attn.py +216 -0
  36. fla/layers/quasar.py +439 -0
  37. fla/layers/rebased.py +144 -0
  38. fla/layers/rodimus.py +397 -0
  39. fla/layers/rwkv6.py +360 -0
  40. fla/layers/rwkv7.py +347 -0
  41. fla/layers/simple_gla.py +273 -0
  42. fla/layers/utils.py +218 -0
  43. fla/models/__init__.py +3 -0
  44. fla/models/abc/__init__.py +12 -0
  45. fla/models/abc/configuration_abc.py +105 -0
  46. fla/models/abc/modeling_abc.py +371 -0
  47. fla/models/bitnet/__init__.py +12 -0
  48. fla/models/bitnet/configuration_bitnet.py +81 -0
  49. fla/models/bitnet/modeling_bitnet.py +395 -0
  50. fla/models/comba/__init__.py +11 -0
.gitattributes ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ tokenizer.json filter=lfs diff=lfs merge=lfs -text
37
+ quasar_banner.png filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,710 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ language:
3
+ - en
4
+ - ar
5
+ license: mit
6
+ tags:
7
+ - silx-ai
8
+ - quasar-preview
9
+ - quasar
10
+ - foundation-model
11
+ - moe
12
+ - 18b
13
+ - 2b-active
14
+ - long-context
15
+ - bittensor
16
+ - sn24
17
+ - decentralized-training
18
+ - distillation
19
+ - hybrid-transformer
20
+ - loop-transformer
21
+ - safe-nope
22
+ - drope
23
+ pipeline_tag: text-generation
24
+ library_name: transformers
25
+ ---
26
+
27
+ <p align="center">
28
+ <img src="./quasar_banner.png" alt="Quasar-Preview Foundation Model" width="100%">
29
+ </p>
30
+
31
+ # **Quasar-Preview**
32
+
33
+ **Quasar-Preview** is the first public model in SILX AI’s **Quasar Foundation Model** series.
34
+
35
+ It is an early preview checkpoint built to demonstrate the direction of the Quasar architecture at real scale: sparse MoE routing, hybrid recurrent/attention layers, and an experimental long-context configuration designed for future memory-based systems.
36
+
37
+ This is **not the finished Quasar model**.
38
+
39
+ Quasar-Preview is the first public step in a larger series of Quasar models that will continue scaling through decentralized training, distillation, architecture improvements, and long-context research on **Bittensor SN24**.
40
+
41
+ ---
42
+
43
+ ## TL;DR
44
+
45
+ - **First public Quasar model**
46
+ - **~18B total parameter MoE**
47
+ - **~2B active parameter path**
48
+ - **Experimental 5M-token context configuration**
49
+ - Built with **Loop Transformer + Quasar hybrid attention**
50
+ - Includes **Quasar / Raven / GLA** hybrid layers
51
+ - Designed for **Bittensor SN24 decentralized distillation**
52
+ - Trained on **>1T and <1.5T tokens**
53
+ - Long-context extension path has received **<1B tokens** so far
54
+ - Early preview checkpoint, not a final production/SOTA model
55
+
56
+ Quasar-Preview should be understood as an **architecture preview and foundation checkpoint**, not the final endpoint of the Quasar roadmap.
57
+
58
+ ---
59
+
60
+ # Important Note
61
+
62
+ Quasar-Preview is an early model from our broader Quasar model series.
63
+
64
+ It is released to make the architecture public, allow miners and researchers to work with the model, and begin the next phase of decentralized scaling.
65
+
66
+ This model is:
67
+
68
+ - An **early preview checkpoint**
69
+ - The **first model** in a planned series of Quasar models
70
+ - Trained on **>1T and <1.5T tokens**
71
+ - Built for **research, distillation, and SN24 training**
72
+ - Not yet the final Quasar model
73
+ - Not intended to represent the final quality of the Quasar architecture
74
+
75
+ Performance is expected to improve through:
76
+
77
+ - Iterative subnet training
78
+ - Distillation cycles
79
+ - Longer training runs
80
+ - Stronger post-training
81
+ - More long-context extension training
82
+ - Future Quasar architecture updates
83
+
84
+ ---
85
+
86
+ # Model Overview
87
+
88
+ | Field | Value |
89
+ | --- | --- |
90
+ | Model Name | Quasar-Preview |
91
+ | Model Family | Quasar Foundation Models |
92
+ | Organization | SILX AI |
93
+ | Model Type | `quasar_long` |
94
+ | Architecture | Quasar Long Hybrid Transformer |
95
+ | Total Parameters | ~18B class |
96
+ | Active Parameters | ~2B class sparse MoE path |
97
+ | Training Stage | Early preview checkpoint |
98
+ | Context Config | Experimental 5M-token config |
99
+ | Long-Context Method | Safe NoPE / DrOPE-style staging |
100
+ | Tokenizer | Quasar tokenizer preserved from checkpoint lineage |
101
+ | Primary Use | Research, distillation, SN24 decentralized training |
102
+ | License | MIT |
103
+
104
+ ---
105
+
106
+ # What Is Active In This Checkpoint?
107
+
108
+ Quasar-Preview includes several architecture paths. Some are active in this checkpoint, while others are included for future Quasar versions.
109
+
110
+ | Component | Status in Quasar-Preview |
111
+ | --- | --- |
112
+ | Sparse MoE | Active |
113
+ | Quasar hybrid layers | Active |
114
+ | GLA branch | Active |
115
+ | Raven branch | Active |
116
+ | GQA compatibility attention | Active in this checkpoint |
117
+ | Safe NoPE / DrOPE-style context config | Active |
118
+ | Loop Transformer scaffold | Present |
119
+ | Loop execution | Configured as single-loop |
120
+ | Looped anchor injection | Disabled |
121
+ | Engram memory | Included and loadable, not active by default |
122
+ | 5M context | Config exposed, early long-context training only |
123
+
124
+ The goal of this release is to expose the first working Quasar architecture checkpoint while keeping the model stable for research and SN24 training.
125
+
126
+ ---
127
+
128
+ # Quick Start
129
+
130
+ Quasar-Preview uses custom architecture code.
131
+
132
+ Use `trust_remote_code=True` when loading the model.
133
+
134
+ ```python
135
+ from transformers import AutoTokenizer, AutoModelForCausalLM
136
+ import torch
137
+
138
+ model_id = "SILX-AI/Quasar-Preview"
139
+
140
+ tokenizer = AutoTokenizer.from_pretrained(
141
+ model_id,
142
+ trust_remote_code=True
143
+ )
144
+
145
+ model = AutoModelForCausalLM.from_pretrained(
146
+ model_id,
147
+ trust_remote_code=True,
148
+ torch_dtype=torch.bfloat16,
149
+ device_map="auto"
150
+ )
151
+
152
+ prompt = "Explain the purpose of long-context models in simple terms."
153
+
154
+ inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
155
+
156
+ with torch.no_grad():
157
+ output = model.generate(
158
+ **inputs,
159
+ max_new_tokens=256,
160
+ do_sample=True,
161
+ temperature=0.7,
162
+ top_p=0.9
163
+ )
164
+
165
+ print(tokenizer.decode(output[0], skip_special_tokens=True))
166
+ ```
167
+
168
+ ## Inference Notes
169
+
170
+ Quasar-Preview is an ~18B total parameter MoE checkpoint. Even though the active path is ~2B parameters, the full checkpoint still requires loading the model weights.
171
+
172
+ Actual memory usage depends on:
173
+
174
+ - Precision
175
+ - Quantization
176
+ - Runtime implementation
177
+ - Sequence length
178
+ - Batch size
179
+ - Device mapping
180
+ - Whether long-context experiments are enabled
181
+
182
+ The 5M context configuration is experimental. Do not assume ordinary inference hardware can run full 5M-token contexts without specialized infrastructure.
183
+
184
+ ---
185
+
186
+ # Quasar-Preview Benchmark Snapshot
187
+
188
+ These are early benchmark results from the current Quasar checkpoint lineage.
189
+
190
+ They should be treated as a moving snapshot, not final model quality.
191
+
192
+ | Category | Benchmark | Quasar-Preview |
193
+ | --- | --- | ---: |
194
+ | Knowledge | MMLU (5-shot) | **68.40%** |
195
+ | Knowledge | MMLU-Pro | **33.20%** |
196
+ | Knowledge | GPQA | **25.60%** |
197
+ | Commonsense | ARC Challenge | **63.00%** |
198
+ | Commonsense | ARC Easy | **80.10%** |
199
+ | Commonsense | PIQA | **81.90%** |
200
+ | Commonsense | HellaSwag | **74.00%** |
201
+ | Science | OpenBookQA | **47.00%** |
202
+ | Math | MATH-500 (4-shot) | **71.40%** |
203
+
204
+ ## Evaluation Notes
205
+
206
+ These results are provided as an early internal snapshot for the current Quasar-Preview checkpoint lineage.
207
+
208
+ They are not presented as final model quality. Public verification, different harness versions, prompt formats, decoding settings, and evaluation implementations may change the reported numbers.
209
+
210
+ When comparing Quasar-Preview to other models, please report:
211
+
212
+ - Evaluation harness
213
+ - Harness version or commit
214
+ - Prompt format
215
+ - Shot count
216
+ - Decoding settings
217
+ - Whether chain-of-thought prompting was used
218
+ - Exact checkpoint version
219
+
220
+ ---
221
+
222
+ # Training Strategy
223
+
224
+ Quasar follows a multi-stage training plan.
225
+
226
+ Quasar-Preview is an early checkpoint from this plan.
227
+
228
+ ## Stage 1 — Base Pretraining
229
+
230
+ The base model is trained on a broad corpus to build general next-token prediction, reasoning, and language ability.
231
+
232
+ Goals of this stage:
233
+
234
+ - Stabilize the sparse MoE path
235
+ - Build general language ability
236
+ - Train the hybrid Quasar stack
237
+ - Establish a checkpoint suitable for distillation and subnet training
238
+
239
+ Quasar-Preview has been trained on **>1T and <1.5T tokens** so far.
240
+
241
+ ## Stage 2 — Distillation And Capability Training
242
+
243
+ After base training, Quasar-Preview is improved through task distillation and targeted capability training.
244
+
245
+ The goal is to make the checkpoint more useful for:
246
+
247
+ - Reasoning
248
+ - Instruction-following
249
+ - Commonsense tasks
250
+ - Math and science tasks
251
+ - SN24 miner distillation
252
+ - Future post-training
253
+
254
+ This release is designed to be a foundation for continued decentralized improvement rather than the final result.
255
+
256
+ ## Stage 3 — Long-Context Extension
257
+
258
+ Quasar is designed to move toward ultra-long-context reasoning and memory.
259
+
260
+ The current checkpoint exposes an experimental **5M-token context configuration** using safe NoPE / DrOPE-style staging.
261
+
262
+ Important: the 5M context path has received **less than 1B tokens** of long-context extension training so far.
263
+
264
+ This means the config is present, but mature 5M-token reasoning quality should not be expected yet.
265
+
266
+ The purpose of this stage is to:
267
+
268
+ - Preserve short-context behavior
269
+ - Avoid damaging the base model during extension
270
+ - Prepare the architecture for future long-context training
271
+ - Enable research on scalable memory and recall
272
+
273
+ ---
274
+
275
+ # Quasar Long Hybrid Architecture
276
+
277
+ Quasar is a hybrid transformer architecture designed for long-context research, sparse computation, and decentralized training.
278
+
279
+ It is built around:
280
+
281
+ - A Loop Transformer execution scaffold
282
+ - Sparse Mixture-of-Experts routing
283
+ - Hybrid Quasar / Raven / GLA branch layers
284
+ - Optional anchor-state conditioning
285
+ - Optional Engram n-gram memory
286
+ - Safe NoPE / DrOPE-style long-context configuration
287
+
288
+ Quasar-Preview is the first public checkpoint in this architecture family.
289
+
290
+ ---
291
+
292
+ # Technical Specifications
293
+
294
+ | Component | Value |
295
+ | --- | ---: |
296
+ | Total parameters | ~18B |
297
+ | Active parameters | ~2B |
298
+ | Layers | 20 |
299
+ | Hidden size | 2048 |
300
+ | Intermediate size | 5120 |
301
+ | Attention heads | 16 |
302
+ | KV heads | 4 |
303
+ | Head dim | 128 |
304
+ | Vocabulary size | 157,184 |
305
+ | Experts | 256 |
306
+ | Experts per token | 8 |
307
+ | Shared experts | 1 |
308
+ | Active hybrid layers | 4-19 |
309
+ | Raven slots | 64 |
310
+ | Raven top-k | 32 |
311
+ | Engram slots config | 2,000,000 |
312
+ | Loop count config | 1 |
313
+ | Looped injection config | Disabled |
314
+ | Max context config | 5,000,000 |
315
+ | Safe NoPE cutoff | 512 |
316
+
317
+ Compatibility note: this checkpoint includes GQA for the current release path. Future Quasar versions may change this component as the architecture evolves.
318
+
319
+ ---
320
+
321
+ # Looped Transformer Path
322
+
323
+ Quasar includes a Loop Transformer execution path.
324
+
325
+ The idea is to reuse the decoder stack across multiple passes, increasing effective computation depth without copying every parameter into a deeper model.
326
+
327
+ The current checkpoint is configured conservatively:
328
+
329
+ ```text
330
+ num_loops: 1
331
+ use_looped_injection: false
332
+ ```
333
+
334
+ This means Quasar-Preview runs as a single-loop model by default.
335
+
336
+ The loop machinery is still part of the architecture code and can be enabled in future Quasar configurations.
337
+
338
+ When looped injection is enabled, Quasar keeps an anchor snapshot of the input embedding stream, usually called **P**, and injects it back into the hidden state during looped execution.
339
+
340
+ This gives later loop passes a stable reference to the original token stream.
341
+
342
+ The intended future looped path is:
343
+
344
+ ```text
345
+ Token IDs
346
+ |
347
+ v
348
+ Embedding Layer
349
+ |
350
+ +--> Anchor P snapshot
351
+ |
352
+ v
353
+ Decoder stack
354
+ |
355
+ v
356
+ Loop pass 1
357
+ |
358
+ +--> inject gated Anchor P
359
+ |
360
+ v
361
+ Loop pass 2 / future passes
362
+ |
363
+ v
364
+ Final hidden state
365
+ ```
366
+
367
+ The injection gate is initialized near zero so the model can adapt safely instead of suddenly changing behavior.
368
+
369
+ This gives Quasar a path toward deeper effective reasoning while keeping parameter count controlled.
370
+
371
+ ---
372
+
373
+ # Core Data Flow
374
+
375
+ ```text
376
+ Token IDs
377
+ |
378
+ v
379
+ Token Embedding
380
+ |
381
+ +--> Optional Anchor P snapshot
382
+ |
383
+ v
384
+ Early Transformer Blocks
385
+ layers 0-3
386
+ |
387
+ v
388
+ Hybrid Quasar Blocks
389
+ layers 4-19
390
+ |
391
+ +--> GQA attention path
392
+ |
393
+ +--> Quasar recurrent / linear path
394
+ |
395
+ +--> Raven slot-memory path
396
+ |
397
+ +--> GLA recurrent path
398
+ |
399
+ v
400
+ Hybrid Add / Branch Merge
401
+ |
402
+ v
403
+ Optional Loop Injection / Next Loop
404
+ |
405
+ v
406
+ RMSNorm
407
+ |
408
+ v
409
+ LM Head
410
+ |
411
+ v
412
+ Next-token logits
413
+ ```
414
+
415
+ ---
416
+
417
+ # Hybrid Layer Composition
418
+
419
+ The active hybrid layers are:
420
+
421
+ ```text
422
+ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19
423
+ ```
424
+
425
+ The current layerwise branch cycle is:
426
+
427
+ ```text
428
+ quasar -> raven -> quasar -> quasar -> gla
429
+ ```
430
+
431
+ Across the hybrid stack, this gives:
432
+
433
+ - **Quasar branch:** 10 layers
434
+ - **Raven branch:** 3 layers
435
+ - **GLA branch:** 3 layers
436
+
437
+ The design keeps Quasar as the dominant branch while giving the model targeted recurrent and slot-memory paths.
438
+
439
+ ---
440
+
441
+ # Quasar + GLA
442
+
443
+ GLA is used through the bundled Flash Linear Attention stack.
444
+
445
+ The goal of the GLA branch is to give Quasar a fast recurrent sequence-mixing path that is cheaper than full dense attention at long lengths.
446
+
447
+ Current GLA-related config:
448
+
449
+ ```text
450
+ hybrid_gla_enabled: true
451
+ hybrid_gla_expand_k: 1.0
452
+ hybrid_gla_expand_v: 1.0
453
+ hybrid_use_short_conv: false
454
+ ```
455
+
456
+ GLA is not used as a standalone model here.
457
+
458
+ It is a branch inside Quasar's hybrid layers.
459
+
460
+ ---
461
+
462
+ # Raven Design
463
+
464
+ Raven is included as a slot-routed recurrent attention branch.
465
+
466
+ Current Raven config:
467
+
468
+ ```text
469
+ hybrid_raven_enabled: true
470
+ hybrid_raven_slots: 64
471
+ hybrid_raven_topk: 32
472
+ hybrid_raven_decay_type: Mamba2
473
+ ```
474
+
475
+ Raven routes hidden states through a fixed number of recurrent memory slots.
476
+
477
+ In this checkpoint:
478
+
479
+ - The branch has **64 memory slots**
480
+ - It selects **top-32 routes**
481
+ - It uses a **Mamba2-style decay**
482
+
483
+ Raven gives Quasar a memory-like path where sequence information can be compressed into routed recurrent state instead of relying only on dense attention.
484
+
485
+ ---
486
+
487
+ # Engram Design
488
+
489
+ Engram is Quasar's conditional n-gram memory module.
490
+
491
+ It is included in the repository as `engram.py` and supports:
492
+
493
+ - n-gram orders `[2, 3]`
494
+ - 8 Engram heads
495
+ - configurable memory slots
496
+ - Triton hash-table lookup
497
+ - gated projection back into the residual stream
498
+
499
+ Current Engram config:
500
+
501
+ ```text
502
+ engram_slots: 2,000,000
503
+ engram_dim: 512
504
+ engram_ngram_orders: [2, 3]
505
+ engram_num_heads: 8
506
+ engram_residual_scale: 0.01
507
+ engram_lr_multiplier: 5.0
508
+ engram_layers: []
509
+ ```
510
+
511
+ `engram_layers` is currently empty.
512
+
513
+ This means Engram is included and loadable, but not active by default in Quasar-Preview.
514
+
515
+ Future Quasar versions can enable Engram on selected layers without changing the base model shape.
516
+
517
+ Engram is intended as a fast recall path for repeated local patterns, while the main model focuses on reasoning and generalization.
518
+
519
+ ---
520
+
521
+ # Safe NoPE / DrOPE Context Design
522
+
523
+ The current checkpoint uses safe NoPE as the default long-context configuration.
524
+
525
+ Current context config:
526
+
527
+ ```text
528
+ use_nope: true
529
+ long_context_mode: rope_short_nope_long
530
+ nope_after_position: 512
531
+ max_position_embeddings: 5,000,000
532
+ max_seq_length: 5,000,000
533
+ max_sequence_length: 5,000,000
534
+ rope_scaling: null
535
+ rope_theta: 10000
536
+ ```
537
+
538
+ The behavior is:
539
+
540
+ ```text
541
+ Positions 0-511
542
+ -> normal RoPE
543
+
544
+ Positions 512+
545
+ -> NoPE identity rotation
546
+ cos = 1
547
+ sin = 0
548
+ ```
549
+
550
+ This is a safe DrOPE-style staging design for positional extension.
551
+
552
+ The goals are:
553
+
554
+ - Preserve short-context behavior
555
+ - Avoid stretching RoPE everywhere
556
+ - Avoid allocating a giant 5M RoPE table
557
+ - Expose a 5M sequence-length configuration
558
+ - Prepare for future long-context training runs
559
+
560
+ Important: the 5M context path has only received **less than 1B tokens** of long-context extension training so far.
561
+
562
+ So high-quality 5M-token reasoning should not be expected yet.
563
+
564
+ This setting is included to expose and continue training the long-context path safely.
565
+
566
+ ---
567
+
568
+ # Config Snapshot
569
+
570
+ ```json
571
+ {
572
+ "model_type": "quasar_long",
573
+ "architectures": ["QuasarLongForCausalLM"],
574
+ "hidden_size": 2048,
575
+ "intermediate_size": 5120,
576
+ "num_hidden_layers": 20,
577
+ "num_attention_heads": 16,
578
+ "num_key_value_heads": 4,
579
+ "head_dim": 128,
580
+ "vocab_size": 157184,
581
+ "num_experts": 256,
582
+ "num_experts_per_tok": 8,
583
+ "num_shared_experts": 1,
584
+ "num_loops": 1,
585
+ "use_looped_injection": false,
586
+ "hybrid_attention_layers": [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
587
+ "hybrid_branch_layout": "layerwise",
588
+ "hybrid_layerwise_cycle": ["quasar", "raven", "quasar", "quasar", "gla"],
589
+ "hybrid_replacement_mode": "add",
590
+ "hybrid_eval_mode": "hybrid_add",
591
+ "hybrid_quasar_enabled": true,
592
+ "hybrid_raven_enabled": true,
593
+ "hybrid_gla_enabled": true,
594
+ "hybrid_raven_slots": 64,
595
+ "hybrid_raven_topk": 32,
596
+ "use_nope": true,
597
+ "long_context_mode": "rope_short_nope_long",
598
+ "nope_after_position": 512,
599
+ "max_position_embeddings": 5000000,
600
+ "max_seq_length": 5000000,
601
+ "max_sequence_length": 5000000
602
+ }
603
+ ```
604
+
605
+ ---
606
+
607
+ # Intended Use
608
+
609
+ Quasar-Preview is designed as an early foundation checkpoint for the Quasar ecosystem.
610
+
611
+ It is primarily intended for:
612
+
613
+ - **Bittensor SN24 miners** participating in decentralized training and knowledge distillation
614
+ - **Distillation pipelines** transferring capabilities from stronger teacher models
615
+ - **Research on long-context architectures**
616
+ - **Research on sparse MoE systems**
617
+ - **Hybrid attention research**
618
+ - **Agentic system experiments**
619
+ - **Memory and recall experiments**
620
+ - **Future Quasar model development**
621
+
622
+ This model is best treated as a research and development checkpoint.
623
+
624
+ ---
625
+
626
+ # Out-of-Scope Use
627
+
628
+ Quasar-Preview is not intended to be used as:
629
+
630
+ - A final production assistant
631
+ - A safety-aligned chatbot
632
+ - A medical, legal, or financial authority
633
+ - A final benchmark-maximized release
634
+ - Proof of mature 5M-token reasoning quality
635
+ - The final Quasar architecture endpoint
636
+
637
+ The model may produce incorrect, unsafe, biased, or low-quality outputs.
638
+
639
+ Use appropriate evaluation, filtering, and safety layers before any deployment.
640
+
641
+ ---
642
+
643
+ # Limitations
644
+
645
+ Quasar-Preview is early.
646
+
647
+ Known limitations:
648
+
649
+ - It is not the finished Quasar model.
650
+ - It is the first model in a broader Quasar series.
651
+ - Long-context behavior is experimental.
652
+ - The 5M-token context is a configuration path, not yet mature 5M-token reasoning quality.
653
+ - The long-context path has received less than 1B tokens of extension training so far.
654
+ - Some architecture modules are included for future versions but disabled in this checkpoint.
655
+ - Engram is included but not active by default.
656
+ - Loop execution is configured as single-loop by default.
657
+ - Benchmarks are early checkpoint-lineage snapshots and require public verification.
658
+ - The model may hallucinate or produce incorrect answers.
659
+ - The model has not completed the full Quasar training roadmap.
660
+
661
+ ---
662
+
663
+ # Bittensor SN24
664
+
665
+ Quasar-Preview is designed for the **SN24 Quasar subnet** on Bittensor.
666
+
667
+ The goal is to create a shared architecture where miners can continuously improve the model through distributed knowledge distillation, evaluation, and iterative training.
668
+
669
+ SN24 is intended to support:
670
+
671
+ - Open model improvement
672
+ - Competitive distillation
673
+ - Decentralized training incentives
674
+ - Shared progress on the Quasar architecture
675
+ - Long-context and memory-focused model development
676
+
677
+ Quasar-Preview is the starting checkpoint for this direction.
678
+
679
+ ---
680
+
681
+ # Roadmap
682
+
683
+ Quasar-Preview is only the first public model in the Quasar series.
684
+
685
+ Next Quasar models will continue toward:
686
+
687
+ - Larger-scale decentralized training
688
+ - More training tokens
689
+ - Stronger post-training
690
+ - Better reasoning performance
691
+ - More stable long-context behavior
692
+ - More long-context extension training
693
+ - Deeper Loop Transformer experiments
694
+ - More Raven, GLA, and Engram experimentation
695
+ - Improved benchmark performance
696
+ - Stronger agentic and memory capabilities
697
+
698
+ Future releases may change architecture components, routing, loop configuration, long-context training strategy, and active memory modules as the Quasar series evolves.
699
+
700
+ ---
701
+
702
+ # Release Statement
703
+
704
+ Quasar-Preview is not the final destination.
705
+
706
+ It is the first public checkpoint in the Quasar model series and the first public proof of the architecture direction at scale.
707
+
708
+ The model is early, but it is real, usable, and ready for research, distillation, and decentralized improvement.
709
+
710
+ This is the beginning of Quasar.
chat_template.jinja ADDED
@@ -0,0 +1 @@
 
 
1
+ {% for message in messages %}{% set role = message['role'] | lower %}{% if role == 'user' %}{% set role = 'HUMAN' %}{% endif %}{% set role = role | upper %}{{ '<role>' + role + '</role>' + message['content'] }}{% endfor %}{% if add_generation_prompt %}{{ '<role>ASSISTANT</role>' }}{% endif %}
config.json ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_name_or_path": "silx-ai/Quasar-T",
3
+ "architectures": [
4
+ "QuasarLongForCausalLM"
5
+ ],
6
+ "attention_dropout": 0.0,
7
+ "auto_map": {
8
+ "AutoConfig": "configuration_quasar_long.QuasarLongConfig",
9
+ "AutoModelForCausalLM": "modeling_quasar_long.QuasarLongForCausalLM"
10
+ },
11
+ "dtype": "bfloat16",
12
+ "embedding_dropout": 0.0,
13
+ "engram_dim": 512,
14
+ "engram_layers": [],
15
+ "engram_lr_multiplier": 5.0,
16
+ "engram_ngram_orders": [
17
+ 2,
18
+ 3
19
+ ],
20
+ "engram_num_heads": 8,
21
+ "engram_residual_scale": 0.01,
22
+ "engram_slots": 2000000,
23
+ "eos_token_id": 156892,
24
+ "first_k_dense_replace": 1,
25
+ "head_dim": 128,
26
+ "hidden_act": "silu",
27
+ "hidden_size": 2048,
28
+ "hybrid_alpha_init": 0.8473,
29
+ "hybrid_attention_layers": [
30
+ 4,
31
+ 5,
32
+ 6,
33
+ 7,
34
+ 8,
35
+ 9,
36
+ 10,
37
+ 11,
38
+ 12,
39
+ 13,
40
+ 14,
41
+ 15,
42
+ 16,
43
+ 17,
44
+ 18,
45
+ 19
46
+ ],
47
+ "hybrid_branch_layout": "layerwise",
48
+ "hybrid_eval_force_branch": "",
49
+ "hybrid_eval_mode": "hybrid_add",
50
+ "hybrid_gla_enabled": true,
51
+ "hybrid_gla_expand_k": 1.0,
52
+ "hybrid_gla_expand_v": 1.0,
53
+ "hybrid_layerwise_cycle": [
54
+ "quasar",
55
+ "raven",
56
+ "quasar",
57
+ "quasar",
58
+ "gla"
59
+ ],
60
+ "hybrid_quasar_enabled": true,
61
+ "hybrid_raven_decay_type": "Mamba2",
62
+ "hybrid_raven_enabled": true,
63
+ "hybrid_raven_slots": 64,
64
+ "hybrid_raven_topk": 32,
65
+ "hybrid_replacement_mode": "add",
66
+ "hybrid_use_short_conv": false,
67
+ "initializer_range": 0.02,
68
+ "intermediate_size": 5120,
69
+ "long_context_mode": "rope_short_nope_long",
70
+ "max_position_embeddings": 5000000,
71
+ "max_seq_length": 5000000,
72
+ "max_sequence_length": 5000000,
73
+ "max_window_layers": 20,
74
+ "model_type": "quasar_long",
75
+ "moe_intermediate_size": 512,
76
+ "moe_router_enable_expert_bias": true,
77
+ "moe_shared_expert_intermediate_size": 512,
78
+ "mtp_loss_scaling_factor": 0,
79
+ "n_group": 8,
80
+ "nope_after_position": 512,
81
+ "norm_topk_prob": true,
82
+ "num_attention_heads": 16,
83
+ "num_experts": 256,
84
+ "num_experts_per_tok": 8,
85
+ "num_hidden_layers": 20,
86
+ "num_key_value_heads": 4,
87
+ "num_loops": 1,
88
+ "num_nextn_predict_layers": 0,
89
+ "num_shared_experts": 1,
90
+ "output_dropout": 0.0,
91
+ "output_router_logits": false,
92
+ "pad_token_id": 156892,
93
+ "partial_rotary_factor": 0.5,
94
+ "rms_norm_eps": 1e-06,
95
+ "rope_parameters": {
96
+ "partial_rotary_factor": 0.5,
97
+ "rope_theta": 10000,
98
+ "rope_type": "default"
99
+ },
100
+ "rope_scaling": null,
101
+ "rope_theta": 10000,
102
+ "routed_scaling_factor": 2.5,
103
+ "router_dtype": "fp32",
104
+ "score_function": "sigmoid",
105
+ "tie_word_embeddings": false,
106
+ "topk_group": 4,
107
+ "transformers_version": "5.10.2",
108
+ "use_bias": false,
109
+ "use_cache": true,
110
+ "use_looped_injection": false,
111
+ "use_nope": true,
112
+ "use_qk_norm": true,
113
+ "use_qkv_bias": false,
114
+ "use_rmsnorm": true,
115
+ "vocab_size": 157184
116
+ }
configuration_quasar_long.py ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Quasar Long model configuration"""
2
+
3
+ from transformers.configuration_utils import PretrainedConfig
4
+
5
+
6
+ class QuasarLongConfig(PretrainedConfig):
7
+ model_type = "quasar_long"
8
+
9
+ def __init__(
10
+ self,
11
+ vocab_size=157184,
12
+ hidden_size=2048,
13
+ intermediate_size=5120,
14
+ num_hidden_layers=20,
15
+ num_attention_heads=16,
16
+ num_key_value_heads=4,
17
+ hidden_act="silu",
18
+ use_qkv_bias=False, # quasar legacy
19
+ use_bias=False, # quasar legacy
20
+ rms_norm_eps=1e-06,
21
+ tie_word_embeddings=False, # PretrainedConfig key, here change default value.
22
+ embedding_dropout=0.0,
23
+ attention_dropout=0.0,
24
+ output_dropout=0.0,
25
+ initializer_range=0.02,
26
+ max_position_embeddings=32768,
27
+ rope_theta=600000.0,
28
+ use_cache=True,
29
+ max_window_layers=20,
30
+ rope_scaling=None,
31
+ pad_token_id=156892,
32
+ eos_token_id=156892,
33
+ num_experts=256,
34
+ num_shared_experts=1,
35
+ num_experts_per_tok=8,
36
+ n_group=8,
37
+ topk_group=4,
38
+ moe_intermediate_size=512,
39
+ first_k_dense_replace=1,
40
+ head_dim=128,
41
+ output_router_logits=False,
42
+ use_qk_norm=True,
43
+ num_nextn_predict_layers=0,
44
+ mtp_loss_scaling_factor=0,
45
+ moe_router_enable_expert_bias=True,
46
+ routed_scaling_factor=1.0,
47
+ hybrid_attention_layers=None,
48
+ hybrid_alpha_init=-15.0,
49
+ hybrid_gla_expand_k=1.0,
50
+ hybrid_gla_expand_v=1.0,
51
+ hybrid_use_short_conv=False,
52
+ hybrid_quasar_enabled=True,
53
+ hybrid_gla_enabled=True,
54
+ hybrid_branch_layout="mixed",
55
+ hybrid_layerwise_cycle=None,
56
+ # ── Looped Transformer ────────────────────────────────────────────────
57
+ num_loops=1,
58
+ use_looped_injection=False,
59
+ # ── Engram Conditional Memory ─────────────────────────────────────────
60
+ # engram_layers=[] → module disabled (zero overhead, backward-compatible).
61
+ engram_layers=None,
62
+ engram_dim=512,
63
+ engram_slots=2_000_000,
64
+ engram_num_heads=8,
65
+ engram_ngram_orders=None,
66
+ engram_lr_multiplier=5.0,
67
+ use_nope=False,
68
+ long_context_mode="rope_short_nope_long",
69
+ nope_after_position=512,
70
+ max_seq_length=None,
71
+ max_sequence_length=None,
72
+ **kwargs,
73
+ ):
74
+ self.num_hidden_layers = num_hidden_layers
75
+ self.vocab_size = vocab_size
76
+ self.hidden_size = hidden_size
77
+ self.intermediate_size = intermediate_size
78
+ self.num_attention_heads = num_attention_heads
79
+ self.num_key_value_heads = num_key_value_heads
80
+ self.hidden_act = hidden_act
81
+ self.use_qkv_bias = use_qkv_bias
82
+ self.use_bias = use_bias
83
+ self.rms_norm_eps = rms_norm_eps
84
+ self.embedding_dropout = embedding_dropout
85
+ self.attention_dropout = attention_dropout
86
+ self.output_dropout = output_dropout
87
+ self.num_nextn_predict_layers = num_nextn_predict_layers
88
+ self.mtp_loss_scaling_factor = mtp_loss_scaling_factor
89
+ self.initializer_range = initializer_range
90
+ self.max_position_embeddings = max_position_embeddings
91
+ self.rope_theta = rope_theta
92
+ self.use_cache = use_cache
93
+ self.max_window_layers = max_window_layers
94
+ self.head_dim = head_dim or self.hidden_size // self.num_attention_heads
95
+ self.rope_scaling = rope_scaling
96
+ self.use_qk_norm = use_qk_norm
97
+ self.moe_router_enable_expert_bias = moe_router_enable_expert_bias
98
+ self.routed_scaling_factor = routed_scaling_factor
99
+ self.hybrid_attention_layers = hybrid_attention_layers or []
100
+ self.hybrid_alpha_init = hybrid_alpha_init
101
+ self.hybrid_gla_expand_k = hybrid_gla_expand_k
102
+ self.hybrid_gla_expand_v = hybrid_gla_expand_v
103
+ self.hybrid_use_short_conv = hybrid_use_short_conv
104
+ self.hybrid_quasar_enabled = hybrid_quasar_enabled
105
+ self.hybrid_gla_enabled = hybrid_gla_enabled
106
+ self.hybrid_branch_layout = hybrid_branch_layout
107
+ self.hybrid_layerwise_cycle = list(hybrid_layerwise_cycle) if hybrid_layerwise_cycle is not None else [
108
+ "quasar",
109
+ "raven",
110
+ "gla",
111
+ ]
112
+
113
+ # Looped Transformer
114
+ self.num_loops = num_loops
115
+ self.use_looped_injection = use_looped_injection
116
+
117
+ # Engram Conditional Memory
118
+ self.engram_layers = list(engram_layers) if engram_layers is not None else []
119
+ self.engram_dim = engram_dim
120
+ self.engram_slots = engram_slots
121
+ self.engram_num_heads = engram_num_heads
122
+ self.engram_ngram_orders = list(engram_ngram_orders) if engram_ngram_orders is not None else [2, 3]
123
+ self.engram_lr_multiplier = engram_lr_multiplier
124
+ self.use_nope = use_nope
125
+ self.long_context_mode = long_context_mode
126
+ self.nope_after_position = int(nope_after_position)
127
+ self.max_seq_length = int(max_seq_length) if max_seq_length is not None else None
128
+ self.max_sequence_length = int(max_sequence_length) if max_sequence_length is not None else None
129
+
130
+ # MoE configs
131
+ self.num_experts = num_experts
132
+ self.num_shared_experts = num_shared_experts
133
+ self.num_experts_per_tok = num_experts_per_tok
134
+ self.n_group = n_group
135
+ self.topk_group = topk_group
136
+ self.moe_intermediate_size = moe_intermediate_size
137
+ self.first_k_dense_replace = first_k_dense_replace
138
+ self.output_router_logits = output_router_logits
139
+
140
+ super().__init__(pad_token_id=pad_token_id, eos_token_id=eos_token_id, tie_word_embeddings=tie_word_embeddings, **kwargs)
engram.py ADDED
@@ -0,0 +1,611 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ EngramModule: Conditional N-gram Memory for Quasar-RoPE
3
+ Implements Engram from DeepSeek-AI (arXiv:2601.07372).
4
+
5
+ Design constraints:
6
+ - No Python loops over T (sequence length) or B (batch).
7
+ - N-gram extraction via torch.unfold (single vectorized op).
8
+ - Hash computed via vectorized XOR reduction (loop over n=2..3 only, compile-time constant).
9
+ - Embedding lookup via batched advanced indexing — no loop over T.
10
+ - Optional Triton kernel fuses hash + lookup + accumulation into a single SRAM pass.
11
+ - Zero output at init: conv.weight=0, out_proj uses deep Trinity init.
12
+ """
13
+
14
+ import math
15
+ import os
16
+ import torch
17
+ import torch.nn as nn
18
+ import torch.nn.functional as F
19
+
20
+ try:
21
+ import triton
22
+ import triton.language as tl
23
+ HAS_TRITON = True
24
+ except ImportError:
25
+ HAS_TRITON = False
26
+
27
+
28
+ # ─────────────────────────────────────────────────────────────────────────────
29
+ # Helpers
30
+ # ─────────────────────────────────────────────────────────────────────────────
31
+
32
+ def _next_prime(n: int) -> int:
33
+ """Smallest prime >= n."""
34
+ def _is_prime(x: int) -> bool:
35
+ if x < 2:
36
+ return False
37
+ if x == 2:
38
+ return True
39
+ if x % 2 == 0:
40
+ return False
41
+ for i in range(3, int(x ** 0.5) + 1, 2):
42
+ if x % i == 0:
43
+ return False
44
+ return True
45
+ n = max(n, 2)
46
+ while not _is_prime(n):
47
+ n += 1
48
+ return n
49
+
50
+
51
+ # ─────────────────────────────────────────────────────────────────────────────
52
+ # Triton Kernel: Fused N-gram Hash + Embedding Lookup
53
+ #
54
+ # Grid: (B, T). Each program handles one (batch, position) pair.
55
+ # For each of the `num_tables` embedding tables:
56
+ # 1. Load the suffix n-gram ending at position t (causal, no future tokens).
57
+ # 2. Compute XOR-multiplicative hash (loop over n ≤ 3, constexpr-unrolled).
58
+ # 3. Index into the embedding table and write directly to output.
59
+ # One SRAM pass — no intermediate [B,T,n] tensor, no round-trip to HBM.
60
+ # ─────────────────────────────────────────────────────────────────────────────
61
+
62
+ if HAS_TRITON:
63
+ @triton.jit
64
+ def _engram_hash_lookup_kernel(
65
+ # [B, T] canonical token IDs (int32 on device)
66
+ canonical_ptr, stride_cb, stride_ct,
67
+ # [B, T, num_tables * d_slot] output (bfloat16)
68
+ output_ptr, stride_ob, stride_ot,
69
+ # [num_tables, M, d_slot] embedding tables (float32)
70
+ tables_ptr, stride_tn, stride_tm, stride_td,
71
+ # [num_tables] per-table seeds (int64)
72
+ seeds_ptr,
73
+ # [num_ngram_orders] ngram order values, e.g. [2, 3]
74
+ ngrams_ptr,
75
+ # Scalars
76
+ B, T: tl.constexpr, M, d_slot: tl.constexpr,
77
+ num_tables: tl.constexpr,
78
+ num_ngram_orders: tl.constexpr, # ≤ 4
79
+ num_heads: tl.constexpr, # ≤ 16
80
+ MAX_N: tl.constexpr, # max(ngram_orders), e.g. 3
81
+ BLOCK_D: tl.constexpr, # power-of-2 ≥ d_slot
82
+ ):
83
+ b_idx = tl.program_id(0)
84
+ t_idx = tl.program_id(1)
85
+
86
+ d_offs = tl.arange(0, BLOCK_D)
87
+ d_mask = d_offs < d_slot
88
+
89
+ # Pre-load the last MAX_N canonical tokens ending at t_idx (causal).
90
+ # Positions before 0 are treated as padding (0). We unroll to pure scalars for Triton compatibility.
91
+ # Crucial Safety: clamp pos using tl.where to ensure pointer arithmetic is never negative.
92
+ c0 = tl.full((), 0, dtype=tl.int64)
93
+ c1 = tl.full((), 0, dtype=tl.int64)
94
+ c2 = tl.full((), 0, dtype=tl.int64)
95
+ c3 = tl.full((), 0, dtype=tl.int64)
96
+
97
+ if MAX_N >= 1:
98
+ pos_raw = t_idx - (MAX_N - 1 - 0)
99
+ valid = pos_raw >= 0
100
+ pos = tl.where(valid, pos_raw, 0)
101
+ tok = tl.load(
102
+ canonical_ptr + b_idx * stride_cb + pos * stride_ct,
103
+ mask=valid, other=0,
104
+ )
105
+ c0 = tl.where(valid, tok.to(tl.int64), tl.full((), 0, dtype=tl.int64))
106
+ if MAX_N >= 2:
107
+ pos_raw = t_idx - (MAX_N - 1 - 1)
108
+ valid = pos_raw >= 0
109
+ pos = tl.where(valid, pos_raw, 0)
110
+ tok = tl.load(
111
+ canonical_ptr + b_idx * stride_cb + pos * stride_ct,
112
+ mask=valid, other=0,
113
+ )
114
+ c1 = tl.where(valid, tok.to(tl.int64), tl.full((), 0, dtype=tl.int64))
115
+ if MAX_N >= 3:
116
+ pos_raw = t_idx - (MAX_N - 1 - 2)
117
+ valid = pos_raw >= 0
118
+ pos = tl.where(valid, pos_raw, 0)
119
+ tok = tl.load(
120
+ canonical_ptr + b_idx * stride_cb + pos * stride_ct,
121
+ mask=valid, other=0,
122
+ )
123
+ c2 = tl.where(valid, tok.to(tl.int64), tl.full((), 0, dtype=tl.int64))
124
+ if MAX_N >= 4:
125
+ pos_raw = t_idx - (MAX_N - 1 - 3)
126
+ valid = pos_raw >= 0
127
+ pos = tl.where(valid, pos_raw, 0)
128
+ tok = tl.load(
129
+ canonical_ptr + b_idx * stride_cb + pos * stride_ct,
130
+ mask=valid, other=0,
131
+ )
132
+ c3 = tl.where(valid, tok.to(tl.int64), tl.full((), 0, dtype=tl.int64))
133
+
134
+ # Iterate over all tables; loop bounds are constexpr → fully unrolled by compiler.
135
+ for n_ord in tl.static_range(4): # ≤ num_ngram_orders
136
+ if n_ord < num_ngram_orders:
137
+ n = tl.load(ngrams_ptr + n_ord).to(tl.int32)
138
+
139
+ for k in tl.static_range(16): # ≤ num_heads
140
+ if k < num_heads:
141
+ # Safety: Compute unique table_idx directly from static loop indices to avoid mutable variable register compilation bugs.
142
+ table_idx = n_ord * num_heads + k
143
+ seed = tl.load(seeds_ptr + table_idx).to(tl.int64)
144
+
145
+ # XOR-multiplicative hash over the suffix n-gram.
146
+ # loop over MAX_N positions; positions outside the suffix are skipped.
147
+ h = seed
148
+ for i in tl.static_range(MAX_N):
149
+ include = i >= (MAX_N - n)
150
+ tok = tl.full((), 0, dtype=tl.int64)
151
+ if i == 0:
152
+ tok = c0
153
+ elif i == 1:
154
+ tok = c1
155
+ elif i == 2:
156
+ tok = c2
157
+ elif i == 3:
158
+ tok = c3
159
+ new_h = h * 2654435761 ^ tok
160
+ h = tl.where(include, new_h, h)
161
+
162
+ # Clamp absolute value of hash using tl.where for maximum Triton version safety
163
+ idx = tl.where(h >= 0, h, -h) % M
164
+
165
+ # Load d_slot floats from embed_tables[table_idx, idx]
166
+ emb_base = table_idx * stride_tn + idx * stride_tm
167
+ emb = tl.load(
168
+ tables_ptr + emb_base + d_offs * stride_td,
169
+ mask=d_mask, other=0.0,
170
+ )
171
+
172
+ # Write to output[b, t, table_idx*d_slot : (table_idx+1)*d_slot]
173
+ out_base = b_idx * stride_ob + t_idx * stride_ot + table_idx * d_slot
174
+ tl.store(
175
+ output_ptr + out_base + d_offs,
176
+ emb.to(output_ptr.dtype.element_ty),
177
+ mask=d_mask,
178
+ )
179
+
180
+
181
+ # ─────────────────────────────────────────────────────────────────────────────
182
+ # Custom Autograd Function for Fused Triton Training Lookup
183
+ # ─────────────────────────────────────────────────────────────────────────────
184
+
185
+ class FusedEngramLookupFunction(torch.autograd.Function):
186
+ @staticmethod
187
+ def forward(
188
+ ctx,
189
+ canonical,
190
+ embed_tables,
191
+ seeds,
192
+ ngram_orders_buf,
193
+ M,
194
+ d_slot,
195
+ num_tables,
196
+ num_ngram_orders,
197
+ num_heads,
198
+ ngram_orders,
199
+ ):
200
+ ctx.save_for_backward(canonical, seeds, ngram_orders_buf)
201
+ ctx.M = M
202
+ ctx.d_slot = d_slot
203
+ ctx.num_tables = num_tables
204
+ ctx.num_ngram_orders = num_ngram_orders
205
+ ctx.num_heads = num_heads
206
+ ctx.ngram_orders = ngram_orders
207
+ ctx.embed_tables_shape = embed_tables.shape
208
+
209
+ B, T = canonical.shape
210
+ BLOCK_D = triton.next_power_of_2(d_slot)
211
+
212
+ out = torch.empty(
213
+ B, T, num_tables * d_slot,
214
+ device=canonical.device, dtype=embed_tables.dtype,
215
+ )
216
+
217
+ tables = embed_tables.contiguous()
218
+
219
+ _engram_hash_lookup_kernel[(B, T)](
220
+ canonical.int().contiguous(), canonical.stride(0), canonical.stride(1),
221
+ out, out.stride(0), out.stride(1),
222
+ tables, tables.stride(0), tables.stride(1), tables.stride(2),
223
+ seeds.contiguous(),
224
+ ngram_orders_buf.contiguous(),
225
+ B, T, M, d_slot,
226
+ num_tables, num_ngram_orders, num_heads,
227
+ MAX_N=max(ngram_orders),
228
+ BLOCK_D=BLOCK_D,
229
+ )
230
+ return out
231
+
232
+ @staticmethod
233
+ def backward(ctx, grad_output):
234
+ canonical, seeds, ngram_orders_buf = ctx.saved_tensors
235
+ B, T = canonical.shape
236
+ device = canonical.device
237
+
238
+ # 1. Re-compute hashes for each table in vectorized form
239
+ all_hashes = torch.empty(ctx.num_tables, B * T, dtype=torch.long, device=device)
240
+ table_idx = 0
241
+ for n_idx, n in enumerate(ctx.ngram_orders):
242
+ padded = F.pad(canonical, (n - 1, 0), value=0)
243
+ ngrams = padded.unfold(dimension=1, size=n, step=1)
244
+ for k in range(ctx.num_heads):
245
+ seed = int(seeds[table_idx].item())
246
+ h = torch.full(ngrams.shape[:2], seed, dtype=torch.long, device=device)
247
+ for i in range(ngrams.shape[-1]):
248
+ h = h * 2654435761 ^ ngrams[..., i]
249
+ h = h.abs() % ctx.M
250
+ all_hashes[table_idx] = h.view(B * T)
251
+ table_idx += 1
252
+
253
+ # 2. Reshape and permute grad_output from [B, T, num_tables * d_slot] back to [num_tables, B * T, d_slot]
254
+ grad_out_reshaped = grad_output.reshape(B, T, ctx.num_tables, ctx.d_slot).permute(2, 0, 1, 3).reshape(ctx.num_tables, B * T, ctx.d_slot)
255
+
256
+ # 3. Accumulate gradients into grad_embed_tables using PyTorch's native CUDA-optimized index_put_ scatter-add
257
+ grad_embed_tables = torch.zeros(ctx.embed_tables_shape, dtype=grad_output.dtype, device=device)
258
+ tbl_idx = torch.arange(ctx.num_tables, device=device).unsqueeze(1).expand(ctx.num_tables, B * T)
259
+
260
+ grad_embed_tables.index_put_((tbl_idx, all_hashes), grad_out_reshaped, accumulate=True)
261
+
262
+ # Return gradients matching forward arguments (None for non-tensor / constant arguments)
263
+ return None, grad_embed_tables, None, None, None, None, None, None, None, None
264
+
265
+
266
+ # ─────────────────────────────────────────────────────────────────────────────
267
+ # Lightweight RMSNorm (standalone; avoids circular import from quasar_rope)
268
+ # ─────────────────────────────────────────────────────────────────────────────
269
+
270
+ class _RMSNorm(nn.Module):
271
+ def __init__(self, dim: int, eps: float = 1e-6):
272
+ super().__init__()
273
+ self.weight = nn.Parameter(torch.ones(dim))
274
+ self.eps = eps
275
+
276
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
277
+ dtype = x.dtype
278
+ x = x.float()
279
+ x = x * torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + self.eps)
280
+ return (self.weight * x).to(dtype)
281
+
282
+
283
+ # ─────────────────────────────────────────────────────────────────────────────
284
+ # EngramModule
285
+ # ─────────────────────────────────────────────────────────────────────────────
286
+
287
+ class EngramModule(nn.Module):
288
+ """
289
+ Engram Conditional Memory Module (DeepSeek-AI, arXiv:2601.07372).
290
+
291
+ Replaces expensive attention layers for static N-gram patterns with
292
+ O(1) hash-table lookups gated into the hidden state.
293
+
294
+ All operations are fully vectorized — no Python loops over T or B:
295
+ • N-gram extraction: torch.unfold (single op)
296
+ • Hash computation: vectorized XOR accumulation (loop over n=2..3 only)
297
+ • Embedding lookup: batched advanced indexing (single gather)
298
+ • Conv: nn.Conv1d with causal pad + slice
299
+ """
300
+
301
+ def __init__(
302
+ self,
303
+ vocab_size: int,
304
+ d_model: int,
305
+ d_mem: int,
306
+ num_heads: int = 8,
307
+ ngram_orders: list = None,
308
+ target_slots: int = 5_700_000,
309
+ n_layers: int = 24,
310
+ ):
311
+ super().__init__()
312
+
313
+ if ngram_orders is None:
314
+ ngram_orders = [2, 3]
315
+
316
+ self.vocab_size = vocab_size
317
+ self.d_model = d_model
318
+ self.d_mem = d_mem
319
+ self.num_heads = num_heads
320
+ self.ngram_orders = list(ngram_orders)
321
+ self.num_ngram_orders = len(ngram_orders)
322
+ self.num_tables = self.num_ngram_orders * num_heads
323
+ self.n_layers = n_layers
324
+
325
+ # ── A. Tokenizer Compression Buffer ──────────────────────────────────
326
+ # Surjective P: V → V', ~23% compression.
327
+ # Deterministic multiplicative hash — no tokenizer object needed at
328
+ # construction time (avoids FSDP serialization problems).
329
+ compressed_size = max(1, int(vocab_size * 0.77))
330
+ self.compressed_vocab_size = compressed_size
331
+ token_map = (
332
+ torch.arange(vocab_size, dtype=torch.long) * 2654435761
333
+ ) % compressed_size
334
+ self.register_buffer('token_map', token_map)
335
+
336
+ # ── B. Embedding Tables ───────────────────────────────────────────────
337
+ # All num_tables share the same prime size M for vectorized indexing.
338
+ slots_per_table = max(1, target_slots // self.num_tables)
339
+ self.M = _next_prime(slots_per_table)
340
+ self.d_slot = max(16, d_mem // max(1, self.num_tables))
341
+ self.total_embed_dim = self.num_tables * self.d_slot
342
+
343
+ # Single parameter tensor — enables batched advanced-index gather.
344
+ self.embed_tables = nn.Parameter(
345
+ torch.empty(self.num_tables, self.M, self.d_slot)
346
+ )
347
+
348
+ # Per-table hashing seeds (non-trainable).
349
+ seeds = torch.randint(1, 2 ** 31 - 1, (self.num_tables,), dtype=torch.long)
350
+ self.register_buffer('seeds', seeds)
351
+
352
+ # N-gram order list as a buffer for the Triton kernel.
353
+ self.register_buffer(
354
+ 'ngram_orders_buf',
355
+ torch.tensor(self.ngram_orders, dtype=torch.long),
356
+ )
357
+
358
+ # ── C. Projection total_embed_dim → d_mem ────────────────────────────
359
+ self.embed_proj = nn.Linear(self.total_embed_dim, d_mem, bias=False)
360
+
361
+ # ── D. Context-aware gating ───────────────────────────────────────────
362
+ self.q_proj = nn.Linear(d_model, d_mem, bias=False)
363
+ self.W_K = nn.Linear(d_mem, d_mem, bias=False)
364
+ self.W_V = nn.Linear(d_mem, d_mem, bias=False)
365
+
366
+ # ── E. Causal depthwise Conv1d ────────────────────────────────────────
367
+ # kernel=4, dilation=3 → causal receptive field = 1 + (4-1)*3 = 10
368
+ self.kernel_size = 4
369
+ self.dilation = 3
370
+ self.conv_norm = _RMSNorm(d_mem)
371
+ self.conv = nn.Conv1d(
372
+ d_mem, d_mem,
373
+ kernel_size=self.kernel_size,
374
+ dilation=self.dilation,
375
+ groups=d_mem, # depthwise
376
+ bias=False,
377
+ )
378
+
379
+ # ── F. Output projection d_mem → d_model ─────────────────────────────
380
+ self.out_proj = nn.Linear(d_mem, d_model, bias=False)
381
+
382
+ # Triton eligible when compile-time bounds fit the kernel
383
+ self._triton_ok = (
384
+ HAS_TRITON
385
+ and self.num_ngram_orders <= 4
386
+ and num_heads <= 16
387
+ and max(ngram_orders) <= 3
388
+ )
389
+ self.triton_training = True
390
+
391
+ self._init_weights()
392
+
393
+ # ── Initialization ────────────────────────────────────────────────────────
394
+
395
+ def _init_weights(self):
396
+ # 1. Deterministic buffer re-population (bypasses meta-device empty uninitialized memory)
397
+ if hasattr(self, "token_map") and self.token_map is not None:
398
+ dev = "cpu" if self.token_map.device.type == "meta" else self.token_map.device
399
+ t_map = (torch.arange(self.vocab_size, dtype=torch.long, device=dev) * 2654435761) % self.compressed_vocab_size
400
+ self.token_map.data.copy_(t_map)
401
+
402
+ if hasattr(self, "seeds") and self.seeds is not None:
403
+ # Deterministic hash seeds across all ranks
404
+ g = torch.Generator().manual_seed(42)
405
+ dev = "cpu" if self.seeds.device.type == "meta" else self.seeds.device
406
+ s_t = torch.randint(1, 2 ** 31 - 1, (self.num_tables,), dtype=torch.long, device=dev, generator=g)
407
+ self.seeds.data.copy_(s_t)
408
+
409
+ if hasattr(self, "ngram_orders_buf") and self.ngram_orders_buf is not None:
410
+ dev = "cpu" if self.ngram_orders_buf.device.type == "meta" else self.ngram_orders_buf.device
411
+ ord_buf = torch.tensor(self.ngram_orders, dtype=torch.long, device=dev)
412
+ self.ngram_orders_buf.data.copy_(ord_buf)
413
+
414
+ trinity_std = 0.5 / math.sqrt(self.d_model)
415
+ scale_factor = 1.0 / math.sqrt(2 * self.n_layers)
416
+
417
+ # 2. Deep init on output → zero-init to guarantee exactly zero output at step 0
418
+ nn.init.zeros_(self.out_proj.weight)
419
+ # Gating projections: standard Trinity
420
+ nn.init.normal_(self.q_proj.weight, std=trinity_std)
421
+ nn.init.normal_(self.W_K.weight, std=trinity_std)
422
+ nn.init.normal_(self.W_V.weight, std=trinity_std)
423
+ # embed_proj: standard Trinity
424
+ nn.init.normal_(self.embed_proj.weight, std=trinity_std)
425
+ # Conv: zero init → identity pass-through at step 0
426
+ nn.init.zeros_(self.conv.weight)
427
+ # Embedding tables: small normal (paper standard)
428
+ nn.init.normal_(self.embed_tables, std=0.01)
429
+ # Conv norm: fill with ones
430
+ if hasattr(self.conv_norm, "weight") and self.conv_norm.weight is not None:
431
+ nn.init.ones_(self.conv_norm.weight)
432
+
433
+ # Check for any non-finite initialization values
434
+ for name, p in [("out_proj", self.out_proj.weight), ("q_proj", self.q_proj.weight),
435
+ ("W_K", self.W_K.weight), ("W_V", self.W_V.weight),
436
+ ("embed_proj", self.embed_proj.weight), ("conv", self.conv.weight),
437
+ ("embed_tables", self.embed_tables), ("conv_norm", self.conv_norm.weight)]:
438
+ if p.device.type != "meta":
439
+ if not torch.isfinite(p).all():
440
+ print(f"[engram-init-warn] Parameter {name} contains non-finite values! Re-initializing with zeros.", flush=True)
441
+ nn.init.zeros_(p)
442
+
443
+ # ── Core helpers ──────────────────────────────────────────────────────────
444
+
445
+ @staticmethod
446
+ def _hash_ngrams(ngrams: torch.Tensor, table_size: int, seed: int) -> torch.Tensor:
447
+ """
448
+ Vectorized XOR-multiplicative hash.
449
+ ngrams: [B, T, n] — n ∈ {2, 3}, compile-time constant.
450
+ Returns: [B, T] — indices into embedding table.
451
+ No loop over T or B; only loops over n (≤ 3).
452
+ """
453
+ h = torch.full(ngrams.shape[:2], seed, dtype=torch.long, device=ngrams.device)
454
+ for i in range(ngrams.shape[-1]): # n iterations, NOT T
455
+ h = h * 2654435761 ^ ngrams[..., i]
456
+ return h.abs() % table_size
457
+
458
+ def _lookup_pytorch(self, canonical: torch.Tensor) -> torch.Tensor:
459
+ """
460
+ Pure-PyTorch path: fully vectorized, no T/B loops.
461
+
462
+ Steps:
463
+ 1. For each n-gram order, extract suffix n-grams via unfold → [B, T, n]
464
+ 2. Hash all (n, k) pairs → [num_tables, B, T]
465
+ 3. Batched advanced-index gather from embed_tables → [num_tables, B*T, d_slot]
466
+ 4. Reshape to [B, T, total_embed_dim]
467
+ """
468
+ B, T = canonical.shape
469
+ device = canonical.device
470
+
471
+ # Step 1+2: collect hashes for all tables — loop over num_tables (≤ 32, not over T)
472
+ all_hashes = torch.empty(self.num_tables, B * T, dtype=torch.long, device=device)
473
+ table_idx = 0
474
+ seeds_cpu = self.seeds.cpu().tolist() if hasattr(self, "seeds") and self.seeds is not None else []
475
+ for n_idx, n in enumerate(self.ngram_orders): # 2 or 3 iterations
476
+ # Vectorized n-gram extraction: unfold over T → [B, T, n]
477
+ padded = F.pad(canonical, (n - 1, 0), value=0) # [B, T+n-1]
478
+ ngrams = padded.unfold(dimension=1, size=n, step=1) # [B, T, n]
479
+
480
+ for k in range(self.num_heads): # num_heads iterations (≤ 16)
481
+ seed = seeds_cpu[table_idx] if table_idx < len(seeds_cpu) else 42
482
+ h = self._hash_ngrams(ngrams, self.M, seed) # [B, T]
483
+ all_hashes[table_idx] = h.view(B * T)
484
+ table_idx += 1
485
+
486
+ # Step 3: Single batched gather — no loop over T
487
+ # embed_tables: [num_tables, M, d_slot]
488
+ # all_hashes: [num_tables, B*T]
489
+ # Expand table index for advanced indexing
490
+ tbl_idx = torch.arange(self.num_tables, device=device).unsqueeze(1).expand(
491
+ self.num_tables, B * T
492
+ ) # [num_tables, B*T]
493
+ embeddings = self.embed_tables[tbl_idx, all_hashes] # [num_tables, B*T, d_slot]
494
+
495
+ # Step 4: Reshape to [B, T, total_embed_dim]
496
+ embeddings = embeddings.permute(1, 0, 2) # [B*T, num_tables, d_slot]
497
+ return embeddings.reshape(B, T, self.total_embed_dim)
498
+
499
+ def _lookup_triton(self, canonical: torch.Tensor) -> torch.Tensor:
500
+ """
501
+ Triton path: fused hash + lookup in a single SRAM pass.
502
+ Uses FusedEngramLookupFunction to support exact backward auto-differentiation in training.
503
+ """
504
+ return FusedEngramLookupFunction.apply(
505
+ canonical,
506
+ self.embed_tables,
507
+ self.seeds,
508
+ self.ngram_orders_buf,
509
+ self.M,
510
+ self.d_slot,
511
+ self.num_tables,
512
+ self.num_ngram_orders,
513
+ self.num_heads,
514
+ self.ngram_orders,
515
+ )
516
+
517
+ # ── Forward ───────────────────────────────────────────────────────────────
518
+
519
+ def forward(
520
+ self,
521
+ input_ids: torch.Tensor, # [B, T] raw token IDs
522
+ hidden_states: torch.Tensor, # [B, T, d_model]
523
+ ) -> tuple[torch.Tensor, torch.Tensor]:
524
+ """
525
+ Returns:
526
+ engram_out : [B, T, d_model] — to add to residual stream
527
+ alpha_mean : scalar tensor — mean gate value for LatentMemory suppression
528
+ """
529
+ B, T = input_ids.shape
530
+ orig_dtype = hidden_states.dtype
531
+
532
+ # ── A. Token compression ─────────────────────────────────────────────
533
+ # Single gather op — no loop
534
+ canonical = self.token_map[input_ids.clamp(0, self.vocab_size - 1)] # [B, T]
535
+
536
+ # ── B+C. Hash → lookup → project to d_mem ───────────────────────────
537
+ use_triton_lookup = self._triton_ok and canonical.is_cuda and (
538
+ self.training is False or bool(getattr(self, "triton_training", False))
539
+ )
540
+ if use_triton_lookup:
541
+ raw_embed = self._lookup_triton(canonical) # [B, T, total_embed_dim]
542
+ else:
543
+ raw_embed = self._lookup_pytorch(canonical) # [B, T, total_embed_dim]
544
+
545
+ raw_embed = raw_embed.to(orig_dtype)
546
+
547
+ debug_engram = bool(int(os.environ.get("ENGRAM_DEBUG", "0")))
548
+ if debug_engram:
549
+ print(f"[engram-debug] embed_tables: finite={torch.isfinite(self.embed_tables).all().item()} min={self.embed_tables.float().min().item():.6g} max={self.embed_tables.float().max().item():.6g}", flush=True)
550
+ print(f"[engram-debug] hidden_states: finite={torch.isfinite(hidden_states).all().item()} min={hidden_states.float().min().item():.6g} max={hidden_states.float().max().item():.6g}", flush=True)
551
+ print(f"[engram-debug] raw_embed: finite={torch.isfinite(raw_embed).all().item()} min={raw_embed.float().min().item():.6g} max={raw_embed.float().max().item():.6g}", flush=True)
552
+
553
+ raw_embed = torch.nan_to_num(raw_embed, nan=0.0, posinf=0.0, neginf=0.0).clamp_(-10.0, 10.0)
554
+ e_t = self.embed_proj(raw_embed) # [B, T, d_mem]
555
+ if debug_engram:
556
+ print(f"[engram-debug] e_t: finite={torch.isfinite(e_t).all().item()} min={e_t.float().min().item():.6g} max={e_t.float().max().item():.6g}", flush=True)
557
+ e_t = torch.nan_to_num(e_t, nan=0.0, posinf=0.0, neginf=0.0).clamp_(-100.0, 100.0)
558
+
559
+ # ── D. Context-aware gating ──────────────────────────────────────────
560
+ h_proj = self.q_proj(hidden_states) # [B, T, d_mem]
561
+ k_t = self.W_K(e_t) # [B, T, d_mem]
562
+ v_t = self.W_V(e_t) # [B, T, d_mem]
563
+ if debug_engram:
564
+ print(f"[engram-debug] h_proj: finite={torch.isfinite(h_proj).all().item()} min={h_proj.float().min().item():.6g} max={h_proj.float().max().item():.6g}", flush=True)
565
+ print(f"[engram-debug] k_t: finite={torch.isfinite(k_t).all().item()} min={k_t.float().min().item():.6g} max={k_t.float().max().item():.6g}", flush=True)
566
+ print(f"[engram-debug] v_t: finite={torch.isfinite(v_t).all().item()} min={v_t.float().min().item():.6g} max={v_t.float().max().item():.6g}", flush=True)
567
+
568
+ h_proj = torch.nan_to_num(h_proj, nan=0.0, posinf=0.0, neginf=0.0).clamp_(-100.0, 100.0)
569
+ k_t = torch.nan_to_num(k_t, nan=0.0, posinf=0.0, neginf=0.0).clamp_(-100.0, 100.0)
570
+ v_t = torch.nan_to_num(v_t, nan=0.0, posinf=0.0, neginf=0.0).clamp_(-100.0, 100.0)
571
+
572
+ # L2-normalize for stability (matches Quasar key normalization)
573
+ q_norm = F.normalize(h_proj.float(), dim=-1, eps=1e-6).to(orig_dtype)
574
+ k_norm = F.normalize(k_t.float(), dim=-1, eps=1e-6).to(orig_dtype)
575
+
576
+ # Scalar gate per token per position
577
+ alpha_logits = (q_norm * k_norm).sum(-1, keepdim=True).float() / math.sqrt(self.d_mem)
578
+ alpha_t = torch.sigmoid(alpha_logits.clamp_(-30.0, 30.0)).to(orig_dtype) # [B, T, 1]
579
+ if debug_engram:
580
+ print(f"[engram-debug] alpha_t: finite={torch.isfinite(alpha_t).all().item()} min={alpha_t.float().min().item():.6g} max={alpha_t.float().max().item():.6g}", flush=True)
581
+ gated = alpha_t * v_t # [B, T, d_mem]
582
+ gated = torch.nan_to_num(gated, nan=0.0, posinf=0.0, neginf=0.0).clamp_(-100.0, 100.0)
583
+
584
+ # ── E. Causal depthwise conv ─────────────────────────────────────────
585
+ # Fully vectorized: F.pad + Conv1d + slice — no loop over T
586
+ causal_pad = (self.kernel_size - 1) * self.dilation
587
+ g_norm = self.conv_norm(gated) # [B, T, d_mem]
588
+ if debug_engram:
589
+ print(f"[engram-debug] gated: finite={torch.isfinite(gated).all().item()} min={gated.float().min().item():.6g} max={gated.float().max().item():.6g}", flush=True)
590
+ print(f"[engram-debug] conv_norm.weight: finite={torch.isfinite(self.conv_norm.weight).all().item()} min={self.conv_norm.weight.float().min().item():.6g} max={self.conv_norm.weight.float().max().item():.6g}", flush=True)
591
+ print(f"[engram-debug] g_norm: finite={torch.isfinite(g_norm).all().item()} min={g_norm.float().min().item():.6g} max={g_norm.float().max().item():.6g}", flush=True)
592
+ g_norm = torch.nan_to_num(g_norm, nan=0.0, posinf=0.0, neginf=0.0).clamp_(-100.0, 100.0)
593
+ g_t = g_norm.transpose(1, 2) # [B, d_mem, T]
594
+ g_t = F.pad(g_t, (causal_pad, 0)) # [B, d_mem, T+pad]
595
+ g_t = self.conv(g_t)[..., :T] # [B, d_mem, T]
596
+ g_t = F.silu(g_t).transpose(1, 2) # [B, T, d_mem]
597
+ Y = g_t + gated # residual
598
+ if debug_engram:
599
+ print(f"[engram-debug] Y: finite={torch.isfinite(Y).all().item()} min={Y.float().min().item():.6g} max={Y.float().max().item():.6g}", flush=True)
600
+ Y = torch.nan_to_num(Y, nan=0.0, posinf=0.0, neginf=0.0).clamp_(-100.0, 100.0)
601
+
602
+ # ── F. Output projection ─────────────────────────────────────────────
603
+ engram_out = self.out_proj(Y) # [B, T, d_model]
604
+ if debug_engram:
605
+ print(f"[engram-debug] engram_out: finite={torch.isfinite(engram_out).all().item()} min={engram_out.float().min().item():.6g} max={engram_out.float().max().item():.6g}", flush=True)
606
+ engram_out = torch.nan_to_num(engram_out, nan=0.0, posinf=0.0, neginf=0.0).clamp_(-100.0, 100.0)
607
+
608
+ # alpha_mean: mean gate activity — used by LatentMemory for suppression
609
+ alpha_mean = alpha_t.squeeze(-1) # [B, T]
610
+
611
+ return engram_out, alpha_mean
fla/__init__.py ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ # Lightweight package initializer for the vendored FLA subset used by Quasar.
2
+ #
3
+ # The upstream file eagerly imports every layer and model, which is slow and can
4
+ # hang on fresh training containers while optional kernels are being resolved.
5
+ # Import concrete modules directly, e.g. `from fla.layers.quasar import ...`.
6
+
7
+ __version__ = "0.1.0"
8
+ __all__ = []
fla/distributed_compat.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+ """
3
+ Centralized compatibility module for torch.distributed imports.
4
+ All distributed-related imports should go through here to handle environments
5
+ where distributed tensor APIs are not available.
6
+ """
7
+
8
+ import torch
9
+
10
+ # DeviceMesh
11
+ try:
12
+ from torch.distributed import DeviceMesh
13
+ except ImportError:
14
+ try:
15
+ from torch.distributed.device_mesh import DeviceMesh
16
+ except ImportError:
17
+ DeviceMesh = None
18
+
19
+ # DTensor
20
+ try:
21
+ from torch.distributed.tensor import DTensor
22
+ except (ImportError, AttributeError):
23
+ DTensor = None
24
+
25
+ # Replicate, Shard, distribute_module, Placement
26
+ try:
27
+ from torch.distributed.tensor import Placement, Replicate, Shard, distribute_module
28
+ except (ImportError, AttributeError):
29
+ Placement = Replicate = Shard = distribute_module = None
30
+
31
+ # ParallelStyle
32
+ try:
33
+ from torch.distributed.tensor.parallel import ParallelStyle
34
+ except (ImportError, AttributeError):
35
+ ParallelStyle = None
36
+
37
+ # Convenience flag
38
+ HAS_DISTRIBUTED = all([
39
+ DeviceMesh is not None,
40
+ DTensor is not None,
41
+ Placement is not None,
42
+ Replicate is not None,
43
+ Shard is not None,
44
+ distribute_module is not None,
45
+ ParallelStyle is not None,
46
+ ])
47
+
48
+ __all__ = [
49
+ 'DeviceMesh',
50
+ 'DTensor',
51
+ 'Placement',
52
+ 'Replicate',
53
+ 'Shard',
54
+ 'distribute_module',
55
+ 'ParallelStyle',
56
+ 'HAS_DISTRIBUTED',
57
+ ]
fla/layers/__init__.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ # Keep layer package imports lazy. Import specific layer modules directly.
2
+
3
+ __all__ = []
fla/layers/abc.py ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+
3
+ from __future__ import annotations
4
+
5
+ import warnings
6
+ from typing import TYPE_CHECKING
7
+
8
+ import torch
9
+ import torch.nn as nn
10
+ from einops import rearrange
11
+
12
+ from fla.layers.utils import get_layer_cache, update_layer_cache
13
+ from fla.modules import FusedRMSNormGated, RMSNorm, RotaryEmbedding, ShortConvolution
14
+ from fla.modules.activations import swiglu, swish
15
+ from fla.ops.abc.chunk import chunk_abc
16
+
17
+ if TYPE_CHECKING:
18
+ from fla.models.utils import Cache
19
+
20
+
21
+ class ABCAttention(nn.Module):
22
+
23
+ def __init__(
24
+ self,
25
+ hidden_size: int = 1024,
26
+ expand_k: float = 0.5,
27
+ expand_v: float = 1.0,
28
+ num_heads: int = 4,
29
+ use_short_conv: bool = False,
30
+ conv_size: int = 4,
31
+ conv_bias: bool = False,
32
+ num_slots: int | None = None,
33
+ elementwise_affine: bool | None = True,
34
+ norm_eps: float = 1e-5,
35
+ gate_low_rank_dim: int = 16,
36
+ gate_logit_normalizer: int = 16,
37
+ use_rope: bool = True,
38
+ use_input_gate: bool = False,
39
+ use_output_gate: bool = True,
40
+ use_norm: bool = True,
41
+ clamp_min: float | None = -32,
42
+ clamp_max: float | None = 32,
43
+ layer_idx: int | None = None,
44
+ **kwargs,
45
+ ) -> ABCAttention:
46
+ super().__init__()
47
+
48
+ self.hidden_size = hidden_size
49
+ self.expand_k = expand_k
50
+ self.expand_v = expand_v
51
+ self.num_heads = num_heads
52
+ self.key_dim = int(self.hidden_size * self.expand_k)
53
+ self.value_dim = int(self.hidden_size * self.expand_v)
54
+ self.head_k_dim = self.key_dim // self.num_heads
55
+ self.head_v_dim = self.value_dim // self.num_heads
56
+
57
+ self.use_short_conv = use_short_conv
58
+ self.conv_size = conv_size
59
+ self.conv_bias = conv_bias
60
+
61
+ self.gate_low_rank_dim = gate_low_rank_dim
62
+ self.gate_logit_normalizer = gate_logit_normalizer
63
+
64
+ self.use_rope = use_rope
65
+ self.use_input_gate = use_input_gate
66
+ self.use_output_gate = use_output_gate
67
+ self.use_norm = use_norm
68
+
69
+ if num_slots is None:
70
+ num_slots = self.head_k_dim
71
+ self.num_slots = num_slots
72
+
73
+ self.norm_eps = norm_eps
74
+
75
+ self.clamp_min = clamp_min
76
+ self.clamp_max = clamp_max
77
+ self.layer_idx = layer_idx
78
+
79
+ if layer_idx is None:
80
+ warnings.warn(
81
+ f"Instantiating {self.__class__.__name__} without passing `layer_idx` is not recommended and will "
82
+ "to errors during the forward call, if caching is used. Please make sure to provide a `layer_idx` "
83
+ "when creating this class.",
84
+ )
85
+
86
+ self.q_proj = nn.Linear(self.hidden_size, self.key_dim, bias=False)
87
+ self.k_proj = nn.Linear(self.hidden_size, self.key_dim, bias=False)
88
+ self.v_proj = nn.Linear(self.hidden_size, self.value_dim, bias=False)
89
+
90
+ if use_output_gate:
91
+ self.g_proj = nn.Linear(self.hidden_size, self.value_dim, bias=False)
92
+ self.s_proj = nn.Linear(self.hidden_size, self.num_heads * self.num_slots, bias=False)
93
+ self.o_proj = nn.Linear(self.value_dim, self.hidden_size, bias=False)
94
+
95
+ if use_short_conv:
96
+ self.conv_size = conv_size
97
+ self.q_conv1d = ShortConvolution(
98
+ hidden_size=self.key_dim,
99
+ kernel_size=conv_size,
100
+ bias=conv_bias,
101
+ activation='silu',
102
+ )
103
+ self.k_conv1d = ShortConvolution(
104
+ hidden_size=self.key_dim,
105
+ kernel_size=conv_size,
106
+ bias=conv_bias,
107
+ activation='silu',
108
+ )
109
+ self.v_conv1d = ShortConvolution(
110
+ hidden_size=self.value_dim,
111
+ kernel_size=conv_size,
112
+ bias=conv_bias,
113
+ activation='silu',
114
+ )
115
+
116
+ if self.use_norm:
117
+ if self.use_output_gate:
118
+ self.g_norm = FusedRMSNormGated(
119
+ hidden_size=self.head_v_dim,
120
+ elementwise_affine=elementwise_affine,
121
+ eps=norm_eps,
122
+ )
123
+ else:
124
+ self.g_norm = RMSNorm(
125
+ hidden_size=self.head_v_dim,
126
+ elementwise_affine=elementwise_affine,
127
+ eps=norm_eps,
128
+ dtype=torch.float32,
129
+ )
130
+
131
+ if self.use_rope:
132
+ self.rotary = RotaryEmbedding(self.head_k_dim)
133
+
134
+ def forward(
135
+ self,
136
+ hidden_states: torch.Tensor,
137
+ attention_mask: torch.Tensor | None = None,
138
+ past_key_values: Cache | None = None,
139
+ use_cache: bool | None = False,
140
+ output_attentions: bool | None = False,
141
+ **kwargs,
142
+ ) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
143
+ if attention_mask is not None:
144
+ assert len(attention_mask.shape) == 2, (
145
+ "Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
146
+ "for padding purposes (0 indicating padding). "
147
+ "Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
148
+ )
149
+
150
+ last_state = get_layer_cache(self, past_key_values)
151
+
152
+ cu_seqlens = kwargs.get('cu_seqlens')
153
+ if cu_seqlens is not None:
154
+ raise NotImplementedError("Training with cu_seqlens is not supported yet for ABCAttention")
155
+ if self.use_short_conv:
156
+ conv_state_q, conv_state_k, conv_state_v = None, None, None
157
+ if last_state is not None:
158
+ conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
159
+ conv_mask = attention_mask[:, -hidden_states.shape[1]:] if attention_mask is not None else None
160
+ q, conv_state_q = self.q_conv1d(
161
+ x=self.q_proj(hidden_states),
162
+ mask=conv_mask,
163
+ cache=conv_state_q,
164
+ output_final_state=use_cache,
165
+ cu_seqlens=cu_seqlens,
166
+ )
167
+ k, conv_state_k = self.k_conv1d(
168
+ x=self.k_proj(hidden_states),
169
+ mask=conv_mask,
170
+ cache=conv_state_k,
171
+ output_final_state=use_cache,
172
+ cu_seqlens=cu_seqlens,
173
+ )
174
+ v, conv_state_v = self.v_conv1d(
175
+ x=self.v_proj(hidden_states),
176
+ mask=conv_mask,
177
+ cache=conv_state_v,
178
+ output_final_state=use_cache,
179
+ cu_seqlens=cu_seqlens,
180
+ )
181
+ else:
182
+ q = self.q_proj(hidden_states)
183
+ k = self.k_proj(hidden_states)
184
+ v = self.v_proj(hidden_states)
185
+
186
+ if self.use_input_gate:
187
+ q, k, v = map(lambda x: swish(x), (q, k, v))
188
+ # dealing with left-padding
189
+ if attention_mask is not None:
190
+ v = v.mul_(attention_mask[:, -v.shape[-2]:, None])
191
+
192
+ q, k = map(lambda x: rearrange(x, '... (h d) -> ... h d', d=self.head_k_dim), (q, k))
193
+ v = rearrange(v, '... (h d) -> ... h d', d=self.head_v_dim)
194
+ if self.use_rope:
195
+ seqlen_offset = 0
196
+ if past_key_values is not None:
197
+ seqlen_offset = past_key_values.get_seq_length(self.layer_idx)
198
+ q, k = self.rotary(q, k, seqlen_offset=seqlen_offset)
199
+
200
+ s = rearrange(self.s_proj(hidden_states), '... (h m) -> ... h m', m=self.num_slots)
201
+ s = s.clamp_(self.clamp_min, self.clamp_max)
202
+
203
+ recurrent_state = last_state['recurrent_state'] if last_state is not None else None
204
+ o, recurrent_state = chunk_abc(
205
+ q=q,
206
+ k=k,
207
+ v=v,
208
+ s=s,
209
+ initial_state=recurrent_state,
210
+ output_final_state=use_cache,
211
+ )
212
+ update_layer_cache(
213
+ self,
214
+ past_key_values,
215
+ recurrent_state=recurrent_state,
216
+ conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
217
+ offset=q.shape[1],
218
+ )
219
+
220
+ if self.use_norm and not self.use_output_gate:
221
+ o = self.g_norm(o)
222
+ elif self.use_output_gate:
223
+ g = rearrange(self.g_proj(hidden_states), '... (h d) -> ... h d', d=self.head_v_dim)
224
+ o = self.g_norm(o, g) if self.use_norm else swiglu(g, o)
225
+ o = rearrange(o, '... h d -> ... (h d)')
226
+ o = self.o_proj(o)
227
+
228
+ return o, None, past_key_values
229
+
230
+ def state_size(self, seq_len: int = 2048):
231
+ return 2 * self.num_slots * self.hidden_size
fla/layers/attn.py ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+
3
+ from __future__ import annotations
4
+
5
+ import warnings
6
+ from typing import TYPE_CHECKING
7
+
8
+ import torch
9
+ import torch.nn as nn
10
+ from einops import rearrange
11
+ from transformers.utils import logging
12
+
13
+ from fla.layers.utils import pad_input, unpad_input
14
+ from fla.modules import RMSNorm, RotaryEmbedding
15
+ from fla.ops.utils.index import prepare_lens_from_mask
16
+
17
+ if TYPE_CHECKING:
18
+ from fla.models.utils import Cache
19
+
20
+ try:
21
+ from flash_attn import flash_attn_func, flash_attn_varlen_func
22
+ except ImportError:
23
+ warnings.warn(
24
+ "Flash Attention is not installed. Please install it via `pip install flash-attn --no-build-isolation`",
25
+ category=ImportWarning,
26
+ )
27
+ flash_attn_func = None
28
+
29
+ logger = logging.get_logger(__name__)
30
+
31
+
32
+ class Attention(nn.Module):
33
+
34
+ def __init__(
35
+ self,
36
+ hidden_size: int = 2048,
37
+ num_heads: int = 32,
38
+ num_kv_heads: int | None = None,
39
+ qkv_bias: bool = False,
40
+ qk_norm: bool = False,
41
+ window_size: int | None = None,
42
+ rope_theta: float | None = 10000.,
43
+ max_position_embeddings: int | None = None,
44
+ use_nope: bool = False,
45
+ layer_idx: int = None,
46
+ ):
47
+ super().__init__()
48
+
49
+ self.hidden_size = hidden_size
50
+ self.num_heads = num_heads
51
+ if num_kv_heads is None:
52
+ self.num_kv_heads = self.num_heads
53
+ else:
54
+ self.num_kv_heads = num_kv_heads
55
+ self.num_kv_groups = num_heads // self.num_kv_heads
56
+ self.head_dim = self.hidden_size // self.num_heads
57
+ self.kv_dim = self.num_kv_heads * self.head_dim
58
+ self.qkv_bias = qkv_bias
59
+ self.qk_norm = qk_norm
60
+
61
+ self.window_size = window_size
62
+ self.rope_theta = rope_theta
63
+ self.max_position_embeddings = max_position_embeddings
64
+ self.use_nope = use_nope
65
+ self.layer_idx = layer_idx
66
+
67
+ if flash_attn_func is None:
68
+ raise ImportError("Please install Flash Attention via `pip install flash-attn --no-build-isolation` first")
69
+
70
+ self.q_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=self.qkv_bias)
71
+ self.k_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=self.qkv_bias)
72
+ self.v_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=self.qkv_bias)
73
+ self.o_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=False)
74
+
75
+ if qk_norm:
76
+ self.q_norm = RMSNorm(self.head_dim, dtype=torch.float32)
77
+ self.k_norm = RMSNorm(self.head_dim, dtype=torch.float32)
78
+
79
+ self.rotary = RotaryEmbedding(dim=self.head_dim, base=self.rope_theta)
80
+
81
+ def forward(
82
+ self,
83
+ hidden_states: torch.Tensor,
84
+ attention_mask: torch.LongTensor | None = None,
85
+ past_key_values: Cache | None = None,
86
+ output_attentions: bool = False,
87
+ use_cache: bool = False,
88
+ **kwargs,
89
+ ) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
90
+ if attention_mask is not None:
91
+ assert len(attention_mask.shape) == 2, (
92
+ "Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
93
+ "for padding purposes (0 indicating padding). "
94
+ "Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
95
+ )
96
+
97
+ batch_size, q_len, _ = hidden_states.size()
98
+
99
+ q = rearrange(self.q_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
100
+ k = rearrange(self.k_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
101
+ v = rearrange(self.v_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
102
+
103
+ if self.qk_norm:
104
+ q, k = self.q_norm(q), self.k_norm(k)
105
+
106
+ # equivalent to cu_seqlens in `flash_attn`
107
+ cu_seqlens = kwargs.get('cu_seqlens')
108
+
109
+ seqlen_offset, max_seqlen = 0, q_len
110
+ if past_key_values is not None:
111
+ seqlen_offset = past_key_values.get_seq_length(self.layer_idx)
112
+ max_seqlen = q.shape[1] + seqlen_offset
113
+
114
+ if attention_mask is not None:
115
+ # to deliminate the offsets of padding tokens
116
+ seqlen_offset = seqlen_offset + prepare_lens_from_mask(attention_mask) - attention_mask.shape[-1]
117
+ max_seqlen = q.shape[1] + max(seqlen_offset)
118
+
119
+ if self.max_position_embeddings is not None:
120
+ max_seqlen = max(max_seqlen, self.max_position_embeddings)
121
+ if not self.use_nope:
122
+ q, k = self.rotary(q, k, seqlen_offset=seqlen_offset, max_seqlen=max_seqlen, cu_seqlens=cu_seqlens)
123
+
124
+ if past_key_values is not None:
125
+ cache_has_content = past_key_values.get_seq_length(self.layer_idx) > 0
126
+ k_cached, v_cached = past_key_values.update(
127
+ attn_state=(k.flatten(-2, -1), v.flatten(-2, -1)),
128
+ layer_idx=self.layer_idx,
129
+ offset=q_len,
130
+ cache_kwargs=dict(window_size=self.window_size),
131
+ )['attn_state']
132
+ if cache_has_content:
133
+ k, v = k_cached, v_cached
134
+ k = rearrange(k, '... (h d) -> ... h d', d=self.head_dim)
135
+ v = rearrange(v, '... (h d) -> ... h d', d=self.head_dim)
136
+
137
+ # Contains at least one padding token in the sequence
138
+ if attention_mask is not None:
139
+ if q.shape[1] == 1 and self.window_size is not None:
140
+ attention_mask = attention_mask[:, -self.window_size:]
141
+ q, (k, v), indices_q, cu_seqlens, max_seq_lens = unpad_input(q, (k, v), attention_mask, q_len)
142
+ cu_seqlens_q, cu_seqlens_k = cu_seqlens
143
+ max_seqlen_q, max_seqlen_k = max_seq_lens
144
+ o = flash_attn_varlen_func(
145
+ q, k, v,
146
+ cu_seqlens_q=cu_seqlens_q,
147
+ cu_seqlens_k=cu_seqlens_k,
148
+ max_seqlen_q=max_seqlen_q,
149
+ max_seqlen_k=max_seqlen_k,
150
+ causal=True,
151
+ window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
152
+ )
153
+ o = pad_input(o, indices_q, batch_size, q_len)
154
+ elif cu_seqlens is not None:
155
+ o = flash_attn_varlen_func(
156
+ q.squeeze(0), k.squeeze(0), v.squeeze(0),
157
+ cu_seqlens_q=cu_seqlens,
158
+ cu_seqlens_k=cu_seqlens,
159
+ max_seqlen_q=max_seqlen,
160
+ max_seqlen_k=max_seqlen,
161
+ causal=True,
162
+ window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
163
+ ).unsqueeze(0)
164
+ else:
165
+ o = flash_attn_func(
166
+ q, k, v,
167
+ causal=True,
168
+ window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
169
+ )
170
+ o = o.reshape(batch_size, q_len, -1)
171
+ o = self.o_proj(o)
172
+
173
+ if not output_attentions:
174
+ attentions = None
175
+
176
+ return o, attentions, past_key_values
fla/layers/based.py ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+
3
+ """
4
+ Linear attention in Based.
5
+ https://github.com/HazyResearch/zoology/blob/main/zoology/mixers/based.py
6
+ """
7
+
8
+ import torch
9
+ import torch.nn as nn
10
+ from einops import rearrange
11
+
12
+ from fla.modules.feature_map import TaylorFeatureMap
13
+ from fla.ops.based import parallel_based
14
+ from fla.ops.linear_attn import chunk_linear_attn, fused_chunk_linear_attn
15
+
16
+
17
+ class BasedLinearAttention(nn.Module):
18
+
19
+ def __init__(
20
+ self,
21
+ hidden_size: int,
22
+ feature_dim: int = 16,
23
+ num_key_value_heads: int = 12,
24
+ num_heads: int = 12,
25
+ feature_name: str = "taylor_exp",
26
+ eps: float = 1e-12,
27
+ causal: bool = True,
28
+ mode: str = "parallel",
29
+ ):
30
+ super().__init__()
31
+
32
+ self.hidden_size = hidden_size
33
+ self.mode = mode
34
+ self.feature_name = feature_name
35
+ self.feature_dim = feature_dim
36
+ self.num_key_value_heads = num_key_value_heads
37
+ self.num_heads = num_heads
38
+ self.head_dim = self.hidden_size // self.num_key_value_heads
39
+ assert self.hidden_size % self.head_dim == 0
40
+ self.causal = causal
41
+
42
+ self.q_proj = nn.Linear(self.hidden_size, self.feature_dim * self.num_heads, bias=False)
43
+ self.k_proj = nn.Linear(self.hidden_size, self.feature_dim * self.num_heads, bias=False)
44
+ self.v_proj = nn.Linear(self.hidden_size, self.num_key_value_heads * self.head_dim, bias=False)
45
+ self.o_proj = nn.Linear(self.num_heads * self.head_dim, self.hidden_size, bias=False)
46
+ self.dropout = nn.Identity()
47
+ self.feature_map = TaylorFeatureMap(feature_dim)
48
+ self.eps = eps
49
+
50
+ def forward(self, hidden_states: torch.Tensor, **kwargs):
51
+ mode = self.mode
52
+ q, k, v = self.q_proj(hidden_states), self.k_proj(hidden_states), self.v_proj(hidden_states)
53
+ q, k, v = map(lambda x: rearrange(x, "... (h d) -> ... h d", d=self.head_dim), [q, k, v])
54
+ if mode == "fused_chunk":
55
+ q, k = self.feature_map(q), self.feature_map(k)
56
+ o, _ = fused_chunk_linear_attn(q, k, v, normalize=True, scale=1)
57
+ elif mode == 'chunk':
58
+ q, k = self.feature_map(q), self.feature_map(k)
59
+ o, _ = chunk_linear_attn(q, k, v, normalize=True, scale=1)
60
+ elif mode == 'parallel':
61
+ assert q.shape[-1] <= 128
62
+ o = parallel_based(q, k, v, scale=1, use_norm=True)
63
+ o = rearrange(o, 'b t h d -> b t (h d)')
64
+ o = self.o_proj(o)
65
+ o = self.dropout(o)
66
+ return o
67
+
68
+ def forward_reference(self, hidden_states: torch.Tensor, **kwargs):
69
+ """
70
+ x (torch.Tensor): tensor of shape (b, d, t)
71
+ y (torch.Tensor): tensor of shape (b, d, t)
72
+ """
73
+ # hidden_states = hidden_states.transpose(1, 2)
74
+ b, t, _ = hidden_states.size()
75
+ q, k, v = self.q_proj(hidden_states), self.k_proj(hidden_states), self.v_proj(hidden_states)
76
+
77
+ q = q.view(b, t, self.num_heads, self.feature_dim).transpose(1, 2)
78
+ k = k.view(b, t, self.num_key_value_heads, self.feature_dim).transpose(1, 2)
79
+ v = v.view(b, t, self.num_key_value_heads, self.head_dim).transpose(1, 2)
80
+
81
+ # Linear attention
82
+ q, k = self.feature_map(q), self.feature_map(k)
83
+ q, k, v = q.unsqueeze(-2), k.unsqueeze(-2), v.unsqueeze(-1)
84
+
85
+ # Compute attention
86
+ if self.causal:
87
+ y = ((q * (k * v).cumsum(2)).sum(-1) / ((q * k.cumsum(2)).sum(-1) + self.eps))
88
+ else:
89
+ y = ((q * (k * v).sum(2, True)).sum(-1) / ((q * k.sum(2, True)).sum(-1) + self.eps))
90
+ y = rearrange(y, 'b h t d -> b t (h d)')
91
+ y = self.o_proj(y.to(hidden_states.dtype))
92
+ y = self.dropout(y)
93
+ return y.to(hidden_states.dtype)
fla/layers/bitattn.py ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+
3
+ from __future__ import annotations
4
+
5
+ import warnings
6
+ from typing import TYPE_CHECKING
7
+
8
+ import torch
9
+ import torch.nn as nn
10
+ from einops import rearrange
11
+ from transformers.utils import logging
12
+
13
+ from fla.layers.utils import pad_input, unpad_input
14
+ from fla.modules import RotaryEmbedding
15
+ from fla.modules.fused_bitlinear import FusedBitLinear
16
+ from fla.ops.utils.index import prepare_lens_from_mask
17
+
18
+ if TYPE_CHECKING:
19
+ from fla.models.utils import Cache
20
+
21
+ try:
22
+ from flash_attn import flash_attn_func, flash_attn_varlen_func
23
+ except ImportError:
24
+ warnings.warn(
25
+ "Flash Attention is not installed. Please install it via `pip install flash-attn --no-build-isolation`",
26
+ category=ImportWarning,
27
+ )
28
+ flash_attn_func = None
29
+
30
+ logger = logging.get_logger(__name__)
31
+
32
+
33
+ class BitAttention(nn.Module):
34
+
35
+ def __init__(
36
+ self,
37
+ hidden_size: int = 2048,
38
+ num_heads: int = 32,
39
+ num_kv_heads: int | None = None,
40
+ window_size: int | None = None,
41
+ rope_theta: float | None = 10000.,
42
+ max_position_embeddings: int | None = None,
43
+ norm_eps: float = 1e-5,
44
+ layer_idx: int = None,
45
+ ):
46
+ super().__init__()
47
+
48
+ self.num_heads = num_heads
49
+ if num_kv_heads is None:
50
+ self.num_kv_heads = self.num_heads
51
+ else:
52
+ self.num_kv_heads = num_kv_heads
53
+ self.num_kv_groups = num_heads // self.num_kv_heads
54
+ self.hidden_size = hidden_size
55
+ self.head_dim = self.hidden_size // self.num_heads
56
+ self.kv_dim = self.num_kv_heads * self.head_dim
57
+ self.kv_dim = self.num_kv_heads * self.head_dim
58
+ self.window_size = window_size
59
+ self.rope_theta = rope_theta
60
+ self.max_position_embeddings = max_position_embeddings
61
+ self.layer_idx = layer_idx
62
+
63
+ self.q_proj = FusedBitLinear(self.hidden_size, self.hidden_size, bias=False)
64
+ self.k_proj = FusedBitLinear(self.hidden_size, self.kv_dim, bias=False)
65
+ self.v_proj = FusedBitLinear(self.hidden_size, self.kv_dim, bias=False)
66
+ self.o_proj = FusedBitLinear(self.hidden_size, self.hidden_size, bias=False)
67
+
68
+ self.rotary = RotaryEmbedding(dim=self.head_dim, base=self.rope_theta)
69
+
70
+ def forward(
71
+ self,
72
+ hidden_states: torch.Tensor,
73
+ attention_mask: torch.LongTensor | None = None,
74
+ past_key_values: Cache | None = None,
75
+ output_attentions: bool = False,
76
+ use_cache: bool = False,
77
+ **kwargs,
78
+ ) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
79
+ if attention_mask is not None:
80
+ assert len(attention_mask.shape) == 2, (
81
+ "Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
82
+ "for padding purposes (0 indicating padding). "
83
+ "Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
84
+ )
85
+
86
+ batch_size, q_len, _ = hidden_states.size()
87
+
88
+ q = rearrange(self.q_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
89
+ k = rearrange(self.k_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
90
+ v = rearrange(self.v_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
91
+
92
+ # equivalent to cu_seqlens in `flash_attn`
93
+ cu_seqlens = kwargs.get('cu_seqlens')
94
+
95
+ seqlen_offset, max_seqlen = 0, q_len
96
+ if past_key_values is not None:
97
+ seqlen_offset = past_key_values.get_seq_length(self.layer_idx)
98
+ max_seqlen = q.shape[1] + seqlen_offset
99
+
100
+ if attention_mask is not None:
101
+ # to deliminate the offsets of padding tokens
102
+ seqlen_offset = seqlen_offset + prepare_lens_from_mask(attention_mask) - attention_mask.shape[-1]
103
+ max_seqlen = q.shape[1] + max(seqlen_offset)
104
+
105
+ if self.max_position_embeddings is not None:
106
+ max_seqlen = max(max_seqlen, self.max_position_embeddings)
107
+ q, k = self.rotary(q, k, seqlen_offset=seqlen_offset, max_seqlen=max_seqlen, cu_seqlens=cu_seqlens)
108
+
109
+ if past_key_values is not None:
110
+ cache_has_content = past_key_values.get_seq_length(self.layer_idx) > 0
111
+ k_cached, v_cached = past_key_values.update(
112
+ attn_state=(k.flatten(-2, -1), v.flatten(-2, -1)),
113
+ layer_idx=self.layer_idx,
114
+ offset=q_len,
115
+ cache_kwargs=dict(window_size=self.window_size),
116
+ )['attn_state']
117
+ if cache_has_content:
118
+ k, v = k_cached, v_cached
119
+ k = rearrange(k, '... (h d) -> ... h d', d=self.head_dim)
120
+ v = rearrange(v, '... (h d) -> ... h d', d=self.head_dim)
121
+
122
+ if flash_attn_func is None:
123
+ raise ImportError("Please install Flash Attention via `pip install flash-attn --no-build-isolation` first")
124
+
125
+ # Contains at least one padding token in the sequence
126
+ if attention_mask is not None:
127
+ q, (k, v), indices_q, cu_seqlens, max_seq_lens = unpad_input(q, (k, v), attention_mask, q_len)
128
+ cu_seqlens_q, cu_seqlens_k = cu_seqlens
129
+ max_seqlen_q, max_seqlen_k = max_seq_lens
130
+ o = flash_attn_varlen_func(
131
+ q, k, v,
132
+ cu_seqlens_q=cu_seqlens_q,
133
+ cu_seqlens_k=cu_seqlens_k,
134
+ max_seqlen_q=max_seqlen_q,
135
+ max_seqlen_k=max_seqlen_k,
136
+ causal=True,
137
+ window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
138
+ )
139
+ o = pad_input(o, indices_q, batch_size, q_len)
140
+ elif cu_seqlens is not None:
141
+ o = flash_attn_varlen_func(
142
+ q.squeeze(0), k.squeeze(0), v.squeeze(0),
143
+ cu_seqlens_q=cu_seqlens,
144
+ cu_seqlens_k=cu_seqlens,
145
+ max_seqlen_q=max_seqlen,
146
+ max_seqlen_k=max_seqlen,
147
+ causal=True,
148
+ window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
149
+ ).unsqueeze(0)
150
+ else:
151
+ o = flash_attn_func(
152
+ q, k, v,
153
+ causal=True,
154
+ window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
155
+ )
156
+ o = o.reshape(batch_size, q_len, -1)
157
+ o = self.o_proj(o)
158
+
159
+ if not output_attentions:
160
+ attentions = None
161
+
162
+ return o, attentions, past_key_values
fla/layers/comba.py ADDED
@@ -0,0 +1,328 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+
3
+ from __future__ import annotations
4
+
5
+ import math
6
+ import warnings
7
+ from typing import TYPE_CHECKING
8
+
9
+ import torch
10
+ import torch.nn as nn
11
+ from einops import rearrange, repeat
12
+ from torch.nn import functional as F
13
+
14
+ from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, update_layer_cache
15
+ from fla.modules import FusedRMSNormGated, RMSNorm, ShortConvolution
16
+ from fla.ops.comba import chunk_comba, fused_recurrent_comba
17
+
18
+ if TYPE_CHECKING:
19
+ from transformers.processing_utils import Unpack
20
+
21
+ from fla.models.utils import Cache
22
+
23
+
24
+ class Comba(nn.Module):
25
+ """
26
+ The layer implementaion for [Comba: Improving Bilinear RNNs with Closed-loop Control](https://arxiv.org/abs/2506.02475).
27
+
28
+ Similar to Mamba2 and Gated-DeltaNet, each layer contains around 6*hidden_size*hidden_size parameters.
29
+
30
+ Parameter alloation when use_output_gate=True:
31
+ - 0.75 * hidden_size * hidden_size for the q_proj and k_proj each
32
+ - 1.5 * hidden_size * hidden_size for the v_proj, g_proj and o_proj each
33
+ - Others are ignorably small.
34
+ - In total = 0.75 * 2 + 1.5 * 3 = 6 * hidden_size * hidden_size
35
+ NOTE: num_heads * head_dim = 0.75 * hidden_size, please make sure to set the correct num_heads and head_dim.
36
+
37
+ Parameter allocation when use_output_gate=False:
38
+ - 1 * hidden_size * hidden_size for the q_proj and k_proj each
39
+ - 2 * hidden_size * hidden_size for the v_proj and o_proj each
40
+ - Others are ignorably small.
41
+ - In total = 1 * 2 + 2 * 2 = 6 * hidden_size * hidden_size
42
+
43
+ Args:
44
+ hidden_size (int, Optional):
45
+ The hidden size of the input. Default: 2048.
46
+ expand_v (float, Optional):
47
+ The expansion ratio for the value dim. Default: 2.0.
48
+ head_dim (int, Optional):
49
+ The dimension of each head. Default: 256.
50
+ num_heads (int, Optional):
51
+ The number of heads. Default: 4.
52
+ num_v_heads (int, Optional):
53
+ The number of heads for the value projection, equal to `num_heads` if `None`.
54
+ GVA is applied if `num_v_heads` > `num_heads`. Default: `None`.
55
+ mode (str, Optional):
56
+ Which Gated DeltaNet kernel to use.
57
+ Currently available: `chunk` and `fused_recurrent`.
58
+ Default: `chunk`.
59
+ use_beta (bool, Optional):
60
+ Whether to use beta. Default: `True`.
61
+ use_output_gate (bool, Optional):
62
+ Whether to use output gate. Default: `True`.
63
+ use_output_correction (bool, Optional):
64
+ Whether to use <q-dk>. Default: `True`.
65
+ use_short_conv (bool, Optional):
66
+ Whether to use short convolutions. Default: `True`.
67
+ conv_size (int, Optional):
68
+ The kernel size of the short convolution, only used when `use_short_conv` is `True`. Default: 4.
69
+ conv_bias (bool, Optional):
70
+ Whether to use bias in the short convolution, only used when `use_short_conv` is `True`. Default: `False`.
71
+ layer_idx (int, Optional):
72
+ The index of the layer. Default: None.
73
+ norm_eps (float, Optional):
74
+ The epsilon value for the normalization layer. Default: 1e-5.
75
+ """
76
+
77
+ def __init__(
78
+ self,
79
+ hidden_size: int = 2048,
80
+ expand_v: float = 2,
81
+ head_dim: int = 256,
82
+ num_heads: int = 6,
83
+ num_v_heads: int = None,
84
+ mode: str = 'chunk',
85
+ use_short_conv: bool = True,
86
+ use_output_gate: bool = True,
87
+ use_output_correction: bool = True,
88
+ use_inner_decay: bool = True,
89
+ correction_factor: float = 1.,
90
+ conv_size: int = 4,
91
+ conv_bias: bool = False,
92
+ layer_idx: int = None,
93
+ norm_eps: float = 1e-5,
94
+ **kwargs,
95
+ ) -> Comba:
96
+ super().__init__()
97
+
98
+ self.mode = mode
99
+
100
+ self.hidden_size = hidden_size
101
+ self.expand_v = expand_v
102
+
103
+ self.use_short_conv = use_short_conv
104
+ self.use_output_gate = use_output_gate
105
+ self.use_output_correction = use_output_correction
106
+ self.use_inner_decay = use_inner_decay
107
+ self.conv_size = conv_size
108
+ self.conv_bias = conv_bias
109
+
110
+ self.head_dim = head_dim
111
+ self.num_heads = num_heads
112
+ self.num_v_heads = num_v_heads if num_v_heads is not None else num_heads
113
+
114
+ self.head_k_dim = head_dim
115
+ self.head_v_dim = int(self.head_dim * self.expand_v)
116
+ self.key_dim = int(self.num_heads * self.head_k_dim)
117
+ self.value_dim = int(self.num_v_heads * self.head_v_dim)
118
+ self.layer_idx = layer_idx
119
+
120
+ # Consistency check: Ensure expand_v produces integer values
121
+ if not math.isclose(self.num_v_heads * self.head_dim * expand_v, self.value_dim, rel_tol=1e-5):
122
+ raise ValueError(
123
+ f"expand_v={expand_v} does not produce an integer value when multiplied by key_dim={self.key_dim}. "
124
+ f"Resulting value_dim would be {self.num_v_heads * self.head_dim * expand_v}, which is invalid for nn.Linear.",
125
+ )
126
+ if self.num_v_heads > self.num_heads and self.num_v_heads % self.num_heads != 0:
127
+ raise ValueError(
128
+ f"num_v_heads={self.num_v_heads} must be divisible by num_heads={self.num_heads}.",
129
+ )
130
+
131
+ if not math.isclose(head_dim * expand_v, self.head_v_dim, rel_tol=1e-5):
132
+ raise ValueError(
133
+ f"expand_v={expand_v} does not produce an integer value when multiplied by head_dim={head_dim}. "
134
+ f"Resulting head_v_dim would be {head_dim * expand_v}, which is invalid for FusedRMSNormGated.",
135
+ )
136
+ assert mode in ['chunk', 'fused_recurrent'], f"Not supported mode `{mode}`."
137
+
138
+ self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
139
+ self.k_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
140
+ self.v_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
141
+ self.a_proj = nn.Linear(hidden_size, self.num_v_heads, bias=False)
142
+ self.b_proj = nn.Linear(hidden_size, self.num_v_heads, bias=False)
143
+
144
+ if use_inner_decay:
145
+ self.decay = nn.Parameter(torch.ones(self.num_heads))
146
+
147
+ if use_output_correction:
148
+ warnings.warn(
149
+ "The correction_factor is set to 1 by default similar to Mamba2. "
150
+ "However, we find that sometimes correction_factor = 0.02 works better for small-scale models. "
151
+ "In practice, we recommend trying both settings. ",
152
+ )
153
+ self.D = nn.Parameter(torch.ones(self.num_heads) * correction_factor)
154
+ self.D._no_weight_decay = True
155
+
156
+ A = torch.empty(self.num_v_heads, dtype=torch.float32).uniform_(0, 16)
157
+ self.A_log = nn.Parameter(torch.log(A))
158
+ self.A_log._no_weight_decay = True
159
+ # hard coded for now
160
+ dt_min = 0.001
161
+ dt_max = 0.1
162
+ dt_init_floor = 1e-4
163
+ dt = torch.exp(
164
+ torch.rand(self.num_v_heads) * (math.log(dt_max) - math.log(dt_min))
165
+ + math.log(dt_min),
166
+ )
167
+ dt = torch.clamp(dt, min=dt_init_floor)
168
+ # Inverse of softplus: https://github.com/pytorch/pytorch/issues/72759
169
+ inv_dt = dt + torch.log(-torch.expm1(-dt))
170
+ self.dt_bias = nn.Parameter(inv_dt)
171
+ # Just to be explicit. Without this we already don't put wd on dt_bias because of the check
172
+ # name.endswith("bias") in param_grouping.py
173
+ self.dt_bias._no_weight_decay = True
174
+
175
+ if use_short_conv:
176
+ self.conv_size = conv_size
177
+ self.q_conv1d = ShortConvolution(
178
+ hidden_size=self.key_dim,
179
+ kernel_size=conv_size,
180
+ bias=conv_bias,
181
+ activation='silu',
182
+ )
183
+ self.k_conv1d = ShortConvolution(
184
+ hidden_size=self.key_dim,
185
+ kernel_size=conv_size,
186
+ bias=conv_bias,
187
+ activation='silu',
188
+ )
189
+ self.v_conv1d = ShortConvolution(
190
+ hidden_size=self.value_dim,
191
+ kernel_size=conv_size,
192
+ bias=conv_bias,
193
+ activation='silu',
194
+ )
195
+ else:
196
+ warnings.warn(
197
+ "ShortConvolution is crucial to the performance. "
198
+ "Do not turn it off, i.e., setting `use_short_conv=False` unless you know what you are doing.",
199
+ )
200
+ if use_output_gate:
201
+ self.g_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
202
+ self.o_norm = FusedRMSNormGated(self.head_v_dim, activation='sigmoid', eps=norm_eps)
203
+ else:
204
+ self.o_norm = RMSNorm(self.head_v_dim, eps=norm_eps, dtype=torch.float32)
205
+ self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
206
+
207
+ def forward(
208
+ self,
209
+ hidden_states: torch.Tensor,
210
+ attention_mask: torch.Tensor | None = None,
211
+ past_key_values: Cache | None = None,
212
+ use_cache: bool | None = False,
213
+ output_attentions: bool | None = False,
214
+ **kwargs: Unpack[dict],
215
+ ) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
216
+ if attention_mask is not None:
217
+ assert len(attention_mask.shape) == 2, (
218
+ "Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
219
+ "for padding purposes (0 indicating padding). "
220
+ "Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
221
+ )
222
+
223
+ batch_size, q_len, _ = hidden_states.shape
224
+ # change to inference mode.
225
+ mode = 'fused_recurrent' if (q_len <= 64 and not self.training) else self.mode
226
+ if self.training:
227
+ assert mode == 'chunk', "Only chunk mode is supported in training."
228
+ last_state = get_layer_cache(self, past_key_values)
229
+
230
+ cu_seqlens = kwargs.get('cu_seqlens')
231
+ if attention_mask is not None:
232
+ indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
233
+ hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
234
+
235
+ if self.use_short_conv:
236
+ conv_state_q, conv_state_k, conv_state_v = None, None, None
237
+ if last_state is not None:
238
+ conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
239
+ q, conv_state_q = self.q_conv1d(
240
+ x=self.q_proj(hidden_states),
241
+ cache=conv_state_q,
242
+ output_final_state=use_cache,
243
+ cu_seqlens=cu_seqlens,
244
+ )
245
+ k, conv_state_k = self.k_conv1d(
246
+ x=self.k_proj(hidden_states),
247
+ cache=conv_state_k,
248
+ output_final_state=use_cache,
249
+ cu_seqlens=cu_seqlens,
250
+ )
251
+ v, conv_state_v = self.v_conv1d(
252
+ x=self.v_proj(hidden_states),
253
+ cache=conv_state_v,
254
+ output_final_state=use_cache,
255
+ cu_seqlens=cu_seqlens,
256
+ )
257
+ else:
258
+ q = F.silu(self.q_proj(hidden_states))
259
+ k = F.silu(self.k_proj(hidden_states))
260
+ v = F.silu(self.v_proj(hidden_states))
261
+
262
+ q, k = map(lambda x: rearrange(x, '... (h d) -> ... h d', d=self.head_k_dim), (q, k))
263
+
264
+ if self.use_inner_decay:
265
+ p = k * self.decay[None, None, :, None].sigmoid()
266
+ else:
267
+ p = k
268
+
269
+ if self.use_output_correction:
270
+ q = q - self.D[None, None, :, None] * p
271
+
272
+ v = rearrange(v, '... (h d) -> ... h d', d=self.head_v_dim)
273
+
274
+ if self.num_v_heads > self.num_heads:
275
+ q, k = map(lambda x: repeat(x, '... h d -> ... (h g) d', g=self.num_v_heads // self.num_heads), (q, k))
276
+
277
+ beta = self.b_proj(hidden_states).sigmoid()
278
+ g = -self.A_log.float().exp() * F.softplus(self.a_proj(hidden_states).float() + self.dt_bias)
279
+
280
+ recurrent_state = last_state['recurrent_state'] if last_state is not None else None
281
+ if mode == 'chunk':
282
+ o, recurrent_state = chunk_comba(
283
+ q=q,
284
+ k=k,
285
+ v=v,
286
+ p=p,
287
+ g=g,
288
+ beta=beta,
289
+ initial_state=recurrent_state,
290
+ output_final_state=use_cache,
291
+ cu_seqlens=cu_seqlens,
292
+ use_qk_l2norm_in_kernel=True,
293
+ )
294
+ elif mode == 'fused_recurrent':
295
+ o, recurrent_state = fused_recurrent_comba(
296
+ q=q,
297
+ k=k,
298
+ v=v,
299
+ p=p,
300
+ g=g,
301
+ beta=beta,
302
+ initial_state=recurrent_state,
303
+ output_final_state=use_cache,
304
+ cu_seqlens=cu_seqlens,
305
+ use_qk_l2norm_in_kernel=True,
306
+ )
307
+ else:
308
+ raise NotImplementedError(f"Not supported mode `{mode}`.")
309
+
310
+ update_layer_cache(
311
+ self,
312
+ past_key_values,
313
+ recurrent_state=recurrent_state,
314
+ conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
315
+ offset=q_len,
316
+ )
317
+
318
+ if self.use_output_gate:
319
+ g = rearrange(self.g_proj(hidden_states), '... (h d) -> ... h d', d=self.head_v_dim)
320
+ o = self.o_norm(o, g)
321
+ else:
322
+ o = self.o_norm(o)
323
+ o = rearrange(o, 'b t h d -> b t (h d)')
324
+ o = self.o_proj(o)
325
+ if attention_mask is not None:
326
+ o = pad_input(o.squeeze(0), indices, batch_size, q_len)
327
+
328
+ return o, None, past_key_values
fla/layers/delta_net.py ADDED
@@ -0,0 +1,287 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+
3
+ from __future__ import annotations
4
+
5
+ import warnings
6
+ from typing import TYPE_CHECKING
7
+
8
+ import torch
9
+ import torch.nn as nn
10
+ from einops import rearrange
11
+ from torch.nn import functional as F
12
+
13
+ from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, update_layer_cache
14
+ from fla.modules import FusedRMSNormGated, RMSNorm, ShortConvolution
15
+ from fla.ops.delta_rule import chunk_delta_rule, fused_recurrent_delta_rule
16
+
17
+ if TYPE_CHECKING:
18
+ from transformers.processing_utils import Unpack
19
+
20
+ from fla.models.utils import Cache
21
+
22
+
23
+ def elu_p1(x):
24
+ return (F.elu(x, 1., False) + 1.).to(x)
25
+
26
+
27
+ def sum_norm(x):
28
+ return (x / x.sum(-1, keepdim=True)).to(x)
29
+
30
+
31
+ class DeltaNet(nn.Module):
32
+ r"""
33
+ The layer implementaion for [Parallelizing Linear Transformers with the Delta Rule over Sequence Length](https://arxiv.org/abs/2406.06484). # noqa:
34
+ DeltaNet was originally proposed in [Linear Transformers Are Secretly Fast Weight Programmers](https://arxiv.org/abs/2102.11174). # noqa
35
+
36
+ Args:
37
+ mode (str, Optional):
38
+ Which DeltaNet kernel to use.
39
+ Currently available: `chunk`, `fused_recurrent`, and `fused_chunk`.
40
+ Default: `chunk`.
41
+ hidden_size (int, Optional):
42
+ The hidden size of the input. Default: 1024.
43
+ expand_k (float, Optional):
44
+ The expansion ratio for the key dim. Default: 1.0.
45
+ expand_v (float, Optional):
46
+ The expansion ratio for the value dim. Default: 1.0.
47
+ num_heads (int, Optional):
48
+ The number of heads. Default: 4.
49
+ use_beta (bool, Optional):
50
+ Whether to use beta. Default: `True`.
51
+ use_gate (bool, Optional):
52
+ Whether to use output gate. Default: `False`.
53
+ use_short_conv (bool, Optional):
54
+ Whether to use short convolutions. Default: `True`.
55
+ conv_size (int, Optional):
56
+ The kernel size of the short convolution, only used when `use_short_conv` is `True`. Default: 4.
57
+ conv_bias (bool, Optional):
58
+ Whether to use bias in the short convolution, only used when `use_short_conv` is `True`. Default: `False`.
59
+ allow_neg_eigval (bool, Optional):
60
+ Allow negative eigenvalues. Default: `False`. If set to `True`, the beta will be multiplied by 2.
61
+ See reference: [Unlocking State-Tracking in Linear RNNs Through Negative Eigenvalues](https://arxiv.org/abs/2411.12537)
62
+ layer_idx (int, Optional):
63
+ The index of the layer. Default: None.
64
+ norm_eps (float, Optional):
65
+ The epsilon value for the layernorm/rmsnorm layer. Default: 1e-5.
66
+ qk_activation (str, Optional):
67
+ The activation function for the query and key. Default: `silu`.
68
+ qk_norm (str, Optional):
69
+ The normalization method for the query and key. Default: `l2`.
70
+ """
71
+
72
+ def __init__(
73
+ self,
74
+ mode: str = 'chunk',
75
+ d_model: int = None,
76
+ hidden_size: int = 1024,
77
+ expand_k: float = 1.0,
78
+ expand_v: float = 1.0,
79
+ num_heads: int = 4,
80
+ use_beta: bool = True,
81
+ use_gate: bool = False,
82
+ use_short_conv: bool = True,
83
+ conv_size: int = 4,
84
+ conv_bias: bool = False,
85
+ allow_neg_eigval: bool = False,
86
+ layer_idx: int = None,
87
+ qk_activation: str = 'silu',
88
+ qk_norm: str = 'l2',
89
+ norm_eps: float = 1e-5,
90
+ **kwargs,
91
+ ) -> DeltaNet:
92
+ super().__init__()
93
+
94
+ self.mode = mode
95
+ self.qk_activation = qk_activation
96
+ self.qk_norm = qk_norm
97
+
98
+ assert self.qk_activation in ['silu', 'relu', 'elu', 'identity']
99
+ assert self.qk_norm in ['l2', 'sum']
100
+
101
+ if d_model is not None:
102
+ hidden_size = d_model
103
+ self.hidden_size = hidden_size
104
+ self.expand_k = expand_k
105
+ self.expand_v = expand_v
106
+ self.num_heads = num_heads
107
+ self.use_gate = use_gate
108
+ self.use_short_conv = use_short_conv
109
+ self.conv_size = conv_size
110
+ self.conv_bias = conv_bias
111
+ self.allow_neg_eigval = allow_neg_eigval
112
+
113
+ self.key_dim = int(hidden_size * expand_k)
114
+ self.value_dim = int(hidden_size * expand_v)
115
+ self.head_k_dim = self.key_dim // num_heads
116
+ self.head_v_dim = self.value_dim // num_heads
117
+ self.layer_idx = layer_idx
118
+
119
+ if mode == 'fused_chunk':
120
+ raise NotImplementedError("fused_chunk_delta_rule is now deprecated. Please use `chunk_delta_rule` instead.")
121
+ assert mode in ['chunk', 'fused_recurrent'], f"Not supported mode `{mode}`."
122
+ assert self.key_dim % num_heads == 0, f"key dim must be divisible by num_heads of {num_heads}"
123
+ assert self.value_dim % num_heads == 0, f"value dim must be divisible by num_heads of {num_heads}"
124
+
125
+ self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
126
+ self.k_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
127
+ self.v_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
128
+
129
+ self.use_beta = use_beta
130
+ if self.use_beta:
131
+ self.b_proj = nn.Linear(hidden_size, self.num_heads, bias=False)
132
+ if use_short_conv:
133
+ self.conv_size = conv_size
134
+ self.q_conv1d = ShortConvolution(
135
+ hidden_size=self.key_dim,
136
+ kernel_size=conv_size,
137
+ bias=conv_bias,
138
+ activation='silu' if qk_activation == 'silu' else None,
139
+ )
140
+ self.k_conv1d = ShortConvolution(
141
+ hidden_size=self.key_dim,
142
+ kernel_size=conv_size,
143
+ bias=conv_bias,
144
+ activation='silu' if qk_activation == 'silu' else None,
145
+ )
146
+ self.v_conv1d = ShortConvolution(
147
+ hidden_size=self.value_dim,
148
+ kernel_size=conv_size,
149
+ bias=conv_bias,
150
+ activation='silu',
151
+ )
152
+ else:
153
+ warnings.warn(
154
+ "ShortConvolution is crucial to the performance. "
155
+ "Do not turn it off, i.e., setting `use_short_conv=False` unless you know what you are doing.",
156
+ )
157
+ if use_gate:
158
+ self.g_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
159
+ self.o_norm = FusedRMSNormGated(self.head_v_dim, eps=norm_eps)
160
+ else:
161
+ self.o_norm = RMSNorm(self.head_v_dim, eps=norm_eps, dtype=torch.float32)
162
+
163
+ self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
164
+
165
+ def forward(
166
+ self,
167
+ hidden_states: torch.Tensor,
168
+ attention_mask: torch.Tensor | None = None,
169
+ past_key_values: Cache | None = None,
170
+ use_cache: bool | None = False,
171
+ output_attentions: bool | None = False,
172
+ **kwargs: Unpack[dict],
173
+ ) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
174
+ if attention_mask is not None:
175
+ assert len(attention_mask.shape) == 2, (
176
+ "Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
177
+ "for padding purposes (0 indicating padding). "
178
+ "Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
179
+ )
180
+
181
+ batch_size, q_len, _ = hidden_states.shape
182
+ # change to inference mode.
183
+ mode = 'fused_recurrent' if q_len <= 64 else self.mode
184
+
185
+ last_state = get_layer_cache(self, past_key_values)
186
+
187
+ cu_seqlens = kwargs.get('cu_seqlens')
188
+ if attention_mask is not None:
189
+ indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
190
+ hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
191
+
192
+ if self.use_short_conv:
193
+ conv_state_q, conv_state_k, conv_state_v = None, None, None
194
+ if last_state is not None:
195
+ conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
196
+ q, conv_state_q = self.q_conv1d(
197
+ x=self.q_proj(hidden_states),
198
+ cache=conv_state_q,
199
+ output_final_state=use_cache,
200
+ cu_seqlens=cu_seqlens,
201
+ )
202
+ k, conv_state_k = self.k_conv1d(
203
+ x=self.k_proj(hidden_states),
204
+ cache=conv_state_k,
205
+ output_final_state=use_cache,
206
+ cu_seqlens=cu_seqlens,
207
+ )
208
+ v, conv_state_v = self.v_conv1d(
209
+ x=self.v_proj(hidden_states),
210
+ cache=conv_state_v,
211
+ output_final_state=use_cache,
212
+ cu_seqlens=cu_seqlens,
213
+ )
214
+ else:
215
+ q = self.q_proj(hidden_states)
216
+ k = self.k_proj(hidden_states)
217
+ if self.qk_activation == 'silu':
218
+ q, k = F.silu(q), F.silu(k)
219
+ v = F.silu(self.v_proj(hidden_states))
220
+
221
+ q, k = map(lambda x: rearrange(x, '... (h d) -> ... h d', d=self.head_k_dim), (q, k))
222
+ v = rearrange(v, '... (h d) -> ... h d', d=self.head_v_dim)
223
+ if self.qk_activation != 'silu':
224
+ if self.qk_activation == 'relu':
225
+ q, k = q.relu(), k.relu()
226
+ elif self.qk_activation == 'elu':
227
+ q, k = elu_p1(q), elu_p1(k)
228
+ elif self.qk_activation != 'identity':
229
+ raise NotImplementedError
230
+
231
+ if self.qk_norm == 'sum':
232
+ q = sum_norm(q).to(q)
233
+ k = sum_norm(k).to(k)
234
+
235
+ if self.use_beta:
236
+ beta = self.b_proj(hidden_states).sigmoid()
237
+ else:
238
+ beta = torch.ones_like(q[..., 0])
239
+
240
+ if self.allow_neg_eigval:
241
+ beta = beta * 2.
242
+
243
+ recurrent_state = last_state['recurrent_state'] if last_state is not None else None
244
+ if mode == 'fused_recurrent':
245
+ o, recurrent_state = fused_recurrent_delta_rule(
246
+ q=q,
247
+ k=k,
248
+ v=v,
249
+ beta=beta,
250
+ initial_state=recurrent_state,
251
+ output_final_state=use_cache,
252
+ cu_seqlens=cu_seqlens,
253
+ use_qk_l2norm_in_kernel=(self.qk_norm == 'l2'),
254
+ )
255
+ elif mode == 'chunk':
256
+ o, recurrent_state = chunk_delta_rule(
257
+ q=q,
258
+ k=k,
259
+ v=v,
260
+ beta=beta,
261
+ initial_state=recurrent_state,
262
+ output_final_state=use_cache,
263
+ cu_seqlens=cu_seqlens,
264
+ use_qk_l2norm_in_kernel=(self.qk_norm == 'l2'),
265
+ )
266
+ else:
267
+ raise NotImplementedError(f"Not supported mode `{mode}`.")
268
+
269
+ update_layer_cache(
270
+ self,
271
+ past_key_values,
272
+ recurrent_state=recurrent_state,
273
+ conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
274
+ offset=q_len,
275
+ )
276
+
277
+ if self.use_gate:
278
+ g = rearrange(self.g_proj(hidden_states), '... (h d) -> ... h d', d=self.head_v_dim)
279
+ o = self.o_norm(o, g)
280
+ else:
281
+ o = self.o_norm(o)
282
+ o = rearrange(o, 'b t h d -> b t (h d)')
283
+ o = self.o_proj(o)
284
+ if attention_mask is not None:
285
+ o = pad_input(o.squeeze(0), indices, batch_size, q_len)
286
+
287
+ return o, None, past_key_values
fla/layers/deltaformer.py ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ import torch
8
+ import torch.nn as nn
9
+ from einops import rearrange
10
+ from transformers.utils import logging
11
+
12
+ from fla.modules import RMSNorm, RotaryEmbedding
13
+ from fla.ops.deltaformer import deltaformer_attn
14
+ from fla.ops.utils.index import prepare_lens_from_mask
15
+
16
+ if TYPE_CHECKING:
17
+ from fla.models.utils import Cache
18
+
19
+ logger = logging.get_logger(__name__)
20
+
21
+
22
+ class DeltaFormerAttention(nn.Module):
23
+
24
+ r"""
25
+ The layer implementation for DeltaFormer,
26
+ [Understanding Transformer from the Perspective of Associative Memory]
27
+ (https://arxiv.org/pdf/2505.19488).
28
+
29
+ Notes
30
+ - DeltaFormer attention is implemented with Triton kernels in `fla.ops.deltaformer` and is tuned
31
+ for typical head dimensions (e.g., 64/128). It currently supports fixed-length inputs.
32
+ - For variable-length inputs (padding masks), the deltaformer computation falls back to using the
33
+ fixed-length path, while the second stage (softmax attention over U) uses FlashAttention's
34
+ varlen path when an attention mask is provided.
35
+ - K/V grouping (GQA) is supported natively by FlashAttention via `num_kv_heads`.
36
+ - Uses K-K similarity in deltaformer computation instead of Q-K similarity for better performance.
37
+
38
+ Args:
39
+ hidden_size (int, Optional):
40
+ The hidden size of the input. Default: 2048.
41
+ num_heads (int, Optional):
42
+ The number of attention heads. Default: 32.
43
+ num_kv_heads (int, Optional):
44
+ The number of key/value heads for grouped-query attention. If None, equals `num_heads`.
45
+ Default: None.
46
+ qkv_bias (bool, Optional):
47
+ Whether to use bias for Q/K/V projections. Default: False.
48
+ qk_norm (bool, Optional):
49
+ Whether to apply per-head RMSNorm to Q and K before attention. Default: False.
50
+ rope_theta (float, Optional):
51
+ The base frequency for rotary position embedding. Default: 10000.
52
+ max_position_embeddings (int, Optional):
53
+ The maximum position embeddings. Default: None.
54
+ layer_idx (int, Optional):
55
+ The index of the layer (used for cache compatibility). Default: None.
56
+ """
57
+
58
+ def __init__(
59
+ self,
60
+ hidden_size: int = 2048,
61
+ num_heads: int = 32,
62
+ num_kv_heads: int | None = None,
63
+ qkv_bias: bool = False,
64
+ qk_norm: bool = False,
65
+ rope_theta: float = 10000.,
66
+ max_position_embeddings: int | None = None,
67
+ layer_idx: int | None = None,
68
+ ):
69
+ super().__init__()
70
+
71
+ self.hidden_size = hidden_size
72
+ self.num_heads = num_heads
73
+ self.num_kv_heads = num_kv_heads if num_kv_heads is not None else num_heads
74
+ self.num_kv_groups = num_heads // self.num_kv_heads
75
+ self.head_dim = self.hidden_size // self.num_heads
76
+ self.kv_dim = self.num_kv_heads * self.head_dim
77
+ self.qkv_bias = qkv_bias
78
+ self.qk_norm = qk_norm
79
+ self.rope_theta = rope_theta
80
+ self.max_position_embeddings = max_position_embeddings
81
+ self.layer_idx = layer_idx
82
+
83
+ self.q_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=self.qkv_bias)
84
+ self.k_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=self.qkv_bias)
85
+ self.v_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=self.qkv_bias)
86
+ self.b_proj = nn.Linear(self.hidden_size, self.num_heads, bias=True)
87
+ self.o_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=False)
88
+
89
+ if qk_norm:
90
+ self.q_norm = RMSNorm(self.head_dim, dtype=torch.float32)
91
+ self.k_norm = RMSNorm(self.head_dim, dtype=torch.float32)
92
+
93
+ self.rotary = RotaryEmbedding(dim=self.head_dim, base=self.rope_theta)
94
+
95
+ def forward(
96
+ self,
97
+ hidden_states: torch.Tensor,
98
+ attention_mask: torch.LongTensor | None = None,
99
+ past_key_values: Cache | None = None,
100
+ output_attentions: bool = False,
101
+ use_cache: bool = False,
102
+ **kwargs,
103
+ ) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
104
+ attentions = None
105
+ if attention_mask is not None:
106
+ assert len(attention_mask.shape) == 2, (
107
+ "Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
108
+ "for padding purposes (0 indicating padding). "
109
+ "Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
110
+ )
111
+
112
+ batch_size, q_len, _ = hidden_states.size()
113
+
114
+ q = rearrange(self.q_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
115
+ k = rearrange(self.k_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
116
+ v = rearrange(self.v_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
117
+ beta = self.b_proj(hidden_states)
118
+
119
+ if self.qk_norm:
120
+ q, k = self.q_norm(q), self.k_norm(k)
121
+
122
+ cu_seqlens_kw = kwargs.get('cu_seqlens')
123
+ seqlen_offset, max_seqlen = 0, q_len
124
+ if past_key_values is not None:
125
+ seqlen_offset = past_key_values.get_seq_length(self.layer_idx)
126
+ max_seqlen = q_len + seqlen_offset
127
+
128
+ if attention_mask is not None:
129
+ seqlen_offset = seqlen_offset + prepare_lens_from_mask(attention_mask) - attention_mask.shape[-1]
130
+ max_seqlen = q_len + max(seqlen_offset)
131
+
132
+ if self.max_position_embeddings is not None:
133
+ max_seqlen = max(max_seqlen, self.max_position_embeddings)
134
+
135
+ q, k = self.rotary(q, k, seqlen_offset=seqlen_offset, max_seqlen=max_seqlen, cu_seqlens=cu_seqlens_kw)
136
+
137
+ o = deltaformer_attn(
138
+ q=q,
139
+ k=k,
140
+ v=v,
141
+ beta=beta,
142
+ attention_mask=attention_mask,
143
+ cu_seqlens=cu_seqlens_kw,
144
+ )
145
+
146
+ o = o.reshape(batch_size, q_len, -1)
147
+ o = self.o_proj(o)
148
+
149
+ if not output_attentions:
150
+ attentions = None
151
+
152
+ return o, attentions, past_key_values
fla/layers/forgetting_attn.py ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ import torch
8
+ import torch.nn as nn
9
+ import torch.nn.functional as F
10
+ import torch.utils.checkpoint
11
+ from einops import rearrange
12
+ from transformers.utils import logging
13
+
14
+ from fla.layers.utils import pad_input, unpad_input
15
+ from fla.modules import GroupNorm
16
+ from fla.ops.attn.decoding import attn_decoding_one_step
17
+ from fla.ops.forgetting_attn.parallel import parallel_forgetting_attn
18
+
19
+ if TYPE_CHECKING:
20
+ from fla.models.utils import Cache
21
+
22
+ logger = logging.get_logger(__name__)
23
+
24
+
25
+ class ForgettingAttention(nn.Module):
26
+
27
+ def __init__(
28
+ self,
29
+ hidden_size: int = 2048,
30
+ num_heads: int = 32,
31
+ num_kv_heads: int | None = None,
32
+ qkv_bias: bool = False,
33
+ qk_norm: bool = False,
34
+ window_size: int | None = None,
35
+ use_output_gate: bool = False,
36
+ layer_idx: int = None,
37
+ ):
38
+ super().__init__()
39
+
40
+ self.hidden_size = hidden_size
41
+ self.num_heads = num_heads
42
+ if num_kv_heads is None:
43
+ self.num_kv_heads = self.num_heads
44
+ else:
45
+ self.num_kv_heads = num_kv_heads
46
+ self.num_kv_groups = num_heads // self.num_kv_heads
47
+ self.head_dim = self.hidden_size // self.num_heads
48
+ self.kv_dim = self.num_kv_heads * self.head_dim
49
+ self.qkv_bias = qkv_bias
50
+ self.qk_norm = qk_norm
51
+
52
+ self.window_size = window_size
53
+ self.use_output_gate = use_output_gate
54
+ self.layer_idx = layer_idx
55
+
56
+ self.q_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=self.qkv_bias)
57
+ self.k_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=self.qkv_bias)
58
+ self.v_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=self.qkv_bias)
59
+ self.f_proj = nn.Linear(self.hidden_size, self.num_heads, bias=True)
60
+
61
+ if use_output_gate:
62
+ self.g_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=False)
63
+ self.o_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=False)
64
+
65
+ if qk_norm:
66
+ self.q_norm = GroupNorm(
67
+ num_groups=self.num_heads,
68
+ hidden_size=self.hidden_size,
69
+ is_rms_norm=True,
70
+ )
71
+ self.k_norm = GroupNorm(
72
+ num_groups=self.num_kv_heads,
73
+ hidden_size=self.kv_dim,
74
+ is_rms_norm=True,
75
+ )
76
+
77
+ def forward(
78
+ self,
79
+ hidden_states: torch.Tensor,
80
+ attention_mask: torch.LongTensor | None = None,
81
+ past_key_values: Cache | None = None,
82
+ output_attentions: bool = False,
83
+ use_cache: bool = False,
84
+ **kwargs,
85
+ ) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
86
+ if attention_mask is not None:
87
+ assert len(attention_mask.shape) == 2, (
88
+ "Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
89
+ "for padding purposes (0 indicating padding). "
90
+ "Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
91
+ )
92
+
93
+ batch_size, q_len, _ = hidden_states.size()
94
+
95
+ q, k, v = self.q_proj(hidden_states), self.k_proj(hidden_states), self.v_proj(hidden_states)
96
+ f = F.logsigmoid(self.f_proj(hidden_states).float())
97
+ if self.qk_norm:
98
+ q, k = self.q_norm(q), self.k_norm(k)
99
+
100
+ cu_seqlens = kwargs.get('cu_seqlens')
101
+ if past_key_values is not None:
102
+ assert cu_seqlens is None, "cu_seqlens should not be provided when past_key_values is not None"
103
+ state = past_key_values.update(
104
+ attn_state=(k, v, f),
105
+ layer_idx=self.layer_idx,
106
+ offset=q_len,
107
+ cache_kwargs=dict(window_size=self.window_size),
108
+ )
109
+ k, v, f = state['attn_state']
110
+
111
+ q = rearrange(q, '... (h d) -> ... h d', d=self.head_dim)
112
+ k = rearrange(k, '... (h d) -> ... h d', d=self.head_dim)
113
+ v = rearrange(v, '... (h d) -> ... h d', d=self.head_dim)
114
+
115
+ if attention_mask is not None:
116
+ q, (k, v, f), indices_q, cu_seqlens, max_seq_lens = unpad_input(q, (k, v, f), attention_mask, q_len, keepdim=True)
117
+ _, cu_seqlens_k = cu_seqlens
118
+ cu_seqlens = cu_seqlens_k
119
+ max_seqlen_q, max_seqlen_k = max_seq_lens
120
+ if max_seqlen_q != max_seqlen_k:
121
+ assert max_seqlen_q == 1, "only support q_len == 1 for decoding"
122
+ o = attn_decoding_one_step(q, k, v, f, cu_seqlens=cu_seqlens)
123
+ else:
124
+ o = parallel_forgetting_attn(q, k, v, f, cu_seqlens=cu_seqlens)
125
+ else:
126
+ o = parallel_forgetting_attn(q, k, v, f, cu_seqlens=cu_seqlens)
127
+ if attention_mask is not None:
128
+ o = pad_input(o.squeeze(0), indices_q, batch_size, q_len)
129
+ o = rearrange(o, '... h d -> ... (h d)')
130
+ if self.use_output_gate:
131
+ o = self.g_proj(hidden_states).sigmoid() * o
132
+ o = self.o_proj(o)
133
+ return o, None, past_key_values
fla/layers/gated_deltanet.py ADDED
@@ -0,0 +1,316 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+
3
+ from __future__ import annotations
4
+
5
+ import math
6
+ import warnings
7
+ from typing import TYPE_CHECKING
8
+
9
+ import torch
10
+ import torch.nn as nn
11
+ from einops import rearrange, repeat
12
+ from torch.nn import functional as F
13
+
14
+ from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, update_layer_cache
15
+ from fla.modules import FusedRMSNormGated, RMSNorm, ShortConvolution
16
+ from fla.ops.gated_delta_rule import chunk_gated_delta_rule, fused_recurrent_gated_delta_rule
17
+
18
+ if TYPE_CHECKING:
19
+ from transformers.processing_utils import Unpack
20
+
21
+ from fla.models.utils import Cache
22
+
23
+
24
+ @torch.compile
25
+ def elu_p1(x):
26
+ return (F.elu(x, 1., False) + 1.).to(x)
27
+
28
+
29
+ @torch.compile
30
+ def sum_norm(x):
31
+ return (x / x.sum(-1, keepdim=True)).to(x)
32
+
33
+
34
+ class GatedDeltaNet(nn.Module):
35
+ """
36
+ The layer implementaion for [Gated Delta Networks: Improving Mamba2 with Delta Rule](https://arxiv.org/abs/2412.06464). # noqa
37
+
38
+ Similar to Mamba2, each layer contains around 6*hidden_size*hidden_size parameters.
39
+
40
+ Parameter alloation when use_gate=True:
41
+ - 0.75 * hidden_size * hidden_size for the q_proj and k_proj each
42
+ - 1.5 * hidden_size * hidden_size for the v_proj, g_proj and o_proj each
43
+ - Others are ignorably small.
44
+ - In total = 0.75 * 2 + 1.5 * 3 = 6 * hidden_size * hidden_size
45
+ NOTE: num_heads * head_dim = 0.75 * hidden_size, please make sure to set the correct num_heads and head_dim.
46
+
47
+ Parameter allocation when use_gate=False:
48
+ - 1 * hidden_size * hidden_size for the q_proj and k_proj each
49
+ - 2 * hidden_size * hidden_size for the v_proj and o_proj each
50
+ - Others are ignorably small.
51
+ - In total = 1 * 2 + 2 * 2 = 6 * hidden_size * hidden_size
52
+
53
+ Args:
54
+ hidden_size (int, Optional):
55
+ The hidden size of the input. Default: 2048.
56
+ expand_v (float, Optional):
57
+ The expansion ratio for the value dim. Default: 2.0.
58
+ head_dim (int, Optional):
59
+ The dimension of each head. Default: 256.
60
+ num_heads (int, Optional):
61
+ The number of heads. Default: 4.
62
+ num_v_heads (int, Optional):
63
+ The number of heads for the value projection, equal to `num_heads` if `None`.
64
+ GVA is applied if `num_v_heads` > `num_heads`. Default: `None`.
65
+ mode (str, Optional):
66
+ Which Gated DeltaNet kernel to use.
67
+ Currently available: `chunk` and `fused_recurrent`.
68
+ Default: `chunk`.
69
+ use_beta (bool, Optional):
70
+ Whether to use beta. Default: `True`.
71
+ use_gate (bool, Optional):
72
+ Whether to use output gate. Default: `True`.
73
+ use_short_conv (bool, Optional):
74
+ Whether to use short convolutions. Default: `True`.
75
+ allow_neg_eigval (bool, Optional):
76
+ Allow negative eigenvalues. Default: `False`. If set to `True`, the beta will be multiplied by 2.
77
+ See reference: [Unlocking State-Tracking in Linear RNNs Through Negative Eigenvalues](https://arxiv.org/abs/2411.12537)
78
+ conv_size (int, Optional):
79
+ The kernel size of the short convolution, only used when `use_short_conv` is `True`. Default: 4.
80
+ conv_bias (bool, Optional):
81
+ Whether to use bias in the short convolution, only used when `use_short_conv` is `True`. Default: `False`.
82
+ layer_idx (int, Optional):
83
+ The index of the layer. Default: None.
84
+ norm_eps (float, Optional):
85
+ The epsilon value for the normalization layer. Default: 1e-5.
86
+ """
87
+
88
+ def __init__(
89
+ self,
90
+ hidden_size: int = 2048,
91
+ expand_v: float = 2,
92
+ head_dim: int = 256,
93
+ num_heads: int = 6,
94
+ num_v_heads: int = None,
95
+ mode: str = 'chunk',
96
+ use_gate: bool = True,
97
+ use_short_conv: bool = True,
98
+ allow_neg_eigval: bool = False,
99
+ conv_size: int = 4,
100
+ conv_bias: bool = False,
101
+ layer_idx: int = None,
102
+ norm_eps: float = 1e-5,
103
+ **kwargs,
104
+ ) -> GatedDeltaNet:
105
+ super().__init__()
106
+
107
+ self.mode = mode
108
+ self.allow_neg_eigval = allow_neg_eigval
109
+ self.hidden_size = hidden_size
110
+ self.expand_v = expand_v
111
+
112
+ self.use_gate = use_gate
113
+ self.use_short_conv = use_short_conv
114
+ self.conv_size = conv_size
115
+ self.conv_bias = conv_bias
116
+
117
+ self.head_dim = head_dim
118
+ self.num_heads = num_heads
119
+ self.num_v_heads = num_v_heads if num_v_heads is not None else num_heads
120
+
121
+ self.head_k_dim = head_dim
122
+ self.head_v_dim = int(self.head_dim * self.expand_v)
123
+ self.key_dim = int(self.num_heads * self.head_k_dim)
124
+ self.value_dim = int(self.num_v_heads * self.head_v_dim)
125
+ self.layer_idx = layer_idx
126
+
127
+ # Consistency check: Ensure expand_v produces integer values
128
+ if not math.isclose(self.num_v_heads * self.head_dim * expand_v, self.value_dim, rel_tol=1e-5):
129
+ raise ValueError(
130
+ f"expand_v={expand_v} does not produce an integer value when multiplied by key_dim={self.key_dim}. "
131
+ f"Resulting value_dim would be {self.num_v_heads * self.head_dim * expand_v}, which is invalid for nn.Linear.",
132
+ )
133
+ if self.num_v_heads > self.num_heads and self.num_v_heads % self.num_heads != 0:
134
+ raise ValueError(
135
+ f"num_v_heads={self.num_v_heads} must be divisible by num_heads={self.num_heads}.",
136
+ )
137
+
138
+ if not math.isclose(head_dim * expand_v, self.head_v_dim, rel_tol=1e-5):
139
+ raise ValueError(
140
+ f"expand_v={expand_v} does not produce an integer value when multiplied by head_dim={head_dim}. "
141
+ f"Resulting head_v_dim would be {head_dim * expand_v}, which is invalid for FusedRMSNormGated.",
142
+ )
143
+ assert mode in ['chunk', 'fused_recurrent'], f"Not supported mode `{mode}`."
144
+
145
+ self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
146
+ self.k_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
147
+ self.v_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
148
+ self.a_proj = nn.Linear(hidden_size, self.num_v_heads, bias=False)
149
+ self.b_proj = nn.Linear(hidden_size, self.num_v_heads, bias=False)
150
+
151
+ A = torch.empty(self.num_v_heads, dtype=torch.float32).uniform_(0, 16)
152
+ self.A_log = nn.Parameter(torch.log(A))
153
+ self.A_log._no_weight_decay = True
154
+ # hard coded for now
155
+ dt_min = 0.001
156
+ dt_max = 0.1
157
+ dt_init_floor = 1e-4
158
+ dt = torch.exp(
159
+ torch.rand(self.num_v_heads) * (math.log(dt_max) - math.log(dt_min))
160
+ + math.log(dt_min),
161
+ )
162
+ dt = torch.clamp(dt, min=dt_init_floor)
163
+ # Inverse of softplus: https://github.com/pytorch/pytorch/issues/72759
164
+ inv_dt = dt + torch.log(-torch.expm1(-dt))
165
+ self.dt_bias = nn.Parameter(inv_dt)
166
+ # Just to be explicit. Without this we already don't put wd on dt_bias because of the check
167
+ # name.endswith("bias") in param_grouping.py
168
+ self.dt_bias._no_weight_decay = True
169
+
170
+ if use_short_conv:
171
+ self.conv_size = conv_size
172
+ self.q_conv1d = ShortConvolution(
173
+ hidden_size=self.key_dim,
174
+ kernel_size=conv_size,
175
+ bias=conv_bias,
176
+ activation='silu',
177
+ )
178
+ self.k_conv1d = ShortConvolution(
179
+ hidden_size=self.key_dim,
180
+ kernel_size=conv_size,
181
+ bias=conv_bias,
182
+ activation='silu',
183
+ )
184
+ self.v_conv1d = ShortConvolution(
185
+ hidden_size=self.value_dim,
186
+ kernel_size=conv_size,
187
+ bias=conv_bias,
188
+ activation='silu',
189
+ )
190
+ else:
191
+ warnings.warn(
192
+ "ShortConvolution is crucial to the performance. "
193
+ "Do not turn it off, i.e., setting `use_short_conv=False` unless you know what you are doing.",
194
+ )
195
+ if use_gate:
196
+ self.g_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
197
+ self.o_norm = FusedRMSNormGated(self.head_v_dim, eps=norm_eps)
198
+ else:
199
+ self.o_norm = RMSNorm(self.head_v_dim, eps=norm_eps, dtype=torch.float32)
200
+ self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
201
+
202
+ def forward(
203
+ self,
204
+ hidden_states: torch.Tensor,
205
+ attention_mask: torch.Tensor | None = None,
206
+ past_key_values: Cache | None = None,
207
+ use_cache: bool | None = False,
208
+ output_attentions: bool | None = False,
209
+ **kwargs: Unpack[dict],
210
+ ) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
211
+ if attention_mask is not None:
212
+ assert len(attention_mask.shape) == 2, (
213
+ "Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
214
+ "for padding purposes (0 indicating padding). "
215
+ "Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
216
+ )
217
+
218
+ batch_size, q_len, _ = hidden_states.shape
219
+ # change to inference mode.
220
+ mode = 'fused_recurrent' if (q_len <= 64 and not self.training) else self.mode
221
+ if self.training:
222
+ assert mode == 'chunk', "Only chunk mode is supported in training."
223
+
224
+ last_state = get_layer_cache(self, past_key_values)
225
+
226
+ cu_seqlens = kwargs.get('cu_seqlens')
227
+ if attention_mask is not None:
228
+ indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
229
+ hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
230
+
231
+ if self.use_short_conv:
232
+ conv_state_q, conv_state_k, conv_state_v = None, None, None
233
+ if last_state is not None:
234
+ conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
235
+ q, conv_state_q = self.q_conv1d(
236
+ x=self.q_proj(hidden_states),
237
+ cache=conv_state_q,
238
+ output_final_state=use_cache,
239
+ cu_seqlens=cu_seqlens,
240
+ )
241
+ k, conv_state_k = self.k_conv1d(
242
+ x=self.k_proj(hidden_states),
243
+ cache=conv_state_k,
244
+ output_final_state=use_cache,
245
+ cu_seqlens=cu_seqlens,
246
+ )
247
+ v, conv_state_v = self.v_conv1d(
248
+ x=self.v_proj(hidden_states),
249
+ cache=conv_state_v,
250
+ output_final_state=use_cache,
251
+ cu_seqlens=cu_seqlens,
252
+ )
253
+ else:
254
+ q = F.silu(self.q_proj(hidden_states))
255
+ k = F.silu(self.k_proj(hidden_states))
256
+ v = F.silu(self.v_proj(hidden_states))
257
+
258
+ q, k = map(lambda x: rearrange(x, '... (h d) -> ... h d', d=self.head_k_dim), (q, k))
259
+ v = rearrange(v, '... (h d) -> ... h d', d=self.head_v_dim)
260
+
261
+ if self.num_v_heads > self.num_heads:
262
+ q, k = map(lambda x: repeat(x, '... h d -> ... (h g) d', g=self.num_v_heads // self.num_heads), (q, k))
263
+
264
+ beta = self.b_proj(hidden_states).sigmoid()
265
+ if self.allow_neg_eigval:
266
+ beta = beta * 2.
267
+
268
+ g = -self.A_log.float().exp() * F.softplus(self.a_proj(hidden_states).float() + self.dt_bias)
269
+
270
+ recurrent_state = last_state['recurrent_state'] if last_state is not None else None
271
+ if mode == 'chunk':
272
+ o, recurrent_state = chunk_gated_delta_rule(
273
+ q=q,
274
+ k=k,
275
+ v=v,
276
+ g=g,
277
+ beta=beta,
278
+ initial_state=recurrent_state,
279
+ output_final_state=use_cache,
280
+ cu_seqlens=cu_seqlens,
281
+ use_qk_l2norm_in_kernel=True,
282
+ )
283
+ elif mode == 'fused_recurrent':
284
+ o, recurrent_state = fused_recurrent_gated_delta_rule(
285
+ q=q,
286
+ k=k,
287
+ v=v,
288
+ g=g,
289
+ beta=beta,
290
+ initial_state=recurrent_state,
291
+ output_final_state=use_cache,
292
+ cu_seqlens=cu_seqlens,
293
+ use_qk_l2norm_in_kernel=True,
294
+ )
295
+ else:
296
+ raise NotImplementedError(f"Not supported mode `{mode}`.")
297
+
298
+ update_layer_cache(
299
+ self,
300
+ past_key_values,
301
+ recurrent_state=recurrent_state,
302
+ conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
303
+ offset=q_len,
304
+ )
305
+
306
+ if self.use_gate:
307
+ g = rearrange(self.g_proj(hidden_states), '... (h d) -> ... h d', d=self.head_v_dim)
308
+ o = self.o_norm(o, g)
309
+ else:
310
+ o = self.o_norm(o)
311
+ o = rearrange(o, 'b t h d -> b t (h d)')
312
+ o = self.o_proj(o)
313
+ if attention_mask is not None:
314
+ o = pad_input(o.squeeze(0), indices, batch_size, q_len)
315
+
316
+ return o, None, past_key_values
fla/layers/gated_deltaproduct.py ADDED
@@ -0,0 +1,287 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+
3
+ from __future__ import annotations
4
+
5
+ import math
6
+ import warnings
7
+ from typing import TYPE_CHECKING
8
+
9
+ import torch
10
+ import torch.nn as nn
11
+ from einops import rearrange, repeat
12
+ from torch.nn import functional as F
13
+
14
+ from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, update_layer_cache
15
+ from fla.modules import FusedRMSNormGated, RMSNorm, ShortConvolution
16
+ from fla.ops.gated_delta_product import chunk_gated_delta_product
17
+ from fla.ops.gated_delta_rule import fused_recurrent_gated_delta_rule
18
+
19
+ if TYPE_CHECKING:
20
+ from transformers.processing_utils import Unpack
21
+
22
+ from fla.models.utils import Cache
23
+
24
+
25
+ class GatedDeltaProduct(nn.Module):
26
+ """
27
+ Generalized version of GatedDoubleDeltaNet that supports arbitrary number of householder transformations.
28
+ """
29
+
30
+ def __init__(
31
+ self,
32
+ hidden_size: int = 2048,
33
+ expand_v: float = 2,
34
+ head_dim: int = 256,
35
+ num_heads: int = 6,
36
+ num_v_heads: int = None,
37
+ mode: str = 'chunk',
38
+ use_output_gate: bool = True,
39
+ use_short_conv: bool = True,
40
+ conv_size: int = 4,
41
+ conv_bias: bool = False,
42
+ layer_idx: int = None,
43
+ norm_eps: float = 1e-5,
44
+ use_forget_gate: bool = True,
45
+ allow_neg_eigval: bool = True,
46
+ num_householder: int = 2,
47
+ **kwargs,
48
+ ) -> GatedDeltaProduct:
49
+ super().__init__()
50
+
51
+ self.mode = mode
52
+
53
+ self.hidden_size = hidden_size
54
+ self.expand_v = expand_v
55
+
56
+ self.use_forget_gate = use_forget_gate
57
+ self.allow_neg_eigval = allow_neg_eigval
58
+ self.num_householder = num_householder
59
+ self.use_output_gate = use_output_gate
60
+ self.use_short_conv = use_short_conv
61
+ self.conv_size = conv_size
62
+ self.conv_bias = conv_bias
63
+
64
+ self.head_dim = head_dim
65
+ self.num_heads = num_heads
66
+ self.num_v_heads = num_v_heads if num_v_heads is not None else num_heads
67
+
68
+ self.head_k_dim = head_dim
69
+ self.head_v_dim = int(self.head_dim * self.expand_v)
70
+ self.key_dim = int(self.num_heads * self.head_k_dim)
71
+ self.value_dim = int(self.num_v_heads * self.head_v_dim)
72
+ self.layer_idx = layer_idx
73
+
74
+ # Consistency check: Ensure expand_v produces integer values
75
+ if not math.isclose(self.num_v_heads * self.head_dim * expand_v, self.value_dim, rel_tol=1e-5):
76
+ raise ValueError(
77
+ f"expand_v={expand_v} does not produce an integer value when multiplied by key_dim={self.key_dim}. "
78
+ f"Resulting value_dim would be {self.num_v_heads * self.head_dim * expand_v}, which is invalid for nn.Linear.",
79
+ )
80
+ if self.num_v_heads > self.num_heads and self.num_v_heads % self.num_heads != 0:
81
+ raise ValueError(
82
+ f"num_v_heads={self.num_v_heads} must be divisible by num_heads={self.num_heads}.",
83
+ )
84
+
85
+ if not math.isclose(head_dim * expand_v, self.head_v_dim, rel_tol=1e-5):
86
+ raise ValueError(
87
+ f"expand_v={expand_v} does not produce an integer value when multiplied by head_dim={head_dim}. "
88
+ f"Resulting head_v_dim would be {head_dim * expand_v}, which is invalid for FusedRMSNormGated.",
89
+ )
90
+ assert mode in ['chunk', 'fused_recurrent'], f"Not supported mode `{mode}`."
91
+
92
+ self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
93
+ self.k_proj = nn.Linear(hidden_size, self.key_dim * num_householder, bias=False)
94
+ self.v_proj = nn.Linear(hidden_size, self.value_dim * num_householder, bias=False)
95
+ self.b_proj = nn.Linear(hidden_size, self.num_v_heads * num_householder, bias=False)
96
+
97
+ if self.use_forget_gate:
98
+ self.a_proj = nn.Linear(hidden_size, self.num_v_heads, bias=False)
99
+ A = torch.empty(self.num_v_heads, dtype=torch.float32).uniform_(0, 16)
100
+ self.A_log = nn.Parameter(torch.log(A))
101
+ self.A_log._no_weight_decay = True
102
+ # hard coded for now
103
+ dt_min = 0.001
104
+ dt_max = 0.1
105
+ dt_init_floor = 1e-4
106
+ dt = torch.exp(
107
+ torch.rand(self.num_v_heads) * (math.log(dt_max) - math.log(dt_min))
108
+ + math.log(dt_min),
109
+ )
110
+ dt = torch.clamp(dt, min=dt_init_floor)
111
+ # Inverse of softplus: https://github.com/pytorch/pytorch/issues/72759
112
+ inv_dt = dt + torch.log(-torch.expm1(-dt))
113
+ self.dt_bias = nn.Parameter(inv_dt)
114
+ # Just to be explicit. Without this we already don't put wd on dt_bias because of the check
115
+ # name.endswith("bias") in param_grouping.py
116
+ self.dt_bias._no_weight_decay = True
117
+
118
+ if use_short_conv:
119
+ self.conv_size = conv_size
120
+ self.q_conv1d = ShortConvolution(
121
+ hidden_size=self.key_dim,
122
+ kernel_size=conv_size,
123
+ bias=conv_bias,
124
+ activation='silu',
125
+ )
126
+ self.k_conv1d = ShortConvolution(
127
+ hidden_size=self.key_dim * num_householder,
128
+ kernel_size=conv_size,
129
+ bias=conv_bias,
130
+ activation='silu',
131
+ )
132
+ self.v_conv1d = ShortConvolution(
133
+ hidden_size=self.value_dim * num_householder,
134
+ kernel_size=conv_size,
135
+ bias=conv_bias,
136
+ activation='silu',
137
+ )
138
+ else:
139
+ warnings.warn(
140
+ "ShortConvolution is crucial to the performance. "
141
+ "Do not turn it off, i.e., setting `use_short_conv=False` unless you know what you are doing.",
142
+ )
143
+ if use_output_gate:
144
+ self.g_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
145
+ self.o_norm = FusedRMSNormGated(self.head_v_dim, eps=norm_eps)
146
+ else:
147
+ self.o_norm = RMSNorm(self.head_v_dim, eps=norm_eps, dtype=torch.float32)
148
+ self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
149
+
150
+ def _initialize_weights(self, module: nn.Module):
151
+ if getattr(module, "_is_hf_initialized", False):
152
+ return
153
+ if isinstance(module, nn.Linear):
154
+ nn.init.xavier_uniform_(module.weight, gain=2 ** -2.5)
155
+ if module.bias is not None:
156
+ nn.init.zeros_(module.bias)
157
+ module._is_hf_initialized = True
158
+
159
+ def forward(
160
+ self,
161
+ hidden_states: torch.Tensor,
162
+ attention_mask: torch.Tensor | None = None,
163
+ past_key_values: Cache | None = None,
164
+ use_cache: bool | None = False,
165
+ output_attentions: bool | None = False,
166
+ **kwargs: Unpack[dict],
167
+ ) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
168
+ if attention_mask is not None:
169
+ assert len(attention_mask.shape) == 2, (
170
+ "Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
171
+ "for padding purposes (0 indicating padding). "
172
+ "Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
173
+ )
174
+
175
+ batch_size, q_len, _ = hidden_states.shape
176
+ # change to inference mode.
177
+ mode = 'fused_recurrent' if (q_len <= 64 and not self.training) else self.mode
178
+ if self.training:
179
+ assert mode == 'chunk', "Only chunk mode is supported in training."
180
+
181
+ last_state = get_layer_cache(self, past_key_values)
182
+
183
+ cu_seqlens = kwargs.get('cu_seqlens')
184
+ if attention_mask is not None:
185
+ indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
186
+ hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
187
+
188
+ if self.use_short_conv:
189
+ conv_state_q, conv_state_k, conv_state_v = None, None, None
190
+ if last_state is not None:
191
+ conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
192
+ q, conv_state_q = self.q_conv1d(
193
+ x=self.q_proj(hidden_states),
194
+ cache=conv_state_q,
195
+ output_final_state=use_cache,
196
+ cu_seqlens=cu_seqlens,
197
+ )
198
+ k, conv_state_k = self.k_conv1d(
199
+ x=self.k_proj(hidden_states),
200
+ cache=conv_state_k,
201
+ output_final_state=use_cache,
202
+ cu_seqlens=cu_seqlens,
203
+ )
204
+ v, conv_state_v = self.v_conv1d(
205
+ x=self.v_proj(hidden_states),
206
+ cache=conv_state_v,
207
+ output_final_state=use_cache,
208
+ cu_seqlens=cu_seqlens,
209
+ )
210
+ else:
211
+ q = F.silu(self.q_proj(hidden_states))
212
+ k = F.silu(self.k_proj(hidden_states))
213
+ v = F.silu(self.v_proj(hidden_states))
214
+
215
+ q = rearrange(q, '... (h d) -> ... h d', d=self.head_k_dim)
216
+ k = rearrange(k, '... t (n h d) -> ... (t n) h d', n=self.num_householder, d=self.head_k_dim)
217
+ v = rearrange(v, '... t (n h d) -> ... (t n) h d', n=self.num_householder, d=self.head_v_dim)
218
+
219
+ if self.num_v_heads > self.num_heads:
220
+ q, k = map(lambda x: repeat(x, '... h d -> ... (h g) d', g=self.num_v_heads // self.num_heads), (q, k))
221
+
222
+ beta = self.b_proj(hidden_states).sigmoid()
223
+ if self.allow_neg_eigval:
224
+ beta = beta * 2.
225
+
226
+ beta = rearrange(beta, '... t (n h) -> ... (t n) h', n=self.num_householder)
227
+ if self.use_forget_gate:
228
+ g = -self.A_log.float().exp() * F.softplus(self.a_proj(hidden_states).float() + self.dt_bias)
229
+ else:
230
+ g = None
231
+
232
+ recurrent_state = last_state['recurrent_state'] if last_state is not None else None
233
+ if mode == 'chunk':
234
+ o, recurrent_state = chunk_gated_delta_product(
235
+ q=q,
236
+ k=k,
237
+ v=v,
238
+ g=g,
239
+ beta=beta,
240
+ initial_state=recurrent_state,
241
+ output_final_state=use_cache,
242
+ cu_seqlens=cu_seqlens,
243
+ num_householder=self.num_householder,
244
+ use_qk_l2norm_in_kernel=True,
245
+ )
246
+
247
+ elif mode == 'fused_recurrent':
248
+ if self.use_forget_gate:
249
+ g_new = g.new_zeros(g.shape[0], g.shape[1], self.num_householder, g.shape[2])
250
+ g_new[:, :, 0] = g
251
+ g = rearrange(g_new, '... t n h -> ... (t n) h')
252
+
253
+ q_new = q.new_zeros(q.shape[0], q.shape[1], self.num_householder, q.shape[2], q.shape[3])
254
+ q_new[:, :, -1] = q
255
+ q = rearrange(q_new, '... t n h d-> ... (t n) h d')
256
+ o, recurrent_state = fused_recurrent_gated_delta_rule(
257
+ q=q,
258
+ k=k,
259
+ v=v,
260
+ g=g,
261
+ beta=beta,
262
+ initial_state=recurrent_state,
263
+ output_final_state=use_cache,
264
+ cu_seqlens=cu_seqlens * self.num_householder if cu_seqlens is not None else None,
265
+ use_qk_l2norm_in_kernel=True,
266
+ )
267
+ o = rearrange(o, '... (t n) h d -> ... t n h d', n=self.num_householder)[..., -1, :, :].contiguous()
268
+
269
+ update_layer_cache(
270
+ self,
271
+ past_key_values,
272
+ recurrent_state=recurrent_state,
273
+ conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
274
+ offset=q_len,
275
+ )
276
+
277
+ if self.use_output_gate:
278
+ g = rearrange(self.g_proj(hidden_states), '... (h d) -> ... h d', d=self.head_v_dim)
279
+ o = self.o_norm(o, g)
280
+ else:
281
+ o = self.o_norm(o)
282
+ o = rearrange(o, 'b t h d -> b t (h d)')
283
+ o = self.o_proj(o)
284
+ if attention_mask is not None:
285
+ o = pad_input(o.squeeze(0), indices, batch_size, q_len)
286
+
287
+ return o, None, past_key_values
fla/layers/gla.py ADDED
@@ -0,0 +1,304 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+
3
+
4
+ from __future__ import annotations
5
+
6
+ from typing import TYPE_CHECKING
7
+
8
+ import torch
9
+ import torch.nn as nn
10
+ import torch.nn.functional as F
11
+ from einops import rearrange, repeat
12
+
13
+ from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, update_layer_cache
14
+ from fla.modules import FusedRMSNormGated, RMSNorm, ShortConvolution
15
+ from fla.modules.activations import ACT2FN
16
+ from fla.ops.gla import chunk_gla, fused_chunk_gla, fused_recurrent_gla
17
+
18
+ if TYPE_CHECKING:
19
+ from transformers.processing_utils import Unpack
20
+
21
+ from fla.models.utils import Cache
22
+
23
+
24
+ class GatedLinearAttention(nn.Module):
25
+ r"""
26
+ The layer implementaion for [Gated Linear Attention Transformers with Hardware-Efficient Training](https://arxiv.org/abs/2312.06635). # noqa
27
+
28
+ Args:
29
+ mode (str, Optional):
30
+ Which GLA kernel to use.
31
+ Currently available: `chunk`, `fused_recurrent`, and `fused_chunk`.
32
+ Default: `chunk`.
33
+ hidden_size (int, Optional):
34
+ The hidden size of the input. Default: 1024.
35
+ expand_k (float, Optional):
36
+ The expansion ratio for the key dim. Default: 0.5.
37
+ expand_v (float, Optional):
38
+ The expansion ratio for the value dim. Default: 1.0.
39
+ num_heads (int, Optional):
40
+ The number of heads. Default: 4.
41
+ num_kv_heads (int, Optional):
42
+ The number of key/value heads, used for MQA. Default: None.
43
+ feature_map (str, Optional):
44
+ Feature map function applied to queries/keys. Default: None.
45
+ use_short_conv (bool, Optional):
46
+ Whether to use short convolutions. Default: `False`.
47
+ conv_size (int, Optional):
48
+ The kernel size of the short convolution, only used when `use_short_conv` is `True`. Default: 4.
49
+ conv_bias (bool, Optional):
50
+ Whether to use bias in the short convolution, only used when `use_short_conv` is `True`. Default: `False`.
51
+ use_output_gate (bool, Optional):
52
+ Whether to use output gate. Default: `True`.
53
+ gate_fn (str, Optional):
54
+ The activation function for the output gate. Default: `swish`.
55
+ elementwise_affine (bool, Optional):
56
+ If `True`, applies elementwise affine to LayerNorm with learnable parameters. Default: `True`.
57
+ norm_eps (float, Optional):
58
+ The epsilon value for the layernorm/rmsnorm layer. Default: 1e-5.
59
+ gate_logit_normalizer (int, Optional):
60
+ The normalizer for the gate logits, appied after `logsigmoid`. Default: 16.
61
+ gate_low_rank_dim (int, Optional):
62
+ The low rank dim for the gate projection. Default: 16.
63
+ clamp_min (float, Optional):
64
+ The minimum value for the gate logits. Default: None.
65
+ fuse_norm (bool, Optional):
66
+ Whether to fuse the norm and the output gate for better memory footprint. Default: `True`.
67
+ layer_idx (int, Optional):
68
+ The index of the layer. Default: None.
69
+ """
70
+
71
+ def __init__(
72
+ self,
73
+ mode: str = 'chunk',
74
+ hidden_size: int = 1024,
75
+ expand_k: float = 0.5,
76
+ expand_v: float = 1.0,
77
+ num_heads: int = 4,
78
+ num_kv_heads: int | None = None,
79
+ feature_map: str | None = None,
80
+ use_short_conv: bool = False,
81
+ conv_size: int = 4,
82
+ conv_bias: bool = False,
83
+ use_output_gate: bool = True,
84
+ gate_fn: str = 'swish',
85
+ elementwise_affine: bool | None = True,
86
+ norm_eps: float = 1e-5,
87
+ gate_logit_normalizer: int = 16,
88
+ gate_low_rank_dim: int = 16,
89
+ clamp_min: float | None = None,
90
+ fuse_norm: bool = True,
91
+ layer_idx: int = None,
92
+ ) -> GatedLinearAttention:
93
+ super().__init__()
94
+
95
+ self.mode = mode
96
+ self.hidden_size = hidden_size
97
+ self.expand_k = expand_k
98
+ self.expand_v = expand_v
99
+ self.num_heads = num_heads
100
+ self.num_kv_heads = num_kv_heads if num_kv_heads is not None else num_heads
101
+ self.num_kv_groups = self.num_heads // self.num_kv_heads
102
+ self.feature_map_fn = ACT2FN[feature_map] if feature_map is not None else None
103
+
104
+ self.use_short_conv = use_short_conv
105
+ self.conv_size = conv_size
106
+ self.conv_bias = conv_bias
107
+ self.use_output_gate = use_output_gate
108
+
109
+ self.key_dim = int(hidden_size * expand_k)
110
+ self.value_dim = int(hidden_size * expand_v)
111
+ self.key_dim_per_group = self.key_dim // self.num_kv_groups
112
+ self.value_dim_per_group = self.value_dim // self.num_kv_groups
113
+ self.clamp_min = clamp_min
114
+ self.layer_idx = layer_idx
115
+
116
+ assert mode in ['chunk', 'fused_recurrent', 'fused_chunk'], f"Not supported mode `{mode}`."
117
+ assert self.key_dim % num_heads == 0, f"key dim must be divisible by num_heads of {num_heads}"
118
+ assert self.value_dim % num_heads == 0, f"value dim must be divisible by num_heads of {num_heads}"
119
+
120
+ self.head_k_dim = self.key_dim // num_heads
121
+ self.head_v_dim = self.value_dim // num_heads
122
+
123
+ self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
124
+ self.k_proj = nn.Linear(hidden_size, self.key_dim_per_group, bias=False)
125
+ self.v_proj = nn.Linear(hidden_size, self.value_dim_per_group, bias=False)
126
+ if self.use_output_gate:
127
+ self.g_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
128
+
129
+ if use_short_conv:
130
+ self.conv_size = conv_size
131
+ self.q_conv1d = ShortConvolution(
132
+ hidden_size=self.key_dim,
133
+ kernel_size=conv_size,
134
+ bias=conv_bias,
135
+ activation='silu',
136
+ )
137
+ self.k_conv1d = ShortConvolution(
138
+ hidden_size=self.key_dim_per_group,
139
+ kernel_size=conv_size,
140
+ bias=conv_bias,
141
+ activation='silu',
142
+ )
143
+ self.v_conv1d = ShortConvolution(
144
+ hidden_size=self.value_dim_per_group,
145
+ kernel_size=conv_size,
146
+ bias=conv_bias,
147
+ activation='silu',
148
+ )
149
+
150
+ self.gk_proj = nn.Sequential(nn.Linear(hidden_size, gate_low_rank_dim, bias=False),
151
+ nn.Linear(gate_low_rank_dim, self.key_dim_per_group, bias=True))
152
+ self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
153
+
154
+ if gate_fn == 'swish' and fuse_norm and use_output_gate:
155
+ self.g_norm_swish_gate = FusedRMSNormGated(
156
+ hidden_size=self.head_v_dim,
157
+ elementwise_affine=elementwise_affine,
158
+ eps=norm_eps,
159
+ )
160
+ self.fuse_norm_and_gate = True
161
+ else:
162
+ self.fuse_norm_and_gate = False
163
+ self.g_norm = RMSNorm(
164
+ hidden_size=self.head_v_dim,
165
+ elementwise_affine=elementwise_affine,
166
+ eps=norm_eps,
167
+ dtype=torch.float32
168
+ )
169
+ self.gate_fn = ACT2FN[gate_fn]
170
+
171
+ self.gate_logit_normalizer = gate_logit_normalizer
172
+
173
+ def reset_parameters(self) -> None:
174
+ for module in self.children():
175
+ reset = getattr(module, "reset_parameters", None)
176
+ if callable(reset):
177
+ reset()
178
+
179
+ def forward(
180
+ self,
181
+ hidden_states: torch.Tensor,
182
+ attention_mask: torch.Tensor | None = None,
183
+ past_key_values: Cache | None = None,
184
+ use_cache: bool | None = False,
185
+ output_attentions: bool | None = False,
186
+ **kwargs: Unpack[dict],
187
+ ) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
188
+ if attention_mask is not None:
189
+ assert len(attention_mask.shape) == 2, (
190
+ "Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
191
+ "for padding purposes (0 indicating padding). "
192
+ "Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
193
+ )
194
+
195
+ batch_size, q_len, _ = hidden_states.shape
196
+ mode = 'fused_recurrent' if hidden_states.shape[1] <= 64 else self.mode
197
+
198
+ last_state = get_layer_cache(self, past_key_values)
199
+
200
+ cu_seqlens = kwargs.get('cu_seqlens')
201
+ if attention_mask is not None:
202
+ indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
203
+ hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
204
+
205
+ if self.use_short_conv:
206
+ conv_state_q, conv_state_k, conv_state_v = None, None, None
207
+ if last_state is not None:
208
+ conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
209
+ q, conv_state_q = self.q_conv1d(
210
+ x=self.q_proj(hidden_states),
211
+ cache=conv_state_q,
212
+ output_final_state=use_cache,
213
+ cu_seqlens=cu_seqlens,
214
+ )
215
+ k, conv_state_k = self.k_conv1d(
216
+ x=self.k_proj(hidden_states),
217
+ cache=conv_state_k,
218
+ output_final_state=use_cache,
219
+ cu_seqlens=cu_seqlens,
220
+ )
221
+ v, conv_state_v = self.v_conv1d(
222
+ x=self.v_proj(hidden_states),
223
+ cache=conv_state_v,
224
+ output_final_state=use_cache,
225
+ cu_seqlens=cu_seqlens,
226
+ )
227
+ else:
228
+ q = self.q_proj(hidden_states)
229
+ k = self.k_proj(hidden_states)
230
+ v = self.v_proj(hidden_states)
231
+ gk = self.gk_proj(hidden_states)
232
+
233
+ q = rearrange(q, '... (h d) -> ... h d', d=self.head_k_dim)
234
+ if self.num_kv_groups > 1:
235
+ k, gk = (repeat(x, '... (h d) -> ... (h g) d', g=self.num_kv_groups, d=self.head_k_dim) for x in (k, gk))
236
+ v = repeat(v, '... (h d) -> ... (h g) d', g=self.num_kv_groups, d=self.head_v_dim)
237
+ else:
238
+ k, gk = (rearrange(x, '... (h d) -> ... h d', d=self.head_k_dim) for x in (k, gk))
239
+ v = rearrange(v, '... (h d) -> ... h d', d=self.head_v_dim)
240
+
241
+ gk = F.logsigmoid(gk) / self.gate_logit_normalizer
242
+ if self.clamp_min is not None:
243
+ gk = torch.clamp_min(gk, self.clamp_min)
244
+
245
+ if self.feature_map_fn is not None:
246
+ q, k = map(self.feature_map_fn, (q, k))
247
+
248
+ recurrent_state = last_state['recurrent_state'] if last_state is not None else None
249
+ if mode == 'fused_recurrent':
250
+ o, recurrent_state = fused_recurrent_gla(
251
+ q=q,
252
+ k=k,
253
+ v=v,
254
+ gk=gk,
255
+ initial_state=recurrent_state,
256
+ output_final_state=use_cache,
257
+ cu_seqlens=cu_seqlens,
258
+ )
259
+ elif mode == 'fused_chunk':
260
+ o, recurrent_state = fused_chunk_gla(
261
+ q=q,
262
+ k=k,
263
+ v=v,
264
+ g=gk,
265
+ initial_state=recurrent_state,
266
+ output_final_state=use_cache,
267
+ )
268
+ elif mode == 'chunk':
269
+ o, recurrent_state = chunk_gla(
270
+ q=q,
271
+ k=k,
272
+ v=v,
273
+ g=gk,
274
+ initial_state=recurrent_state,
275
+ output_final_state=use_cache,
276
+ cu_seqlens=cu_seqlens,
277
+ )
278
+ else:
279
+ raise NotImplementedError(f"Not supported mode `{mode}`.")
280
+
281
+ update_layer_cache(
282
+ self,
283
+ past_key_values,
284
+ recurrent_state=recurrent_state,
285
+ conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
286
+ offset=q_len,
287
+ )
288
+
289
+ if self.use_output_gate:
290
+ g = self.g_proj(hidden_states)
291
+ if self.fuse_norm_and_gate:
292
+ g = rearrange(g, '... (h d) -> ... h d', d=self.head_v_dim)
293
+ o = self.g_norm_swish_gate(o, g)
294
+ o = rearrange(o, '... h d -> ... (h d)')
295
+ else:
296
+ o = rearrange(self.g_norm(o), '... h d -> ... (h d)')
297
+ o = o * self.gate_fn(g)
298
+ else:
299
+ o = rearrange(self.g_norm(o), '... h d -> ... (h d)')
300
+ o = self.o_proj(o)
301
+ if attention_mask is not None:
302
+ o = pad_input(o.squeeze(0), indices, batch_size, q_len)
303
+
304
+ return o, None, past_key_values
fla/layers/gsa.py ADDED
@@ -0,0 +1,237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+
3
+ from __future__ import annotations
4
+
5
+ import warnings
6
+ from typing import TYPE_CHECKING
7
+
8
+ import torch
9
+ import torch.nn as nn
10
+ import torch.nn.functional as F
11
+ from einops import rearrange, repeat
12
+
13
+ from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, update_layer_cache
14
+ from fla.modules import RMSNorm, ShortConvolution
15
+ from fla.modules.feature_map import ReLUFeatureMap, SwishFeatureMap, T2RFeatureMap
16
+ from fla.modules.layernorm import rms_norm_linear
17
+ from fla.ops.gsa import chunk_gsa, fused_recurrent_gsa
18
+
19
+ if TYPE_CHECKING:
20
+ from transformers.processing_utils import Unpack
21
+
22
+ from fla.models.utils import Cache
23
+
24
+
25
+ class GatedSlotAttention(nn.Module):
26
+
27
+ def __init__(
28
+ self,
29
+ mode: str = 'chunk',
30
+ hidden_size: int = 1024,
31
+ expand_k: float = 1.,
32
+ expand_v: float = 1.,
33
+ num_heads: int = 4,
34
+ num_kv_heads: int | None = None,
35
+ use_short_conv: bool = False,
36
+ conv_size: int = 4,
37
+ conv_bias: bool = False,
38
+ num_slots: int | None = None,
39
+ elementwise_affine: bool | None = True,
40
+ norm_eps: float = 1e-5,
41
+ gate_logit_normalizer: int = 8,
42
+ feature_map: str = 'swish',
43
+ use_output_gate: bool = False,
44
+ use_norm: bool = True,
45
+ layer_idx: int | None = None,
46
+ scale: float | None = 1.,
47
+ **kwargs,
48
+ ) -> GatedSlotAttention:
49
+ super().__init__()
50
+
51
+ self.mode = mode
52
+ self.hidden_size = hidden_size
53
+ self.expand_k = expand_k
54
+ self.expand_v = expand_v
55
+ self.num_heads = num_heads
56
+ self.num_kv_heads = num_heads if num_kv_heads is None else num_kv_heads
57
+ self.num_kv_groups = self.num_heads // self.num_kv_heads
58
+ self.key_dim = int(hidden_size * expand_k)
59
+ self.value_dim = int(hidden_size * expand_v)
60
+ self.key_dim_per_group = self.key_dim // self.num_kv_groups
61
+ self.value_dim_per_group = self.value_dim // self.num_kv_groups
62
+ self.head_k_dim = self.key_dim // self.num_heads
63
+ self.head_v_dim = self.value_dim // self.num_heads
64
+
65
+ self.use_short_conv = use_short_conv
66
+ self.conv_size = conv_size
67
+ self.conv_bias = conv_bias
68
+
69
+ self.gate_logit_normalizer = gate_logit_normalizer
70
+
71
+ self.use_output_gate = use_output_gate
72
+ self.use_norm = use_norm
73
+ self.scale = scale
74
+
75
+ if num_slots is None:
76
+ num_slots = self.head_k_dim
77
+ self.num_slots = num_slots
78
+
79
+ self.layer_idx = layer_idx
80
+
81
+ if layer_idx is None:
82
+ warnings.warn(
83
+ f"Instantiating {self.__class__.__name__} without passing `layer_idx` is not recommended and will "
84
+ "to errors during the forward call, if caching is used. Please make sure to provide a `layer_idx` "
85
+ "when creating this class.",
86
+ )
87
+
88
+ self.register_module('feature_map', None)
89
+ if feature_map == 'swish':
90
+ self.feature_map = SwishFeatureMap()
91
+ elif feature_map == 'relu':
92
+ self.feature_map = ReLUFeatureMap()
93
+ elif feature_map == 't2r':
94
+ self.feature_map = T2RFeatureMap(self.head_k_dim, self.head_k_dim)
95
+ else:
96
+ raise NotImplementedError(f"Feature map `{feature_map}` is not supported now.")
97
+
98
+ self.q_proj = nn.Linear(self.hidden_size, self.key_dim, bias=False)
99
+ self.k_proj = nn.Linear(self.hidden_size, self.key_dim_per_group, bias=False)
100
+ self.v_proj = nn.Linear(self.hidden_size, self.value_dim_per_group, bias=False)
101
+ self.f_proj = nn.Linear(self.hidden_size, self.num_kv_heads * self.num_slots, bias=False)
102
+
103
+ if use_short_conv:
104
+ self.conv_size = conv_size
105
+ self.q_conv1d = ShortConvolution(
106
+ hidden_size=self.key_dim,
107
+ kernel_size=conv_size,
108
+ bias=conv_bias,
109
+ activation='silu',
110
+ )
111
+ self.k_conv1d = ShortConvolution(
112
+ hidden_size=self.key_dim_per_group,
113
+ kernel_size=conv_size,
114
+ bias=conv_bias,
115
+ activation='silu',
116
+ )
117
+ self.v_conv1d = ShortConvolution(
118
+ hidden_size=self.value_dim_per_group,
119
+ kernel_size=conv_size,
120
+ bias=conv_bias,
121
+ activation='silu',
122
+ )
123
+
124
+ self.g_norm = RMSNorm(self.hidden_size, elementwise_affine, eps=norm_eps, dtype=torch.float32)
125
+ self.o_proj = nn.Linear(self.value_dim, self.hidden_size, bias=False)
126
+
127
+ def forward(
128
+ self,
129
+ hidden_states: torch.Tensor,
130
+ attention_mask: torch.Tensor | None = None,
131
+ past_key_values: Cache | None = None,
132
+ use_cache: bool | None = False,
133
+ output_attentions: bool | None = False,
134
+ **kwargs: Unpack[dict],
135
+ ) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
136
+ if attention_mask is not None:
137
+ assert len(attention_mask.shape) == 2, (
138
+ "Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
139
+ "for padding purposes (0 indicating padding). "
140
+ "Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
141
+ )
142
+
143
+ batch_size, q_len, _ = hidden_states.shape
144
+ mode = 'fused_recurrent' if hidden_states.shape[1] <= 64 else self.mode
145
+
146
+ last_state = get_layer_cache(self, past_key_values)
147
+
148
+ cu_seqlens = kwargs.get('cu_seqlens')
149
+ if attention_mask is not None:
150
+ indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
151
+ hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
152
+
153
+ if self.use_short_conv:
154
+ conv_state_q, conv_state_k, conv_state_v = None, None, None
155
+ if last_state is not None:
156
+ conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
157
+ q, conv_state_q = self.q_conv1d(
158
+ x=self.q_proj(hidden_states),
159
+ cache=conv_state_q,
160
+ output_final_state=use_cache,
161
+ cu_seqlens=cu_seqlens,
162
+ )
163
+ k, conv_state_k = self.k_conv1d(
164
+ x=self.k_proj(hidden_states),
165
+ cache=conv_state_k,
166
+ output_final_state=use_cache,
167
+ cu_seqlens=cu_seqlens,
168
+ )
169
+ v, conv_state_v = self.v_conv1d(
170
+ x=self.v_proj(hidden_states),
171
+ cache=conv_state_v,
172
+ output_final_state=use_cache,
173
+ cu_seqlens=cu_seqlens,
174
+ )
175
+ else:
176
+ q = self.q_proj(hidden_states)
177
+ k = self.k_proj(hidden_states)
178
+ v = self.v_proj(hidden_states)
179
+ f = self.f_proj(hidden_states)
180
+
181
+ q = rearrange(q, '... (h d) -> ... h d', d=self.head_k_dim)
182
+ k = rearrange(k, '... (h d) -> ... h d', d=self.head_k_dim)
183
+ v = rearrange(v, '... (h d) -> ... h d', d=self.head_v_dim)
184
+ f = rearrange(f, '... (h m) -> ... h m', m=self.num_slots)
185
+
186
+ if self.feature_map is not None:
187
+ q, k = map(lambda x: self.feature_map(x), (q, k))
188
+ v = F.silu(v)
189
+
190
+ f = F.logsigmoid(f) / self.gate_logit_normalizer
191
+ s = (1 - f.exp()).to(f.dtype)
192
+
193
+ if self.num_kv_groups > 1:
194
+ k, v, f, s = map(lambda x: repeat(x, '... h d -> ... (h g) d', g=self.num_kv_groups), (k, v, f, s))
195
+
196
+ recurrent_state = last_state['recurrent_state'] if last_state is not None else None
197
+ if mode == 'fused_recurrent':
198
+ o, recurrent_state = fused_recurrent_gsa(
199
+ q=q,
200
+ k=k,
201
+ v=v,
202
+ s=s,
203
+ g=f,
204
+ initial_state=recurrent_state,
205
+ output_final_state=use_cache,
206
+ scale=self.scale,
207
+ cu_seqlens=cu_seqlens,
208
+ )
209
+ elif mode == 'chunk':
210
+ o, recurrent_state = chunk_gsa(
211
+ q=q,
212
+ k=k,
213
+ v=v,
214
+ s=s,
215
+ g=f,
216
+ initial_state=recurrent_state,
217
+ output_final_state=use_cache,
218
+ scale=self.scale,
219
+ cu_seqlens=cu_seqlens,
220
+ )
221
+ else:
222
+ raise NotImplementedError(f"Not supported mode `{mode}`.")
223
+
224
+ update_layer_cache(
225
+ self,
226
+ past_key_values,
227
+ recurrent_state=recurrent_state,
228
+ conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
229
+ offset=q_len,
230
+ )
231
+
232
+ o = rearrange(o, '... h d -> ... (h d)')
233
+ o = rms_norm_linear(F.silu(o), self.g_norm.weight, self.g_norm.bias, self.o_proj.weight, self.o_proj.bias)
234
+ if attention_mask is not None:
235
+ o = pad_input(o.squeeze(0), indices, batch_size, q_len)
236
+
237
+ return o, None, past_key_values
fla/layers/hgrn.py ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+
3
+ # "Hierarchically Gated Recurrent Neural Network for Sequence Modeling" [https://arxiv.org/abs/2311.04823]
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import TYPE_CHECKING
8
+
9
+ import torch
10
+ import torch.nn as nn
11
+ import torch.nn.functional as F
12
+
13
+ from fla.layers.utils import get_layer_cache, update_layer_cache
14
+ from fla.modules import FusedRMSNormGated, ShortConvolution
15
+ from fla.modules.activations import swiglu
16
+ from fla.ops.hgrn import chunk_hgrn, fused_recurrent_hgrn
17
+
18
+ if TYPE_CHECKING:
19
+ from transformers.processing_utils import Unpack
20
+
21
+ from fla.models.utils import Cache
22
+
23
+
24
+ class HGRNAttention(nn.Module):
25
+
26
+ def __init__(
27
+ self,
28
+ mode: str = 'chunk',
29
+ hidden_size: int = 1024,
30
+ expand_ratio: int | None = 1,
31
+ use_short_conv: bool = False,
32
+ conv_size: int = 4,
33
+ conv_bias: bool = False,
34
+ elementwise_affine: bool | None = True,
35
+ norm_eps: float = 1e-5,
36
+ layer_idx: int = None,
37
+ ) -> HGRNAttention:
38
+ super().__init__()
39
+
40
+ self.mode = mode
41
+ self.hidden_size = hidden_size
42
+ self.expand_ratio = expand_ratio
43
+ self.input_dim = int(hidden_size * expand_ratio)
44
+
45
+ self.use_short_conv = use_short_conv
46
+ self.conv_size = conv_size
47
+ self.conv_bias = conv_bias
48
+
49
+ self.layer_idx = layer_idx
50
+
51
+ assert mode in ['chunk', 'fused_recurrent'], f"Not supported mode `{mode}`."
52
+
53
+ self.i_proj = nn.Linear(hidden_size, self.input_dim, bias=False)
54
+ self.f_proj = nn.Linear(hidden_size, self.input_dim, bias=False)
55
+ self.g_proj = nn.Linear(hidden_size, self.input_dim, bias=False)
56
+
57
+ if use_short_conv:
58
+ self.conv_size = conv_size
59
+ self.f_conv1d = ShortConvolution(
60
+ hidden_size=self.input_dim,
61
+ kernel_size=conv_size,
62
+ bias=conv_bias,
63
+ activation=None,
64
+ )
65
+ self.i_conv1d = ShortConvolution(
66
+ hidden_size=self.input_dim,
67
+ kernel_size=conv_size,
68
+ bias=conv_bias,
69
+ activation=None,
70
+ )
71
+
72
+ self.g_norm = FusedRMSNormGated(
73
+ hidden_size=self.input_dim,
74
+ elementwise_affine=elementwise_affine,
75
+ eps=norm_eps,
76
+ )
77
+ self.o_proj = nn.Linear(self.input_dim, hidden_size, bias=False)
78
+
79
+ def forward(
80
+ self,
81
+ hidden_states: torch.Tensor,
82
+ attention_mask: torch.Tensor | None = None,
83
+ past_key_values: Cache | None = None,
84
+ use_cache: bool | None = False,
85
+ output_attentions: bool | None = False,
86
+ lower_bound: torch.Tensor | None = None,
87
+ **kwargs: Unpack[dict],
88
+ ) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
89
+ if attention_mask is not None:
90
+ assert len(attention_mask.shape) == 2, (
91
+ "Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
92
+ "for padding purposes (0 indicating padding). "
93
+ "Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
94
+ )
95
+
96
+ # launching the triton kernel for just one token will actually be slower
97
+ mode = 'fused_recurrent' if not self.training and hidden_states.shape[1] <= 64 else self.mode
98
+
99
+ last_state = get_layer_cache(self, past_key_values)
100
+
101
+ cu_seqlens = kwargs.get('cu_seqlens')
102
+ if self.use_short_conv:
103
+ conv_state_i, conv_state_f = None, None
104
+ if last_state is not None:
105
+ conv_state_i, conv_state_f = last_state['conv_state']
106
+ conv_mask = attention_mask[:, -hidden_states.shape[1]:] if attention_mask is not None else None
107
+ i, conv_state_i = self.i_conv1d(
108
+ x=self.i_proj(hidden_states),
109
+ mask=conv_mask,
110
+ cache=conv_state_i,
111
+ output_final_state=use_cache,
112
+ cu_seqlens=cu_seqlens,
113
+ )
114
+ f, conv_state_f = self.f_conv1d(
115
+ x=self.f_proj(hidden_states),
116
+ mask=conv_mask,
117
+ cache=conv_state_f,
118
+ output_final_state=use_cache,
119
+ cu_seqlens=cu_seqlens,
120
+ )
121
+ else:
122
+ i = self.i_proj(hidden_states)
123
+ f = self.f_proj(hidden_states)
124
+
125
+ f = F.logsigmoid(f)
126
+ # the lower bound for the first layer is zero
127
+ if lower_bound is not None and self.layer_idx > 0:
128
+ f = torch.logaddexp(lower_bound.log(), torch.log1p(-lower_bound) + f).to(f)
129
+ i = swiglu(i, 1 - f.exp())
130
+
131
+ # dealing with left-padding
132
+ if attention_mask is not None:
133
+ i = i.mul(attention_mask[:, -i.shape[-2]:, None])
134
+
135
+ recurrent_state = last_state['recurrent_state'] if last_state is not None else None
136
+ if mode == 'chunk':
137
+ if cu_seqlens is not None:
138
+ raise NotImplementedError("Chunk mode does not support variable-length sequences.")
139
+ o, recurrent_state = chunk_hgrn(
140
+ x=i,
141
+ g=f,
142
+ initial_state=recurrent_state,
143
+ output_final_state=use_cache,
144
+ )
145
+ elif mode == 'fused_recurrent':
146
+ o, recurrent_state = fused_recurrent_hgrn(
147
+ x=i,
148
+ g=f,
149
+ initial_state=recurrent_state,
150
+ output_final_state=use_cache,
151
+ cu_seqlens=cu_seqlens,
152
+ )
153
+ else:
154
+ raise NotImplementedError(f"Not supported mode `{mode}`.")
155
+
156
+ update_layer_cache(
157
+ self,
158
+ past_key_values,
159
+ recurrent_state=recurrent_state,
160
+ conv_state=(conv_state_i, conv_state_f) if self.use_short_conv else None,
161
+ offset=i.shape[1],
162
+ )
163
+
164
+ o = self.g_norm(o, self.g_proj(hidden_states))
165
+ o = self.o_proj(o)
166
+
167
+ return o, None, past_key_values
168
+
169
+ def state_size(self, **kwargs) -> int:
170
+ state_size = self.hidden_size
171
+ for module in self.children():
172
+ if isinstance(module, ShortConvolution):
173
+ state_size += module.state_size
174
+ return state_size
fla/layers/hgrn2.py ADDED
@@ -0,0 +1,210 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+
3
+ # "HGRN2: Gated Linear RNNs with State Expansion"[https://arxiv.org/abs/2404.07904]
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import TYPE_CHECKING
8
+
9
+ import torch
10
+ import torch.nn as nn
11
+ import torch.nn.functional as F
12
+ from einops import rearrange
13
+
14
+ from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, update_layer_cache
15
+ from fla.modules import RMSNorm, ShortConvolution
16
+ from fla.modules.activations import swish
17
+ from fla.modules.layernorm import rms_norm_linear
18
+ from fla.ops.gla import chunk_gla, fused_chunk_gla, fused_recurrent_gla
19
+
20
+ if TYPE_CHECKING:
21
+ from transformers.processing_utils import Unpack
22
+
23
+ from fla.models.utils import Cache
24
+
25
+
26
+ class HGRN2Attention(nn.Module):
27
+
28
+ def __init__(
29
+ self,
30
+ mode: str = 'chunk',
31
+ hidden_size: int = 1024,
32
+ num_heads: int | None = None,
33
+ expand_ratio: int | None = 128,
34
+ use_short_conv: bool = False,
35
+ conv_size: int = 4,
36
+ conv_bias: bool = False,
37
+ elementwise_affine: bool | None = True,
38
+ norm_eps: float = 1e-5,
39
+ layer_idx: int = None,
40
+ ) -> HGRN2Attention:
41
+ super().__init__()
42
+
43
+ self.mode = mode
44
+ self.hidden_size = hidden_size
45
+
46
+ if expand_ratio is not None:
47
+ num_heads = hidden_size // expand_ratio
48
+ elif expand_ratio is None and num_heads is not None:
49
+ expand_ratio = hidden_size // num_heads
50
+ elif expand_ratio is None and num_heads is None:
51
+ raise RuntimeError("One of `expand_ratio` or `num_heads` should be provided.")
52
+ self.num_heads = num_heads
53
+ self.expand_ratio = expand_ratio
54
+
55
+ self.use_short_conv = use_short_conv
56
+ self.conv_size = conv_size
57
+ self.conv_bias = conv_bias
58
+
59
+ self.forget_dim = int(self.num_heads * self.expand_ratio)
60
+ self.input_dim = hidden_size
61
+ self.layer_idx = layer_idx
62
+
63
+ assert mode in ['chunk', 'fused_recurrent', 'fused_chunk'], f"Not supported mode `{mode}`."
64
+ assert self.forget_dim % num_heads == 0, f"forget dim must be divisible by num_heads of {num_heads}"
65
+ assert self.input_dim % num_heads == 0, f"input dim must be divisible by num_heads of {num_heads}"
66
+
67
+ self.head_f_dim = self.expand_ratio
68
+ self.head_i_dim = self.hidden_size // num_heads
69
+
70
+ self.q_proj = nn.Linear(hidden_size, self.forget_dim, bias=False)
71
+ self.f_proj = nn.Linear(hidden_size, self.forget_dim, bias=False)
72
+ self.i_proj = nn.Linear(hidden_size, self.input_dim, bias=False)
73
+
74
+ if use_short_conv:
75
+ self.conv_size = conv_size
76
+ self.q_conv1d = ShortConvolution(
77
+ hidden_size=self.forget_dim,
78
+ kernel_size=conv_size,
79
+ bias=conv_bias,
80
+ activation=None,
81
+ )
82
+ self.f_conv1d = ShortConvolution(
83
+ hidden_size=self.forget_dim,
84
+ kernel_size=conv_size,
85
+ bias=conv_bias,
86
+ activation=None,
87
+ )
88
+ self.i_conv1d = ShortConvolution(
89
+ hidden_size=self.input_dim,
90
+ kernel_size=conv_size,
91
+ bias=conv_bias,
92
+ activation=None,
93
+ )
94
+
95
+ self.g_norm = RMSNorm(hidden_size=self.hidden_size, elementwise_affine=elementwise_affine,
96
+ eps=norm_eps, dtype=torch.float32)
97
+ self.o_proj = nn.Linear(self.input_dim, hidden_size, bias=False)
98
+
99
+ def forward(
100
+ self,
101
+ hidden_states: torch.Tensor,
102
+ attention_mask: torch.Tensor | None = None,
103
+ past_key_values: Cache | None = None,
104
+ use_cache: bool | None = False,
105
+ output_attentions: bool | None = False,
106
+ lower_bound: torch.Tensor | None = None,
107
+ **kwargs: Unpack[dict],
108
+ ) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
109
+ if attention_mask is not None:
110
+ assert len(attention_mask.shape) == 2, (
111
+ "Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
112
+ "for padding purposes (0 indicating padding). "
113
+ "Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
114
+ )
115
+
116
+ batch_size, q_len, _ = hidden_states.shape
117
+ mode = 'fused_recurrent' if hidden_states.shape[1] <= 64 else self.mode
118
+
119
+ last_state = get_layer_cache(self, past_key_values)
120
+
121
+ cu_seqlens = kwargs.get('cu_seqlens')
122
+ if attention_mask is not None:
123
+ indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
124
+ hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
125
+
126
+ if self.use_short_conv:
127
+ conv_state_q, conv_state_f, conv_state_i = None, None, None
128
+ if last_state is not None:
129
+ conv_state_q, conv_state_f, conv_state_i = last_state['conv_state']
130
+ q, conv_state_q = self.q_conv1d(
131
+ x=self.q_proj(hidden_states),
132
+ cache=conv_state_q,
133
+ output_final_state=use_cache,
134
+ cu_seqlens=cu_seqlens,
135
+ )
136
+ f, conv_state_f = self.f_conv1d(
137
+ x=self.f_proj(hidden_states),
138
+ cache=conv_state_f,
139
+ output_final_state=use_cache,
140
+ cu_seqlens=cu_seqlens,
141
+ )
142
+ i, conv_state_i = self.i_conv1d(
143
+ x=self.i_proj(hidden_states),
144
+ cache=conv_state_i,
145
+ output_final_state=use_cache,
146
+ cu_seqlens=cu_seqlens,
147
+ )
148
+ else:
149
+ q = self.q_proj(hidden_states)
150
+ f = self.f_proj(hidden_states)
151
+ i = self.i_proj(hidden_states)
152
+
153
+ q = swish(q)
154
+
155
+ g = F.logsigmoid(f)
156
+ # the lower bound for the first layer is zero
157
+ if lower_bound is not None and self.layer_idx > 0:
158
+ g = torch.logaddexp(lower_bound.log(), torch.log1p(-lower_bound) + g)
159
+ k = 1 - g.exp()
160
+
161
+ q, k, g = map(lambda x: rearrange(x, '... (h d) -> ... h d', d=self.head_f_dim), (q, k.to(i), g))
162
+ i = rearrange(i, '... (h d) -> ... h d', d=self.head_i_dim)
163
+
164
+ recurrent_state = last_state['recurrent_state'] if last_state is not None else None
165
+ if mode == 'fused_recurrent':
166
+ o, recurrent_state = fused_recurrent_gla(
167
+ q=q,
168
+ k=k,
169
+ v=i,
170
+ gk=g,
171
+ initial_state=recurrent_state,
172
+ output_final_state=use_cache,
173
+ cu_seqlens=cu_seqlens,
174
+ )
175
+ elif mode == 'fused_chunk':
176
+ o, recurrent_state = fused_chunk_gla(
177
+ q=q,
178
+ k=k,
179
+ v=i,
180
+ g=g,
181
+ initial_state=recurrent_state,
182
+ output_final_state=use_cache,
183
+ )
184
+ elif mode == 'chunk':
185
+ o, recurrent_state = chunk_gla(
186
+ q=q,
187
+ k=k,
188
+ v=i,
189
+ g=g,
190
+ initial_state=recurrent_state,
191
+ output_final_state=use_cache,
192
+ cu_seqlens=cu_seqlens,
193
+ )
194
+ else:
195
+ raise NotImplementedError(f"Not supported mode `{mode}`.")
196
+
197
+ update_layer_cache(
198
+ self,
199
+ past_key_values,
200
+ recurrent_state=recurrent_state,
201
+ conv_state=(conv_state_q, conv_state_f, conv_state_i) if self.use_short_conv else None,
202
+ offset=q_len,
203
+ )
204
+
205
+ o = rearrange(o, '... h d -> ... (h d)')
206
+ o = rms_norm_linear(o, self.g_norm.weight, self.g_norm.bias, self.o_proj.weight, self.o_proj.bias)
207
+ if attention_mask is not None:
208
+ o = pad_input(o.squeeze(0), indices, batch_size, q_len)
209
+
210
+ return o, None, past_key_values
fla/layers/kda.py ADDED
@@ -0,0 +1,277 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+
3
+ from __future__ import annotations
4
+
5
+ import math
6
+ from typing import TYPE_CHECKING
7
+
8
+ import torch
9
+ import torch.nn as nn
10
+ from einops import rearrange, repeat
11
+ from torch.nn import functional as F
12
+
13
+ from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, update_layer_cache
14
+ from fla.modules import FusedRMSNormGated, ShortConvolution
15
+ from fla.ops.kda import chunk_kda, fused_recurrent_kda
16
+ from fla.ops.kda.gate import fused_kda_gate
17
+
18
+ if TYPE_CHECKING:
19
+ from transformers.processing_utils import Unpack
20
+
21
+ from fla.models.utils import Cache
22
+
23
+
24
+ class KimiDeltaAttention(nn.Module):
25
+ """
26
+ Kimi Delta Attention (KDA) layer implementation.
27
+
28
+ Args:
29
+ hidden_size (int, Optional):
30
+ The hidden size of the input. Default: 2048.
31
+ expand_v (float, Optional):
32
+ The expansion ratio for the value dimension. Default: 1.0.
33
+ head_dim (int, Optional):
34
+ The dimension of each head. Default: 128.
35
+ num_heads (int, Optional):
36
+ The number of heads. Default: 16.
37
+ num_v_heads (int, Optional):
38
+ The number of heads for the value projection, equal to `num_heads` if `None`.
39
+ GVA (Grouped Value Attention) is applied if `num_v_heads` > `num_heads`. Default: `None`.
40
+ mode (str, Optional):
41
+ Which Kimi Delta Attention kernel to use.
42
+ Currently available: `chunk` and `fused_recurrent`.
43
+ Default: `chunk`.
44
+ use_short_conv (bool, Optional):
45
+ Whether to use short convolutions. Default: `True`.
46
+ allow_neg_eigval (bool, Optional):
47
+ Allow negative eigenvalues. Default: `False`. If set to `True`, the beta will be multiplied by 2.
48
+ See reference:
49
+ [Unlocking State-Tracking in Linear RNNs Through Negative Eigenvalues](https://arxiv.org/abs/2411.12537)
50
+ conv_size (int, Optional):
51
+ The kernel size of the short convolution, only used when `use_short_conv` is `True`. Default: 4.
52
+ conv_bias (bool, Optional):
53
+ Whether to use bias in the short convolution, only used when `use_short_conv` is `True`. Default: `False`.
54
+ layer_idx (int, Optional):
55
+ The index of the layer. Default: None.
56
+ norm_eps (float, Optional):
57
+ The epsilon value for the normalization layer. Default: 1e-5.
58
+ """
59
+
60
+ def __init__(
61
+ self,
62
+ hidden_size: int = 2048,
63
+ expand_v: float = 1,
64
+ head_dim: int = 128,
65
+ num_heads: int = 16,
66
+ num_v_heads: int = None,
67
+ mode: str = "chunk",
68
+ use_short_conv: bool = True,
69
+ allow_neg_eigval: bool = False,
70
+ conv_size: int = 4,
71
+ conv_bias: bool = False,
72
+ layer_idx: int = None,
73
+ norm_eps: float = 1e-5,
74
+ **kwargs,
75
+ ) -> KimiDeltaAttention:
76
+ super().__init__()
77
+
78
+ self.mode = mode
79
+ self.allow_neg_eigval = allow_neg_eigval
80
+ self.hidden_size = hidden_size
81
+ self.expand_v = expand_v
82
+
83
+ self.use_short_conv = use_short_conv
84
+ self.conv_size = conv_size
85
+ self.conv_bias = conv_bias
86
+
87
+ self.head_dim = head_dim
88
+ self.num_heads = num_heads
89
+ self.num_v_heads = num_v_heads if num_v_heads is not None else num_heads
90
+
91
+ self.head_k_dim = head_dim
92
+ self.head_v_dim = int(self.head_dim * self.expand_v)
93
+ self.key_dim = int(self.num_heads * self.head_k_dim)
94
+ self.value_dim = int(self.num_v_heads * self.head_v_dim)
95
+ self.layer_idx = layer_idx
96
+
97
+ # Consistency check: Ensure expand_v produces integer values
98
+ if not math.isclose(self.num_v_heads * self.head_dim * expand_v, self.value_dim, rel_tol=1e-5):
99
+ raise ValueError(
100
+ f"expand_v={expand_v} does not produce an integer value when multiplied by key_dim={self.key_dim}. "
101
+ f"Resulting value_dim would be {self.num_v_heads * self.head_dim * expand_v}, which is invalid for nn.Linear.",
102
+ )
103
+ if self.num_v_heads > self.num_heads and self.num_v_heads % self.num_heads != 0:
104
+ raise ValueError(
105
+ f"num_v_heads={self.num_v_heads} must be divisible by num_heads={self.num_heads}.",
106
+ )
107
+
108
+ if not math.isclose(head_dim * expand_v, self.head_v_dim, rel_tol=1e-5):
109
+ raise ValueError(
110
+ f"expand_v={expand_v} does not produce an integer value when multiplied by head_dim={head_dim}. "
111
+ f"Resulting head_v_dim would be {head_dim * expand_v}, which is invalid for FusedRMSNormGated.",
112
+ )
113
+ assert mode in ["chunk", "fused_recurrent"], f"Not supported mode `{mode}`."
114
+
115
+ self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
116
+ self.k_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
117
+ self.v_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
118
+
119
+ if use_short_conv:
120
+ self.q_conv1d = ShortConvolution(
121
+ hidden_size=self.key_dim,
122
+ kernel_size=conv_size,
123
+ bias=conv_bias,
124
+ activation="silu",
125
+ )
126
+ self.k_conv1d = ShortConvolution(
127
+ hidden_size=self.key_dim,
128
+ kernel_size=conv_size,
129
+ bias=conv_bias,
130
+ activation="silu",
131
+ )
132
+ self.v_conv1d = ShortConvolution(
133
+ hidden_size=self.value_dim,
134
+ kernel_size=conv_size,
135
+ bias=conv_bias,
136
+ activation="silu",
137
+ )
138
+
139
+ self.f_proj = nn.Sequential(
140
+ nn.Linear(hidden_size, self.head_v_dim, bias=False),
141
+ nn.Linear(self.head_v_dim, self.key_dim, bias=False),
142
+ )
143
+ self.b_proj = nn.Linear(hidden_size, self.num_heads, bias=False)
144
+
145
+ self.A_log = nn.Parameter(torch.log(torch.empty(self.num_heads, dtype=torch.float32).uniform_(1, 16)))
146
+ self.A_log._no_weight_decay = True
147
+ dt = torch.exp(
148
+ torch.rand(self.key_dim, dtype=torch.float32) * (math.log(0.1) - math.log(0.001)) + math.log(0.001)
149
+ ).clamp(min=1e-4)
150
+ inv_dt = dt + torch.log(-torch.expm1(-dt))
151
+ self.dt_bias = nn.Parameter(inv_dt)
152
+ self.dt_bias._no_weight_decay = True
153
+
154
+ self.g_proj = nn.Sequential(
155
+ nn.Linear(hidden_size, self.head_v_dim, bias=False),
156
+ nn.Linear(self.head_v_dim, self.value_dim, bias=True),
157
+ )
158
+ self.o_norm = FusedRMSNormGated(self.head_v_dim, activation="sigmoid", eps=norm_eps)
159
+ self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
160
+
161
+ def forward(
162
+ self,
163
+ hidden_states: torch.Tensor,
164
+ attention_mask: torch.Tensor | None = None,
165
+ past_key_values: Cache | None = None,
166
+ use_cache: bool | None = False,
167
+ output_attentions: bool | None = False,
168
+ **kwargs: Unpack[dict],
169
+ ) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
170
+ if attention_mask is not None:
171
+ assert len(attention_mask.shape) == 2, (
172
+ "Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
173
+ "for padding purposes (0 indicating padding). "
174
+ "Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
175
+ )
176
+
177
+ batch_size, q_len, _ = hidden_states.shape
178
+ # change to inference mode.
179
+ mode = "fused_recurrent" if (q_len <= 64 and not self.training) else self.mode
180
+ if self.training:
181
+ assert mode == "chunk", "Only chunk mode is supported in training."
182
+
183
+ last_state = get_layer_cache(self, past_key_values)
184
+
185
+ cu_seqlens = kwargs.get("cu_seqlens")
186
+ if attention_mask is not None:
187
+ indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
188
+ hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
189
+
190
+ if self.use_short_conv:
191
+ conv_state_q, conv_state_k, conv_state_v = None, None, None
192
+ if last_state is not None:
193
+ conv_state_q, conv_state_k, conv_state_v = last_state["conv_state"]
194
+ q, conv_state_q = self.q_conv1d(
195
+ x=self.q_proj(hidden_states),
196
+ cache=conv_state_q,
197
+ output_final_state=use_cache,
198
+ cu_seqlens=cu_seqlens,
199
+ )
200
+ k, conv_state_k = self.k_conv1d(
201
+ x=self.k_proj(hidden_states),
202
+ cache=conv_state_k,
203
+ output_final_state=use_cache,
204
+ cu_seqlens=cu_seqlens,
205
+ )
206
+ v, conv_state_v = self.v_conv1d(
207
+ x=self.v_proj(hidden_states),
208
+ cache=conv_state_v,
209
+ output_final_state=use_cache,
210
+ cu_seqlens=cu_seqlens,
211
+ )
212
+ else:
213
+ q = F.silu(self.q_proj(hidden_states))
214
+ k = F.silu(self.k_proj(hidden_states))
215
+ v = F.silu(self.v_proj(hidden_states))
216
+
217
+ g = self.f_proj(hidden_states)
218
+ beta = self.b_proj(hidden_states).sigmoid()
219
+
220
+ q, k, g = (rearrange(x, "... (h d) -> ... h d", d=self.head_k_dim) for x in (q, k, g))
221
+ v = rearrange(v, "... (h d) -> ... h d", d=self.head_v_dim)
222
+
223
+ # for multi-value attention, we repeat the inputs for simplicity.
224
+ if self.num_v_heads > self.num_heads:
225
+ q, k, g = (repeat(x, "... h d -> ... (h g) d", g=self.num_v_heads // self.num_heads) for x in (q, k, g))
226
+ beta = repeat(beta, "... h -> ... (h g)", g=self.num_v_heads // self.num_heads)
227
+
228
+ if self.allow_neg_eigval:
229
+ beta = beta * 2.0
230
+
231
+ recurrent_state = last_state["recurrent_state"] if last_state is not None else None
232
+ if mode == "chunk":
233
+ o, recurrent_state = chunk_kda(
234
+ q=q,
235
+ k=k,
236
+ v=v,
237
+ g=g,
238
+ beta=beta,
239
+ A_log=self.A_log,
240
+ dt_bias=self.dt_bias,
241
+ initial_state=recurrent_state,
242
+ output_final_state=use_cache,
243
+ use_qk_l2norm_in_kernel=True,
244
+ use_gate_in_kernel=True,
245
+ cu_seqlens=cu_seqlens,
246
+ )
247
+ elif mode == "fused_recurrent":
248
+ g = fused_kda_gate(g=g, A_log=self.A_log, dt_bias=self.dt_bias)
249
+ o, recurrent_state = fused_recurrent_kda(
250
+ q=q,
251
+ k=k,
252
+ v=v,
253
+ g=g,
254
+ beta=beta,
255
+ initial_state=recurrent_state,
256
+ output_final_state=use_cache,
257
+ use_qk_l2norm_in_kernel=True,
258
+ cu_seqlens=cu_seqlens,
259
+ )
260
+ else:
261
+ raise NotImplementedError(f"Not supported mode `{mode}`.")
262
+
263
+ update_layer_cache(
264
+ self,
265
+ past_key_values,
266
+ recurrent_state=recurrent_state,
267
+ conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
268
+ offset=q_len,
269
+ )
270
+
271
+ o = self.o_norm(o, rearrange(self.g_proj(hidden_states), "... (h d) -> ... h d", d=self.head_v_dim))
272
+ o = rearrange(o, "b t h d -> b t (h d)")
273
+ o = self.o_proj(o)
274
+ if attention_mask is not None:
275
+ o = pad_input(o.squeeze(0), indices, batch_size, q_len)
276
+
277
+ return o, None, past_key_values
fla/layers/lightnet.py ADDED
@@ -0,0 +1,238 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+
3
+ # ["You Only Scan Once: Efficient Multi-dimension Sequential Modeling with LightNet"](https://arxiv.org/abs/2405.21022)
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import TYPE_CHECKING
8
+
9
+ import torch
10
+ import torch.nn as nn
11
+ import torch.nn.functional as F
12
+ from einops import rearrange
13
+
14
+ from fla.layers.utils import get_layer_cache, update_layer_cache
15
+ from fla.modules import FusedRMSNormGated, ShortConvolution
16
+ from fla.modules.fused_norm_gate import rms_norm_swish_gate_linear
17
+ from fla.ops.gla import chunk_gla, fused_recurrent_gla
18
+
19
+ if TYPE_CHECKING:
20
+ from transformers.processing_utils import Unpack
21
+
22
+ from fla.models.utils import Cache
23
+
24
+
25
+ class LightNetAttention(nn.Module):
26
+
27
+ def __init__(
28
+ self,
29
+ mode: str = 'chunk',
30
+ hidden_size: int = 1024,
31
+ num_heads: int | None = None,
32
+ expand_ratio: int | None = 128,
33
+ use_short_conv: bool = False,
34
+ conv_size: int = 4,
35
+ conv_bias: bool = False,
36
+ gate_low_rank_dim: int = 128,
37
+ elementwise_affine: bool | None = True,
38
+ norm_eps: float = 1e-5,
39
+ layer_idx: int = None,
40
+ ) -> LightNetAttention:
41
+ super().__init__()
42
+
43
+ self.mode = mode
44
+ self.hidden_size = hidden_size
45
+
46
+ if expand_ratio is None and num_heads is not None:
47
+ expand_ratio = hidden_size // num_heads
48
+ elif expand_ratio is not None and num_heads is None:
49
+ num_heads = hidden_size // expand_ratio
50
+ elif expand_ratio is None and num_heads is None:
51
+ raise RuntimeError("One of `expand_ratio` or `num_heads` should be provided.")
52
+ self.num_heads = num_heads
53
+ self.expand_ratio = expand_ratio
54
+
55
+ self.use_short_conv = use_short_conv
56
+ self.conv_size = conv_size
57
+ self.conv_bias = conv_bias
58
+
59
+ self.key_dim = int(self.num_heads * self.expand_ratio)
60
+ self.value_dim = hidden_size
61
+ self.gate_low_rank_dim = gate_low_rank_dim
62
+ self.layer_idx = layer_idx
63
+
64
+ assert mode in ['chunk', 'fused_chunk'], f"Not supported mode `{mode}`."
65
+ assert self.key_dim % num_heads == 0, f"key dim must be divisible by num_heads of {num_heads}"
66
+ assert self.value_dim % num_heads == 0, f"value dim must be divisible by num_heads of {num_heads}"
67
+
68
+ self.head_f_dim = self.expand_ratio
69
+ self.head_i_dim = self.hidden_size // num_heads
70
+
71
+ self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
72
+ self.k_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
73
+ self.v_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
74
+
75
+ if use_short_conv:
76
+ self.conv_size = conv_size
77
+ self.q_conv1d = ShortConvolution(
78
+ hidden_size=self.key_dim,
79
+ kernel_size=conv_size,
80
+ bias=conv_bias,
81
+ activation=None,
82
+ )
83
+ self.k_conv1d = ShortConvolution(
84
+ hidden_size=self.key_dim,
85
+ kernel_size=conv_size,
86
+ bias=conv_bias,
87
+ activation=None,
88
+ )
89
+ self.v_conv1d = ShortConvolution(
90
+ hidden_size=self.value_dim,
91
+ kernel_size=conv_size,
92
+ bias=conv_bias,
93
+ activation=None,
94
+ )
95
+
96
+ self.g_proj = nn.Sequential(
97
+ nn.Linear(hidden_size, gate_low_rank_dim, bias=False),
98
+ nn.Linear(gate_low_rank_dim, hidden_size, bias=False),
99
+ )
100
+ self.g_norm = FusedRMSNormGated(
101
+ hidden_size=hidden_size,
102
+ elementwise_affine=elementwise_affine,
103
+ eps=norm_eps,
104
+ )
105
+ self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
106
+
107
+ def forward(
108
+ self,
109
+ hidden_states: torch.Tensor,
110
+ attention_mask: torch.Tensor | None = None,
111
+ past_key_values: Cache | None = None,
112
+ use_cache: bool | None = False,
113
+ output_attentions: bool | None = False,
114
+ **kwargs: Unpack[dict],
115
+ ) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
116
+ if attention_mask is not None:
117
+ assert len(attention_mask.shape) == 2, (
118
+ "Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
119
+ "for padding purposes (0 indicating padding). "
120
+ "Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
121
+ )
122
+
123
+ # launching the triton kernel for just one token will actually be slower
124
+ mode = 'fused_recurrent' if hidden_states.shape[1] <= 64 else self.mode
125
+
126
+ last_state = get_layer_cache(self, past_key_values)
127
+
128
+ cu_seqlens = kwargs.get('cu_seqlens')
129
+ if self.use_short_conv:
130
+ conv_state_q, conv_state_k, conv_state_v = None, None, None
131
+ if last_state is not None:
132
+ conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
133
+ conv_mask = attention_mask[:, -hidden_states.shape[1]:] if attention_mask is not None else None
134
+ q, conv_state_q = self.q_conv1d(
135
+ x=self.q_proj(hidden_states),
136
+ mask=conv_mask,
137
+ cache=conv_state_q,
138
+ output_final_state=use_cache,
139
+ cu_seqlens=cu_seqlens,
140
+ )
141
+ k, conv_state_k = self.k_conv1d(
142
+ x=self.k_proj(hidden_states),
143
+ mask=conv_mask,
144
+ cache=conv_state_k,
145
+ output_final_state=use_cache,
146
+ cu_seqlens=cu_seqlens,
147
+ )
148
+ v, conv_state_v = self.v_conv1d(
149
+ x=self.v_proj(hidden_states),
150
+ mask=conv_mask,
151
+ cache=conv_state_v,
152
+ output_final_state=use_cache,
153
+ cu_seqlens=cu_seqlens,
154
+ )
155
+ else:
156
+ q = self.q_proj(hidden_states)
157
+ k = self.k_proj(hidden_states)
158
+ v = self.v_proj(hidden_states)
159
+
160
+ # dealing with left-padding
161
+ if attention_mask is not None:
162
+ v = v.mul(attention_mask[:, -v.shape[-2]:, None])
163
+
164
+ q = F.silu(q)
165
+ q, k = map(lambda x: rearrange(x, '... (h d) -> ... h d', d=self.head_f_dim), (q, k))
166
+ v = rearrange(v, '... (h d) -> ... h d', d=self.head_i_dim)
167
+ # TODO: this 2 steps took huge amount of time, which should be optimized
168
+ last_z = last_state['ffn_state'] if last_state is not None and last_state.get('ffn_state') is not None else None
169
+ if last_z is not None:
170
+ # Decode path: continue logcumsumexp from cached state
171
+ z = torch.logaddexp(last_z, k.float())
172
+ k, g = torch.exp(k - z).to(k.dtype), (last_z - z).to(k.dtype)
173
+ else:
174
+ # Prefill path: mask padding positions to -inf so they don't affect logcumsumexp
175
+ if cu_seqlens is not None:
176
+ raise NotImplementedError("LightNet does not support variable-length sequences for now.")
177
+ k_float = k.float()
178
+ if attention_mask is not None:
179
+ pad_mask = attention_mask[:, -k.shape[1]:, None, None] # (B, T, 1, 1)
180
+ k_for_z = k_float.masked_fill(pad_mask == 0, float('-inf'))
181
+ else:
182
+ k_for_z = k_float
183
+ z = k_for_z.logcumsumexp(1)
184
+ k_new = torch.exp(k_float - z)
185
+ g_new = torch.cat((z[:, :1], z[:, :-1]), 1) - z
186
+ # NaN/inf arise at fully-masked positions (-inf - (-inf)), zero them out
187
+ k = torch.nan_to_num(k_new, nan=0.0, posinf=0.0).to(k.dtype)
188
+ g = torch.nan_to_num(g_new, nan=0.0, posinf=0.0, neginf=0.0).to(k.dtype)
189
+
190
+ recurrent_state = last_state['recurrent_state'] if last_state is not None else None
191
+ if mode == 'fused_recurrent':
192
+ o, recurrent_state = fused_recurrent_gla(
193
+ q=q,
194
+ k=k,
195
+ v=v,
196
+ gk=g,
197
+ initial_state=recurrent_state,
198
+ output_final_state=use_cache,
199
+ cu_seqlens=cu_seqlens,
200
+ )
201
+ elif mode == 'chunk':
202
+ o, recurrent_state = chunk_gla(
203
+ q=q,
204
+ k=k,
205
+ v=v,
206
+ g=g,
207
+ initial_state=recurrent_state,
208
+ output_final_state=use_cache,
209
+ cu_seqlens=cu_seqlens,
210
+ )
211
+ else:
212
+ raise NotImplementedError(f"Not supported mode `{mode}`.")
213
+
214
+ update_layer_cache(
215
+ self,
216
+ past_key_values,
217
+ recurrent_state=recurrent_state,
218
+ conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
219
+ ffn_state=z[:, -1:],
220
+ offset=q.shape[1],
221
+ )
222
+
223
+ o = rms_norm_swish_gate_linear(
224
+ rearrange(o, 'b t h d -> b t (h d)'),
225
+ self.g_proj(hidden_states),
226
+ self.g_norm.weight,
227
+ self.g_norm.bias,
228
+ self.o_proj.weight,
229
+ self.o_proj.bias,
230
+ )
231
+ return o, None, past_key_values
232
+
233
+ def state_size(self, **kwargs) -> int:
234
+ state_size = self.key_dim * self.head_i_dim
235
+ for module in self.children():
236
+ if isinstance(module, ShortConvolution):
237
+ state_size += module.state_size
238
+ return state_size
fla/layers/linear_attn.py ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ import torch
8
+ import torch.nn as nn
9
+ import torch.nn.functional as F
10
+ from einops import rearrange, repeat
11
+
12
+ from fla.layers.utils import get_layer_cache, update_layer_cache
13
+ from fla.modules import RMSNorm
14
+ from fla.modules.feature_map import DPFPFeatureMap, HadamardFeatureMap, HedgehogFeatureMap, T2RFeatureMap
15
+ from fla.ops.linear_attn import chunk_linear_attn, fused_chunk_linear_attn, fused_recurrent_linear_attn
16
+
17
+ if TYPE_CHECKING:
18
+ from fla.models.utils import Cache
19
+
20
+
21
+ class LinearAttention(nn.Module):
22
+
23
+ def __init__(
24
+ self,
25
+ mode: str = 'chunk',
26
+ hidden_size: str = 1024,
27
+ expand_k: float = 1.0,
28
+ expand_v: float = 1.0,
29
+ num_heads: int = 8,
30
+ num_kv_heads: int | None = None,
31
+ feature_map: str = 'elementwise_product',
32
+ tie_feature_map_qk: bool = False,
33
+ output_norm: str = 'rmsnorm',
34
+ norm_q: bool = False,
35
+ norm_k: bool = False,
36
+ do_feature_map_norm: bool = False,
37
+ elementwise_affine: bool = True,
38
+ norm_eps: float = 1e-5,
39
+ layer_idx: int | None = None,
40
+ **kwargs,
41
+ ):
42
+ super().__init__()
43
+
44
+ self.hidden_size = hidden_size
45
+ self.mode = mode
46
+ self.num_heads = num_heads
47
+ self.num_kv_heads = num_kv_heads if num_kv_heads is not None else num_heads
48
+ self.num_kv_groups = self.num_heads // self.num_kv_heads
49
+ self.key_dim = int(hidden_size * expand_k)
50
+ self.value_dim = int(hidden_size * expand_v)
51
+ self.key_dim_per_group = self.key_dim // self.num_kv_groups
52
+ self.value_dim_per_group = self.value_dim // self.num_kv_groups
53
+
54
+ assert mode in ['chunk', 'fused_chunk', 'fused_recurrent'], f"Not supported mode `{mode}`."
55
+ assert self.key_dim % num_heads == 0, f"key dim must be divisible by num_heads of {num_heads}"
56
+ assert self.value_dim % num_heads == 0, f"value dim must be divisible by num_heads of {num_heads}"
57
+
58
+ self.head_k_dim = self.key_dim // num_heads
59
+ self.head_v_dim = self.value_dim // num_heads
60
+ self.do_feature_map_norm = do_feature_map_norm
61
+ self.layer_idx = layer_idx
62
+
63
+ if feature_map == 'hedgehog':
64
+ if tie_feature_map_qk:
65
+ self.feature_map_q = self.feature_map_k = HedgehogFeatureMap(head_dim=self.head_k_dim)
66
+ else:
67
+ self.feature_map_q = HedgehogFeatureMap(head_dim=self.head_k_dim)
68
+ self.feature_map_k = HedgehogFeatureMap(head_dim=self.head_k_dim)
69
+
70
+ elif feature_map == 't2r':
71
+ if tie_feature_map_qk:
72
+ self.feature_map_q = self.feature_map_k = T2RFeatureMap(head_dim=self.head_k_dim)
73
+ else:
74
+ self.feature_map_q = T2RFeatureMap(head_dim=self.head_k_dim)
75
+ self.feature_map_k = T2RFeatureMap(head_dim=self.head_k_dim)
76
+
77
+ elif feature_map == 'elementwise_product':
78
+ if tie_feature_map_qk:
79
+ self.feature_map_q = self.feature_map_k = HadamardFeatureMap(head_dim=self.head_k_dim)
80
+ else:
81
+ self.feature_map_q = HadamardFeatureMap(head_dim=self.head_k_dim)
82
+ self.feature_map_k = HadamardFeatureMap(head_dim=self.head_k_dim)
83
+
84
+ elif feature_map == 'dpfp':
85
+ self.feature_map_q = DPFPFeatureMap(head_dim=self.head_k_dim)
86
+ self.feature_map_k = DPFPFeatureMap(head_dim=self.head_k_dim)
87
+
88
+ elif feature_map == 'elu':
89
+ def elu(x):
90
+ return F.elu(x) + 1
91
+ self.feature_map_q = elu
92
+ self.feature_map_k = elu
93
+
94
+ elif feature_map == 'relu':
95
+ self.feature_map_q = nn.ReLU()
96
+ self.feature_map_k = nn.ReLU()
97
+
98
+ elif feature_map == 'identity':
99
+ self.feature_map_q = nn.Identity()
100
+ self.feature_map_k = nn.Identity()
101
+ else:
102
+ raise NotImplementedError(f"Not supported feature map `{feature_map}`.")
103
+
104
+ self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
105
+ self.k_proj = nn.Linear(hidden_size, self.key_dim_per_group, bias=False)
106
+ self.v_proj = nn.Linear(hidden_size, self.value_dim_per_group, bias=False)
107
+
108
+ if output_norm == 'rmsnorm':
109
+ self.norm = RMSNorm(hidden_size=self.head_v_dim, elementwise_affine=elementwise_affine,
110
+ eps=norm_eps, dtype=torch.float32)
111
+ elif output_norm == 'identity':
112
+ self.norm = nn.Identity()
113
+ else:
114
+ raise NotImplementedError(f"Not supported output norm `{output_norm}`.")
115
+
116
+ self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
117
+
118
+ self.norm_q = norm_q
119
+ self.norm_k = norm_k
120
+
121
+ def forward(
122
+ self,
123
+ hidden_states: torch.Tensor,
124
+ attention_mask: torch.Tensor | None = None,
125
+ past_key_values: Cache | None = None,
126
+ use_cache: bool | None = False,
127
+ output_attentions: bool | None = False,
128
+ **kwargs,
129
+ ) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
130
+ # Match other recurrent layers: use the recurrent kernel for decode/small chunks.
131
+ mode = 'fused_recurrent' if hidden_states.shape[1] <= 64 else self.mode
132
+ last_state = get_layer_cache(self, past_key_values)
133
+
134
+ q = self.q_proj(hidden_states)
135
+ k = self.k_proj(hidden_states)
136
+ v = self.v_proj(hidden_states)
137
+
138
+ if attention_mask is not None:
139
+ v = v.mul(attention_mask[:, -v.shape[-2]:, None])
140
+
141
+ q = rearrange(q, '... (h d) -> ... h d', d=self.head_k_dim)
142
+ if self.num_kv_groups > 1:
143
+ k = repeat(k, '... (h d) -> ... (h g) d', d=self.head_k_dim, g=self.num_kv_groups)
144
+ v = repeat(v, '... (h d) -> ... (h g) d', d=self.head_v_dim, g=self.num_kv_groups)
145
+ else:
146
+ k = rearrange(k, '... (h d) -> ... h d', d=self.head_k_dim)
147
+ v = rearrange(v, '... (h d) -> ... h d', d=self.head_v_dim)
148
+
149
+ q = self.feature_map_q(q)
150
+ k = self.feature_map_k(k)
151
+
152
+ if self.norm_q:
153
+ q = q / (q.sum(-1, True) + 1e-4)
154
+ if self.norm_k:
155
+ k = k / (k.sum(-1, True) + 1e-4)
156
+
157
+ recurrent_state = last_state['recurrent_state'] if last_state is not None else None
158
+ if mode == 'chunk':
159
+ o, final_state = chunk_linear_attn(
160
+ q=q,
161
+ k=k,
162
+ v=v,
163
+ initial_state=recurrent_state,
164
+ output_final_state=use_cache,
165
+ normalize=self.do_feature_map_norm,
166
+ )
167
+ elif mode == 'fused_chunk':
168
+ o, final_state = fused_chunk_linear_attn(
169
+ q=q,
170
+ k=k,
171
+ v=v,
172
+ initial_state=recurrent_state,
173
+ output_final_state=use_cache,
174
+ normalize=self.do_feature_map_norm,
175
+ )
176
+ elif mode == 'fused_recurrent':
177
+ o, final_state = fused_recurrent_linear_attn(
178
+ q=q,
179
+ k=k,
180
+ v=v,
181
+ initial_state=recurrent_state,
182
+ output_final_state=use_cache,
183
+ normalize=self.do_feature_map_norm,
184
+ )
185
+ else:
186
+ raise NotImplementedError
187
+ update_layer_cache(
188
+ self,
189
+ past_key_values,
190
+ recurrent_state=final_state,
191
+ offset=q.shape[1],
192
+ )
193
+ o = self.norm(o)
194
+ o = rearrange(o, '... h d -> ... (h d)')
195
+ o = self.o_proj(o)
196
+ return o, None, past_key_values
fla/layers/log_linear_mamba2.py ADDED
@@ -0,0 +1,684 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import math
4
+ from typing import TYPE_CHECKING
5
+
6
+ import torch
7
+ import torch.nn as nn
8
+ from einops import rearrange
9
+ from transformers.activations import ACT2FN
10
+ from transformers.utils import logging
11
+
12
+ from fla.layers.mamba2 import apply_mask_to_padding_states, causal_conv1d_fn, causal_conv1d_update, is_fast_path_available
13
+ from fla.layers.utils import get_layer_cache, update_layer_cache
14
+ from fla.modules.layernorm_gated import RMSNormGated, rmsnorm_fn
15
+ from fla.ops.log_linear_attn.chunk import LogLinearAttentionState, chunk_log_linear_attn
16
+
17
+ if TYPE_CHECKING:
18
+ from fla.models.utils import Cache
19
+
20
+ logger = logging.get_logger(__name__)
21
+
22
+
23
+ def ceil_log(x: int, b: int) -> int:
24
+ return math.ceil(math.log(x, b))
25
+
26
+
27
+ def get_num_levels(length: int, base: int) -> int:
28
+ return ceil_log(length, base) + 1
29
+
30
+
31
+ MAX_SEQUENCE_LENGTH = 2048 * 8
32
+ LAMBDA_LEVEL_BASE = 2
33
+ MAX_NUM_LEVELS = get_num_levels(length=MAX_SEQUENCE_LENGTH, base=LAMBDA_LEVEL_BASE)
34
+
35
+
36
+ def hmamba_chunk_scan_combined(
37
+ x: torch.Tensor,
38
+ dt: torch.Tensor,
39
+ A: torch.Tensor,
40
+ B: torch.Tensor,
41
+ C: torch.Tensor,
42
+ dl: torch.Tensor,
43
+ L: torch.Tensor,
44
+ chunk_size: int,
45
+ D: torch.Tensor | None = None,
46
+ z: torch.Tensor | None = None,
47
+ dt_bias: torch.Tensor | None = None,
48
+ initial_states: LogLinearAttentionState | None = None,
49
+ seq_idx: torch.Tensor | None = None,
50
+ cu_seqlens: torch.Tensor | None = None,
51
+ dt_softplus: bool = False,
52
+ dt_limit: tuple[float, float] = (0.0, float("inf")),
53
+ return_final_states: bool = False,
54
+ ):
55
+ if z is not None:
56
+ raise NotImplementedError
57
+ if seq_idx is not None:
58
+ raise NotImplementedError
59
+ if cu_seqlens is not None:
60
+ raise NotImplementedError
61
+ if dt_softplus is not True:
62
+ raise NotImplementedError
63
+ if tuple(dt_limit) != (0.0, float("inf")):
64
+ raise NotImplementedError
65
+ if chunk_size != 64:
66
+ raise NotImplementedError
67
+ if not B.shape == C.shape:
68
+ raise ValueError("B and C must have the same shape")
69
+
70
+ if D is not None:
71
+ if D.dim() != 1:
72
+ raise ValueError
73
+ D = rearrange(D, "h -> 1 1 h 1")
74
+ D_residual = x * D
75
+
76
+ if dt_bias is not None:
77
+ dt = dt + rearrange(dt_bias, "h -> 1 1 h")
78
+ if dt_softplus:
79
+ dt = torch.nn.functional.softplus(dt)
80
+ if dt_limit != (0.0, float("inf")):
81
+ dt = torch.clamp(dt, min=dt_limit[0], max=dt_limit[1])
82
+ x = (x * rearrange(dt, "b l h -> b l h 1")).to(x.dtype)
83
+ A = rearrange(A, "h -> 1 1 h") * dt
84
+
85
+ L = torch.nn.functional.softplus(rearrange(L, "h ell -> 1 1 h ell") * dl).to(L.dtype)
86
+
87
+ y, state = chunk_log_linear_attn(
88
+ q=C,
89
+ k=B,
90
+ v=x,
91
+ g=A,
92
+ level_scales=L,
93
+ initial_state=initial_states,
94
+ output_final_state=return_final_states,
95
+ cu_seqlens=cu_seqlens,
96
+ )
97
+
98
+ if D is not None:
99
+ y = y + D_residual
100
+
101
+ return y, state
102
+
103
+
104
+ def hmamba_split_conv1d_scan_combined(
105
+ zxbcdtdl: torch.Tensor,
106
+ conv1d_weight: torch.Tensor,
107
+ conv1d_bias: torch.Tensor,
108
+ dt_bias: torch.Tensor,
109
+ A: torch.Tensor,
110
+ L: torch.Tensor,
111
+ D: torch.Tensor,
112
+ chunk_size: int,
113
+ initial_states: torch.Tensor | None = None,
114
+ seq_idx: torch.Tensor | None = None,
115
+ dt_limit: tuple[float, float] = (0.0, float("inf")),
116
+ return_final_states: bool = False,
117
+ activation: str = "silu",
118
+ rmsnorm_weight: torch.Tensor | None = None,
119
+ rmsnorm_eps: float = 1e-6,
120
+ outproj_weight: torch.Tensor | None = None,
121
+ outproj_bias: torch.Tensor | None = None,
122
+ headdim: int | None = None,
123
+ ngroups: int = 1,
124
+ norm_before_gate: bool = True,
125
+ conv1d_fn=None,
126
+ conv_backend: str = "cuda",
127
+ ) -> torch.Tensor:
128
+ """
129
+ Argument:
130
+ zxbcdtdl: (batch, seqlen, 2 * dim + 2 * ngroups * dstate + nheads) where dim == nheads * headdim
131
+ conv1d_weight: (dim + 2 * ngroups * dstate, width)
132
+ conv1d_bias: (dim + 2 * ngroups * dstate,)
133
+ dt_bias: (nheads,)
134
+ A: (nheads)
135
+ L: (nheads, nlevels)
136
+ D: (nheads, headdim) or (nheads,)
137
+ initial_states: (batch, nheads, headdim, dstate)
138
+ seq_idx: (batch, seqlen), int32
139
+ rmsnorm_weight: (dim,)
140
+ outproj_weight: (out_dim, dim)
141
+ outproj_bias: (out_dim,)
142
+ headdim: if D is 1D, headdim must be passed in
143
+ norm_before_gate: if True, we do RMSNorm(x) * F.silu(z). If False, we do RMSNorm(x * F.silu(z))
144
+ Return:
145
+ out: (batch, seqlen, dim)
146
+ """
147
+ if initial_states is not None:
148
+ raise NotImplementedError
149
+ if seq_idx is not None:
150
+ raise NotImplementedError
151
+ if dt_limit != (0.0, float("inf")):
152
+ raise NotImplementedError
153
+ if return_final_states is not False:
154
+ raise NotImplementedError
155
+ if norm_before_gate is not False:
156
+ raise NotImplementedError
157
+ if rmsnorm_weight is None:
158
+ raise NotImplementedError
159
+ if activation not in ["silu", "swish"]:
160
+ raise NotImplementedError
161
+
162
+ batch, seqlen, _ = zxbcdtdl.shape
163
+ dlambda = L.shape[-1]
164
+ (nheads,) = D.shape
165
+ dim = nheads * headdim
166
+ dstate = (zxbcdtdl.shape[-1] - 2 * dim - nheads - nheads * dlambda) // ngroups // 2
167
+
168
+ if D.dim() != 1:
169
+ raise ValueError
170
+ if headdim is None:
171
+ raise ValueError
172
+ if nheads % ngroups != 0:
173
+ raise ValueError
174
+ if zxbcdtdl.shape != (
175
+ batch,
176
+ seqlen,
177
+ 2 * dim + 2 * ngroups * dstate + nheads + nheads * dlambda,
178
+ ):
179
+ raise ValueError
180
+ if dt_bias.shape != (nheads,):
181
+ raise ValueError
182
+ if A.shape != (nheads,):
183
+ raise ValueError
184
+ if L.shape != (nheads, dlambda):
185
+ raise ValueError
186
+ if D.shape != (nheads,):
187
+ raise ValueError
188
+ if rmsnorm_weight is None:
189
+ raise ValueError
190
+
191
+ zxBCdtl_splits = [dim, dim + 2 * ngroups * dstate, nheads, nheads * dlambda]
192
+ xBC_splits = [dim, ngroups * dstate, ngroups * dstate]
193
+ z, xBC, dt, dl = torch.split(zxbcdtdl, zxBCdtl_splits, dim=-1)
194
+ _conv_fn = conv1d_fn if conv1d_fn is not None else causal_conv1d_fn
195
+ _conv_out = _conv_fn(
196
+ rearrange(xBC, "b s d -> b d s"),
197
+ conv1d_weight,
198
+ bias=conv1d_bias,
199
+ activation=activation,
200
+ seq_idx=seq_idx,
201
+ )
202
+ if conv_backend == 'triton':
203
+ _conv_out = _conv_out[0]
204
+ xBC = rearrange(_conv_out, "b d s -> b s d")
205
+ x, B, C = torch.split(xBC, xBC_splits, dim=-1)
206
+ x = rearrange(x, "b l (h p) -> b l h p", h=nheads, p=headdim)
207
+ B = rearrange(B, "b l (g n) -> b l g n", g=ngroups, n=dstate)
208
+ C = rearrange(C, "b l (g n) -> b l g n", g=ngroups, n=dstate)
209
+ dl = rearrange(dl, "b l (h ell) -> b l h ell", h=nheads, ell=dlambda)
210
+ y, _ = hmamba_chunk_scan_combined(
211
+ x=x,
212
+ dt=dt,
213
+ A=A,
214
+ B=B,
215
+ C=C,
216
+ dl=dl,
217
+ L=L,
218
+ chunk_size=chunk_size,
219
+ D=D,
220
+ z=z if rmsnorm_weight is None else None,
221
+ dt_bias=dt_bias,
222
+ dt_softplus=True,
223
+ seq_idx=seq_idx,
224
+ cu_seqlens=None,
225
+ dt_limit=dt_limit,
226
+ return_final_states=return_final_states,
227
+ )
228
+
229
+ y = rearrange(y, "b l h p -> b l (h p)")
230
+ if rmsnorm_weight is not None:
231
+ y = rmsnorm_fn(
232
+ x=y,
233
+ weight=rmsnorm_weight,
234
+ bias=None,
235
+ z=z,
236
+ eps=rmsnorm_eps,
237
+ group_size=None,
238
+ norm_before_gate=False,
239
+ )
240
+ out = torch.nn.functional.linear(y, outproj_weight, outproj_bias)
241
+ return out
242
+
243
+
244
+ class LogLinearMamba2(nn.Module):
245
+ """
246
+ Compute ∆, A, B, C, and D the state space parameters and compute the `contextualized_states`.
247
+ A, D are input independent (see Mamba paper [1] Section 3.5.2 "Interpretation of A" for why A isn't selective)
248
+ ∆, B, C are input-dependent (this is a key difference between Mamba and the linear time invariant S4,
249
+ and is why Mamba is called **selective** state spaces)
250
+ """
251
+
252
+ def __init__(
253
+ self,
254
+ num_heads: int,
255
+ head_dim: int = 64,
256
+ hidden_size: int = 2048,
257
+ state_size: int = 128,
258
+ expand: int = 2,
259
+ n_groups: int = 1,
260
+ conv_kernel: int = 4,
261
+ use_conv_bias: bool = False,
262
+ hidden_act: str = "silu",
263
+ rms_norm: bool = True,
264
+ chunk_size: int = 64,
265
+ time_step_rank: float = 256,
266
+ time_step_limit: tuple[float, float] = (0.0, float("inf")),
267
+ time_step_min: float = 0.001,
268
+ time_step_max: float = 0.1,
269
+ use_bias: bool = True,
270
+ norm_eps: float = 1e-5,
271
+ layer_idx: int = None,
272
+ backend: str = "cuda",
273
+ ):
274
+ super().__init__()
275
+ self.num_heads = num_heads
276
+ self.hidden_size = hidden_size
277
+ self.ssm_state_size = state_size
278
+ self.conv_kernel_size = conv_kernel
279
+ self.intermediate_size = int(expand * self.hidden_size)
280
+ self.time_step_rank = int(time_step_rank)
281
+ self.layer_idx = layer_idx
282
+ self.use_conv_bias = use_conv_bias
283
+ self.activation = hidden_act
284
+ self.act = ACT2FN[hidden_act]
285
+
286
+ self.layer_norm_epsilon = norm_eps
287
+ self.rms_norm = rms_norm
288
+
289
+ self.n_groups = n_groups
290
+ self.head_dim = head_dim
291
+ self.chunk_size = chunk_size
292
+
293
+ self.time_step_limit = time_step_limit
294
+ self.time_step_min = time_step_min
295
+ self.time_step_max = time_step_max
296
+
297
+ self.conv_dim = self.intermediate_size + 2 * self.n_groups * self.ssm_state_size
298
+ self.conv1d = nn.Conv1d(
299
+ in_channels=self.conv_dim,
300
+ out_channels=self.conv_dim,
301
+ bias=use_conv_bias,
302
+ kernel_size=conv_kernel,
303
+ groups=self.conv_dim,
304
+ padding=conv_kernel - 1,
305
+ )
306
+
307
+ self.num_lambda_dims = MAX_NUM_LEVELS
308
+ self.lambda_level_module = None
309
+
310
+ # projection of the input hidden states
311
+ projection_size = (
312
+ self.intermediate_size
313
+ + self.conv_dim
314
+ + self.num_heads * (self.num_lambda_dims + 1)
315
+ )
316
+ self.in_proj = nn.Linear(
317
+ self.hidden_size,
318
+ projection_size,
319
+ bias=use_bias,
320
+ )
321
+ # selective projection used to make dt, B and C input dependant
322
+
323
+ # time step projection (discretization)
324
+ # instantiate once and copy inv_dt in init_weights of PretrainedModel
325
+ self.dt_bias = nn.Parameter(torch.ones(self.num_heads))
326
+
327
+ # S4D real initialization. These are not discretized!
328
+ # The core is to load them, compute the discrete states, then write the updated state. Keeps the memory bounded
329
+ A = torch.arange(1, self.num_heads + 1)
330
+ self.A_log = nn.Parameter(torch.log(A))
331
+ self.A_log._no_weight_decay = True
332
+
333
+ self.lambda_mode = "positive"
334
+ L = torch.ones(self.num_heads, self.num_lambda_dims)
335
+ self.L = nn.Parameter(L)
336
+ self.L._no_weight_decay = True
337
+
338
+ self.norm = RMSNormGated(
339
+ self.intermediate_size, eps=self.layer_norm_epsilon, norm_before_gate=False,
340
+ )
341
+ self.D = nn.Parameter(torch.ones(self.num_heads))
342
+ self.D._no_weight_decay = True
343
+
344
+ self.out_proj = nn.Linear(
345
+ self.intermediate_size, self.hidden_size, bias=use_bias,
346
+ )
347
+ self.use_bias = use_bias
348
+
349
+ if not is_fast_path_available:
350
+ logger.warning_once(
351
+ "The fast path is not available because one of "
352
+ "`(selective_state_update, causal_conv1d_fn, causal_conv1d_update)` is None. "
353
+ "Falling back to the naive implementation. "
354
+ "To install follow https://github.com/state-spaces/mamba/#installation and"
355
+ "https://github.com/Dao-AILab/causal-conv1d",
356
+ )
357
+ import os
358
+ backend = os.environ.get('FLA_CONV_BACKEND', backend)
359
+ assert backend in ['cuda', 'triton'], f"Unsupported backend: {backend}"
360
+ if backend == 'cuda' and causal_conv1d_fn is None:
361
+ logger.warning_once(
362
+ "The CUDA backend is not available because `causal_conv1d` is None. "
363
+ "Falling back to the Triton backend. "
364
+ "To install follow https://github.com/Dao-AILab/causal-conv1d",
365
+ )
366
+ backend = 'triton'
367
+ if backend == 'triton':
368
+ from fla.modules.convolution import causal_conv1d as causal_conv1d_triton
369
+ from fla.modules.convolution import causal_conv1d_update as causal_conv1d_update_triton
370
+ self.causal_conv1d_fn = causal_conv1d_triton
371
+ self.causal_conv1d_update = causal_conv1d_update_triton
372
+ logger.warning(
373
+ "LogLinearMamba2 does not recommend using Triton's conv1d backend, "
374
+ "as it is untested and may contain bugs.",
375
+ )
376
+ else:
377
+ self.causal_conv1d_fn = causal_conv1d_fn
378
+ self.causal_conv1d_update = causal_conv1d_update
379
+ self.backend = backend
380
+
381
+ def cuda_kernels_forward(
382
+ self,
383
+ hidden_states: torch.Tensor,
384
+ last_state: dict | None = None,
385
+ use_cache: bool = False,
386
+ attention_mask: torch.Tensor | None = None,
387
+ ):
388
+ if self.activation not in ["silu", "swish"]:
389
+ raise ValueError
390
+
391
+ # 1. Gated MLP's linear projection
392
+ # Only apply padding mask during prefill (last_state is None).
393
+ # During decode, attention_mask has shape (B, accumulated_len) which
394
+ # mismatches hidden_states (B, 1, D).
395
+ hidden_states = apply_mask_to_padding_states(
396
+ hidden_states=hidden_states,
397
+ attention_mask=attention_mask if last_state is None else None,
398
+ )
399
+ projected_states = self.in_proj(hidden_states)
400
+
401
+ # Set up dimensions for reshapes later
402
+ batch_size, seq_len, _ = hidden_states.shape
403
+ groups_time_state_size = self.n_groups * self.ssm_state_size
404
+ d_mlp = (
405
+ projected_states.shape[-1]
406
+ - 2 * self.intermediate_size
407
+ - 2 * self.n_groups * self.ssm_state_size
408
+ - self.num_heads * (self.num_lambda_dims + 1)
409
+ ) // 2
410
+ if d_mlp != 0:
411
+ raise ValueError
412
+
413
+ # Single step calculations via cache
414
+ if last_state is not None:
415
+ if hidden_states.shape[1] != 1:
416
+ raise ValueError("LogLinearMamba2 cached decoding only supports a single new token per step.")
417
+
418
+ gate, xBC, dt, dl = torch.split(
419
+ projected_states.squeeze(1),
420
+ [
421
+ self.intermediate_size,
422
+ self.conv_dim,
423
+ self.num_heads,
424
+ self.num_heads * self.num_lambda_dims,
425
+ ],
426
+ dim=-1,
427
+ )
428
+
429
+ # 2. Convolution sequence transformation
430
+ conv_state = last_state['conv_state']
431
+ xBC = self.causal_conv1d_update(
432
+ xBC,
433
+ conv_state,
434
+ rearrange(self.conv1d.weight, "d 1 w -> d w"),
435
+ self.conv1d.bias,
436
+ self.activation,
437
+ )
438
+
439
+ x, B, C = torch.split(
440
+ xBC,
441
+ [
442
+ self.intermediate_size,
443
+ groups_time_state_size,
444
+ groups_time_state_size,
445
+ ],
446
+ dim=-1,
447
+ )
448
+
449
+ # 3. SSM transformation
450
+ A = -torch.exp(self.A_log.float()) # (nheads,)
451
+ B = rearrange(
452
+ B,
453
+ "b (g n) -> b g n",
454
+ b=batch_size,
455
+ g=self.n_groups,
456
+ n=self.ssm_state_size,
457
+ )
458
+ C = rearrange(
459
+ C,
460
+ "b (g n) -> b g n",
461
+ b=batch_size,
462
+ g=self.n_groups,
463
+ n=self.ssm_state_size,
464
+ )
465
+ x_reshaped = rearrange(
466
+ x,
467
+ "b (h p) -> b h p",
468
+ b=batch_size,
469
+ h=self.num_heads,
470
+ p=self.head_dim,
471
+ )
472
+ dl_reshaped = rearrange(
473
+ dl,
474
+ "b (h ell) -> b h ell",
475
+ b=batch_size,
476
+ h=self.num_heads,
477
+ ell=self.num_lambda_dims,
478
+ )
479
+ y, hssm_state = hmamba_chunk_scan_combined(
480
+ x_reshaped,
481
+ dt=dt,
482
+ A=A,
483
+ B=B,
484
+ C=C,
485
+ dl=dl_reshaped,
486
+ L=self.L,
487
+ D=self.D,
488
+ z=None,
489
+ dt_bias=self.dt_bias,
490
+ dt_softplus=True,
491
+ initial_states=last_state['recurrent_state'],
492
+ return_final_states=True,
493
+ )
494
+ y = rearrange(
495
+ y,
496
+ "b h p -> b (h p)",
497
+ b=batch_size,
498
+ h=self.num_heads,
499
+ p=self.head_dim,
500
+ )
501
+ y = self.norm(y, gate)
502
+
503
+ # 4. Final linear projection
504
+ out = self.out_proj(y)[:, None, ...]
505
+ return out, conv_state, hssm_state
506
+
507
+ # Fused calculations or step by step if no initialized cache is found
508
+ else:
509
+ A = -torch.exp(
510
+ self.A_log.float(),
511
+ ) # (num_heads) or (intermediate_size, state_size)
512
+ dt_limit_kwargs = (
513
+ {}
514
+ if self.time_step_limit == (0.0, float("inf"))
515
+ else {"dt_limit": self.time_step_limit}
516
+ )
517
+
518
+ # 2-4. Fused kernel for conv1d, SSM, and the final projection
519
+ if self.training and not use_cache:
520
+ out = torch.utils.checkpoint.checkpoint(
521
+ hmamba_split_conv1d_scan_combined,
522
+ use_reentrant=False,
523
+ # function arguments
524
+ zxbcdtdl=projected_states,
525
+ conv1d_weight=rearrange(self.conv1d.weight, "d 1 w -> d w"),
526
+ conv1d_bias=self.conv1d.bias,
527
+ dt_bias=self.dt_bias,
528
+ A=A,
529
+ L=self.L,
530
+ D=self.D,
531
+ chunk_size=self.chunk_size,
532
+ conv1d_fn=self.causal_conv1d_fn,
533
+ conv_backend=self.backend,
534
+ seq_idx=None, # was seq_idx
535
+ activation=self.activation,
536
+ rmsnorm_weight=self.norm.weight,
537
+ rmsnorm_eps=self.norm.eps,
538
+ outproj_weight=self.out_proj.weight,
539
+ outproj_bias=self.out_proj.bias,
540
+ headdim=self.head_dim,
541
+ ngroups=self.n_groups,
542
+ norm_before_gate=False,
543
+ return_final_states=False,
544
+ **dt_limit_kwargs,
545
+ )
546
+ return out, None, None
547
+
548
+ else:
549
+ gate, xBC, dt, dl = torch.split(
550
+ projected_states,
551
+ [
552
+ self.intermediate_size,
553
+ self.conv_dim,
554
+ self.num_heads,
555
+ self.num_heads * self.num_lambda_dims,
556
+ ],
557
+ dim=-1,
558
+ )
559
+
560
+ # 2. Convolution sequence transformation
561
+ # Init cache
562
+ masked_xBC = apply_mask_to_padding_states(xBC, attention_mask)
563
+ new_conv_state = None
564
+ if use_cache:
565
+ xBC_t = rearrange(masked_xBC, "b l d -> b d l")
566
+ new_conv_state = torch.nn.functional.pad(
567
+ xBC_t,
568
+ (self.conv_kernel_size - xBC_t.shape[-1], 0),
569
+ )
570
+
571
+ _conv1d_output = self.causal_conv1d_fn(
572
+ x=xBC.transpose(1, 2),
573
+ weight=rearrange(self.conv1d.weight, "d 1 w -> d w"),
574
+ bias=self.conv1d.bias,
575
+ activation=self.activation,
576
+ )
577
+ if self.backend == 'cuda':
578
+ xBC = _conv1d_output.transpose(1, 2)
579
+ elif self.backend == 'triton':
580
+ xBC = _conv1d_output[0].transpose(1, 2).contiguous()
581
+ else:
582
+ raise ValueError(f"Unsupported backend: {self.backend}")
583
+
584
+ xBC = apply_mask_to_padding_states(
585
+ hidden_states=xBC,
586
+ attention_mask=attention_mask,
587
+ )
588
+
589
+ x, B, C = torch.split(
590
+ xBC,
591
+ [
592
+ self.intermediate_size,
593
+ groups_time_state_size,
594
+ groups_time_state_size,
595
+ ],
596
+ dim=-1,
597
+ )
598
+
599
+ # 3. SSM transformation
600
+ y, hssm_state = hmamba_chunk_scan_combined(
601
+ rearrange(
602
+ x,
603
+ "b l (h p) -> b l h p",
604
+ b=batch_size,
605
+ l=seq_len,
606
+ p=self.head_dim,
607
+ ),
608
+ dt=dt,
609
+ A=A,
610
+ B=rearrange(
611
+ B,
612
+ "b l (g n) -> b l g n",
613
+ b=batch_size,
614
+ l=seq_len,
615
+ g=self.n_groups,
616
+ ),
617
+ C=rearrange(
618
+ C,
619
+ "b l (g n) -> b l g n",
620
+ b=batch_size,
621
+ l=seq_len,
622
+ g=self.n_groups,
623
+ ),
624
+ dl=rearrange(
625
+ dl,
626
+ "b l (h ell) -> b l h ell",
627
+ b=batch_size,
628
+ h=self.num_heads,
629
+ ell=self.num_lambda_dims,
630
+ ),
631
+ L=self.L,
632
+ chunk_size=self.chunk_size,
633
+ D=self.D,
634
+ z=None,
635
+ seq_idx=None,
636
+ return_final_states=True,
637
+ dt_bias=self.dt_bias,
638
+ dt_softplus=True,
639
+ **dt_limit_kwargs,
640
+ )
641
+
642
+ y = rearrange(
643
+ y,
644
+ "b l h p -> b l (h p)",
645
+ b=batch_size,
646
+ l=seq_len,
647
+ h=self.num_heads,
648
+ p=self.head_dim,
649
+ )
650
+ # Multiply "gate" branch and apply extra normalization layer
651
+ y = self.norm(y, gate)
652
+
653
+ # 4. Final linear projection
654
+ out = self.out_proj(y)
655
+
656
+ return out, new_conv_state, hssm_state
657
+
658
+ def forward(
659
+ self,
660
+ hidden_states: torch.Tensor,
661
+ attention_mask: torch.Tensor | None = None,
662
+ past_key_values: Cache | None = None,
663
+ use_cache: bool | None = False,
664
+ output_attentions: bool | None = False,
665
+ **kwargs,
666
+ ) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
667
+ last_state = get_layer_cache(self, past_key_values)
668
+
669
+ if "cuda" in self.in_proj.weight.device.type:
670
+ output, conv_state, hssm_state = self.cuda_kernels_forward(
671
+ hidden_states, last_state, use_cache, attention_mask
672
+ )
673
+ else:
674
+ raise NotImplementedError
675
+
676
+ update_layer_cache(
677
+ self,
678
+ past_key_values,
679
+ recurrent_state=hssm_state,
680
+ conv_state=conv_state,
681
+ offset=hidden_states.shape[1],
682
+ )
683
+
684
+ return output, None, past_key_values
fla/layers/mamba.py ADDED
@@ -0,0 +1,395 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+
3
+ from __future__ import annotations
4
+
5
+ import warnings
6
+ from typing import TYPE_CHECKING
7
+
8
+ import torch
9
+ import torch.nn as nn
10
+ from transformers.utils import logging
11
+
12
+ from fla.layers.utils import get_layer_cache, update_layer_cache
13
+ from fla.modules.activations import ACT2FN
14
+
15
+ with warnings.catch_warnings():
16
+ warnings.simplefilter('ignore')
17
+ try:
18
+ from mamba_ssm.ops.selective_scan_interface import mamba_inner_fn, selective_scan_fn
19
+ from mamba_ssm.ops.triton.selective_state_update import selective_state_update
20
+ except ImportError:
21
+ selective_state_update, selective_scan_fn, mamba_inner_fn = None, None, None
22
+
23
+ try:
24
+ from causal_conv1d import causal_conv1d_fn, causal_conv1d_update
25
+ except ImportError:
26
+ causal_conv1d_update, causal_conv1d_fn = None, None
27
+ is_fast_path_available = all((
28
+ selective_state_update,
29
+ selective_scan_fn,
30
+ mamba_inner_fn,
31
+ ))
32
+ if TYPE_CHECKING:
33
+ from transformers.processing_utils import Unpack
34
+
35
+ from fla.models.utils import Cache
36
+
37
+ logger = logging.get_logger(__name__)
38
+
39
+
40
+ class Mamba(nn.Module):
41
+ """
42
+ Compute ∆, A, B, C, and D the state space parameters and compute the `contextualized_states`.
43
+ A, D are input independent (see Mamba paper [1] Section 3.5.2 "Interpretation of A" for why A isn't selective)
44
+ ∆, B, C are input-dependent (this is a key difference between Mamba and the linear time invariant S4,
45
+ and is why Mamba is called **selective** state spaces)
46
+ """
47
+
48
+ def __init__(
49
+ self,
50
+ hidden_size: int = 2048,
51
+ state_size: int = 16,
52
+ conv_kernel: int = 4,
53
+ use_conv_bias: bool = True,
54
+ intermediate_size: int = 2048,
55
+ time_step_rank: int = 256,
56
+ use_bias: bool = True,
57
+ hidden_act: str = "silu",
58
+ layer_idx: int = None,
59
+ backend: str = "cuda",
60
+ ):
61
+ super().__init__()
62
+
63
+ self.hidden_size = hidden_size
64
+ self.ssm_state_size = state_size
65
+ self.conv_kernel_size = conv_kernel
66
+ self.use_conv_bias = use_conv_bias
67
+ self.intermediate_size = intermediate_size
68
+ self.time_step_rank = time_step_rank
69
+ self.use_bias = use_bias
70
+
71
+ self.conv1d = nn.Conv1d(
72
+ in_channels=self.intermediate_size,
73
+ out_channels=self.intermediate_size,
74
+ bias=use_conv_bias,
75
+ kernel_size=conv_kernel,
76
+ groups=self.intermediate_size,
77
+ padding=conv_kernel - 1,
78
+ )
79
+
80
+ self.activation = hidden_act
81
+ self.act = ACT2FN[hidden_act]
82
+
83
+ self.layer_idx = layer_idx
84
+
85
+ # projection of the input hidden states
86
+ self.in_proj = nn.Linear(self.hidden_size, self.intermediate_size * 2, bias=use_bias)
87
+ # selective projection used to make dt, B and C input dependant
88
+ self.x_proj = nn.Linear(self.intermediate_size, self.time_step_rank + self.ssm_state_size * 2, bias=False)
89
+ # time step projection (discretization)
90
+ self.dt_proj = nn.Linear(self.time_step_rank, self.intermediate_size, bias=True)
91
+
92
+ # S4D real initialization. These are not discretized!
93
+ # The core is to load them, compute the discrete states, then write the updated state. Keeps the memory bounded
94
+ A = torch.arange(1, self.ssm_state_size + 1, dtype=torch.float32)[None, :]
95
+ A = A.expand(self.intermediate_size, -1).contiguous()
96
+
97
+ self.A_log = nn.Parameter(torch.log(A))
98
+ self.D = nn.Parameter(torch.ones(self.intermediate_size))
99
+ self.out_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=use_bias)
100
+
101
+ if not is_fast_path_available:
102
+ logger.warning_once(
103
+ "The fast path is not available because on of "
104
+ "`(selective_state_update, selective_scan_fn, causal_conv1d_fn, causal_conv1d_update, mamba_inner_fn)`"
105
+ " is None. Falling back to the naive implementation. "
106
+ "To install follow https://github.com/state-spaces/mamba/#installation and"
107
+ " https://github.com/Dao-AILab/causal-conv1d",
108
+ )
109
+ import os
110
+ backend = os.environ.get('FLA_CONV_BACKEND', backend)
111
+ assert backend in ['cuda', 'triton'], f"Unsupported backend: {backend}"
112
+ if backend == 'cuda' and causal_conv1d_fn is None:
113
+ logger.warning_once(
114
+ "The CUDA backend is not available because `causal_conv1d` is None. "
115
+ "Falling back to the Triton backend. "
116
+ "To install follow https://github.com/Dao-AILab/causal-conv1d",
117
+ )
118
+ backend = 'triton'
119
+ if backend == 'triton':
120
+ from fla.modules.convolution import causal_conv1d as causal_conv1d_triton
121
+ from fla.modules.convolution import causal_conv1d_update as causal_conv1d_update_triton
122
+ self.causal_conv1d_fn = causal_conv1d_triton
123
+ self.causal_conv1d_update = causal_conv1d_update_triton
124
+ else:
125
+ self.causal_conv1d_fn = causal_conv1d_fn
126
+ self.causal_conv1d_update = causal_conv1d_update
127
+ self.backend = backend
128
+
129
+ def _to_causal_conv_layout(self, hidden_states: torch.Tensor) -> torch.Tensor:
130
+ return hidden_states.transpose(1, 2).contiguous()
131
+
132
+ def _from_causal_conv_layout(self, hidden_states: torch.Tensor) -> torch.Tensor:
133
+ return hidden_states.transpose(1, 2).contiguous()
134
+
135
+ def _build_conv_state(self, hidden_states: torch.Tensor) -> torch.Tensor:
136
+ seq_len = hidden_states.shape[-1]
137
+ if seq_len >= self.conv_kernel_size:
138
+ return hidden_states[..., -self.conv_kernel_size:].contiguous()
139
+ return nn.functional.pad(hidden_states, (self.conv_kernel_size - seq_len, 0)).contiguous()
140
+
141
+ def cuda_kernels_forward(
142
+ self,
143
+ hidden_states: torch.Tensor,
144
+ last_state: dict | None = None,
145
+ use_cache: bool | None = False,
146
+ attention_mask: torch.LongTensor | None = None,
147
+ **kwargs: Unpack[dict],
148
+ ):
149
+ if last_state is not None and hidden_states.shape[1] != 1:
150
+ raise ValueError("Mamba cached decoding only supports a single new token per step.")
151
+
152
+ # 1. Gated MLP's linear projection
153
+ projected_states = self.in_proj(hidden_states).transpose(1, 2)
154
+
155
+ if self.training and not use_cache:
156
+ contextualized_states = mamba_inner_fn(
157
+ projected_states,
158
+ self.conv1d.weight,
159
+ self.conv1d.bias if self.use_conv_bias else None,
160
+ self.x_proj.weight,
161
+ self.dt_proj.weight,
162
+ self.out_proj.weight,
163
+ self.out_proj.bias.float() if self.use_bias else None,
164
+ -torch.exp(self.A_log.float()),
165
+ None, # input-dependent B
166
+ None, # input-dependent C
167
+ self.D.float(),
168
+ delta_bias=self.dt_proj.bias.float(),
169
+ delta_softplus=True,
170
+ )
171
+ return contextualized_states, None, None
172
+
173
+ hidden_states, gate = projected_states.chunk(2, dim=1)
174
+
175
+ if attention_mask is not None and last_state is None:
176
+ # Mask before the depthwise conv so cached/prefill conv inputs do not keep pad tokens.
177
+ hidden_states = hidden_states * attention_mask.unsqueeze(1)
178
+
179
+ # 2. Convolution sequence transformation
180
+ conv_inputs = hidden_states
181
+ conv_weights = self.conv1d.weight.view(self.conv1d.weight.size(0), self.conv1d.weight.size(2))
182
+ if last_state is not None:
183
+ conv_state = last_state['conv_state']
184
+ ssm_state = last_state['recurrent_state']
185
+
186
+ if self.backend == 'triton':
187
+ hidden_states, conv_state = self.causal_conv1d_update(
188
+ x=self._to_causal_conv_layout(conv_inputs),
189
+ cache=conv_state,
190
+ weight=conv_weights,
191
+ bias=self.conv1d.bias,
192
+ activation=self.activation,
193
+ )
194
+ hidden_states = self._from_causal_conv_layout(hidden_states)
195
+ else:
196
+ hidden_states = self.causal_conv1d_update(
197
+ conv_inputs.squeeze(-1),
198
+ conv_state,
199
+ conv_weights,
200
+ self.conv1d.bias,
201
+ self.activation,
202
+ )
203
+ hidden_states = hidden_states.unsqueeze(-1)
204
+ else:
205
+ conv_state = None
206
+ ssm_state = None
207
+ if self.backend == 'triton':
208
+ hidden_states, conv_state = self.causal_conv1d_fn(
209
+ x=self._to_causal_conv_layout(conv_inputs),
210
+ weight=conv_weights,
211
+ bias=self.conv1d.bias,
212
+ activation=self.activation,
213
+ output_final_state=bool(use_cache),
214
+ )
215
+ hidden_states = self._from_causal_conv_layout(hidden_states)
216
+ else:
217
+ if use_cache:
218
+ conv_state = self._build_conv_state(conv_inputs)
219
+ hidden_states = self.causal_conv1d_fn(
220
+ conv_inputs, conv_weights, self.conv1d.bias, activation=self.activation,
221
+ )
222
+
223
+ if attention_mask is not None and last_state is None:
224
+ # Re-mask after the conv: causal kernels can regenerate non-zero values at masked positions,
225
+ # and those values would otherwise leak into x_proj and the SSM recurrence.
226
+ hidden_states = hidden_states * attention_mask.unsqueeze(1)
227
+
228
+ # 3. State Space Model sequence transformation
229
+ # 3.a. input varying initialization of time_step, B and C
230
+ ssm_parameters = self.x_proj(hidden_states.transpose(1, 2))
231
+ time_step, B, C = torch.split(
232
+ ssm_parameters, [self.time_step_rank, self.ssm_state_size, self.ssm_state_size], dim=-1,
233
+ )
234
+ discrete_time_step = self.dt_proj.weight @ time_step.transpose(1, 2)
235
+
236
+ A = -torch.exp(self.A_log.float())
237
+ # 3.c perform the recurrence y ← SSM(A, B, C)(x)
238
+ time_proj_bias = self.dt_proj.bias.float() if hasattr(self.dt_proj, "bias") else None
239
+ if last_state is not None:
240
+ scan_outputs = selective_state_update(
241
+ ssm_state,
242
+ hidden_states[..., 0],
243
+ discrete_time_step[..., 0],
244
+ A,
245
+ B[:, 0],
246
+ C[:, 0],
247
+ self.D,
248
+ gate[..., 0],
249
+ time_proj_bias,
250
+ dt_softplus=True,
251
+ ).unsqueeze(-1)
252
+ else:
253
+ scan_outputs, ssm_state = selective_scan_fn(
254
+ hidden_states,
255
+ discrete_time_step,
256
+ A,
257
+ B.transpose(1, 2),
258
+ C.transpose(1, 2),
259
+ self.D.float(),
260
+ gate,
261
+ time_proj_bias,
262
+ delta_softplus=True,
263
+ return_last_state=True,
264
+ )
265
+
266
+ # 4. Final linear projection
267
+ contextualized_states = self.out_proj(scan_outputs.transpose(1, 2))
268
+ return contextualized_states, conv_state, ssm_state
269
+
270
+ def slow_forward(
271
+ self,
272
+ input_states,
273
+ last_state: dict | None = None,
274
+ use_cache: bool | None = False,
275
+ attention_mask: torch.LongTensor | None = None,
276
+ **kwargs: Unpack[dict],
277
+ ):
278
+ if last_state is not None and input_states.shape[1] != 1:
279
+ raise ValueError("Mamba cached decoding only supports a single new token per step.")
280
+
281
+ batch_size, seq_len, _ = input_states.shape
282
+ dtype = input_states.dtype
283
+ # 1. Gated MLP's linear projection
284
+ # [batch, 2 * intermediate_size, seq_len]
285
+ projected_states = self.in_proj(input_states).transpose(1, 2)
286
+ hidden_states, gate = projected_states.chunk(2, dim=1)
287
+
288
+ if attention_mask is not None and last_state is None:
289
+ # Mask before the depthwise conv so cached/prefill conv inputs do not keep pad tokens.
290
+ hidden_states = hidden_states * attention_mask.unsqueeze(1)
291
+
292
+ # 2. Convolution sequence transformation
293
+ if last_state is not None:
294
+ conv_state = last_state['conv_state']
295
+ ssm_state = last_state['recurrent_state'].clone().to(hidden_states.device)
296
+
297
+ # decode path: single token
298
+ conv_state = conv_state.roll(shifts=-1, dims=-1)
299
+ conv_state[:, :, -1] = hidden_states[:, :, 0].to(conv_state.device)
300
+ hidden_states = torch.sum(conv_state * self.conv1d.weight[:, 0, :], dim=-1)
301
+ if self.use_conv_bias:
302
+ hidden_states += self.conv1d.bias
303
+ # [batch, intermediate_size, 1] : decoding
304
+ hidden_states = self.act(hidden_states).to(dtype).unsqueeze(-1)
305
+ elif use_cache:
306
+ ssm_state = torch.zeros(
307
+ (batch_size, self.intermediate_size, self.ssm_state_size),
308
+ device=hidden_states.device, dtype=dtype,
309
+ )
310
+ conv_state = self._build_conv_state(hidden_states)
311
+ # [batch, intermediate_size, seq_len]
312
+ hidden_states = self.act(self.conv1d(hidden_states)[..., :seq_len])
313
+ else:
314
+ ssm_state = torch.zeros(
315
+ (batch_size, self.intermediate_size, self.ssm_state_size),
316
+ device=hidden_states.device, dtype=dtype,
317
+ )
318
+ conv_state = None
319
+ # [batch, intermediate_size, seq_len]
320
+ hidden_states = self.act(self.conv1d(hidden_states)[..., :seq_len])
321
+
322
+ if attention_mask is not None and last_state is None:
323
+ # Re-mask after the conv: causal kernels can regenerate non-zero values at masked positions,
324
+ # and those values would otherwise leak into x_proj and the SSM recurrence.
325
+ hidden_states = hidden_states * attention_mask.unsqueeze(1)
326
+
327
+ # 3. State Space Model sequence transformation
328
+ # 3.a. Selection: [batch, seq_len, self.time_step_rank + self.ssm_state_size * 2]
329
+ ssm_parameters = self.x_proj(hidden_states.transpose(1, 2))
330
+ time_step, B, C = torch.split(
331
+ ssm_parameters, [self.time_step_rank, self.ssm_state_size, self.ssm_state_size], dim=-1,
332
+ )
333
+ # [batch, seq_len, intermediate_size]
334
+ discrete_time_step = self.dt_proj(time_step)
335
+ # [batch, intermediate_size, seq_len]
336
+ discrete_time_step = nn.functional.softplus(discrete_time_step).transpose(1, 2)
337
+
338
+ # 3.b. Discretization: B and C to [batch, seq_len, intermediate_size, ssm_state_size] (SRAM)
339
+ # [intermediate_size, ssm_state_size]
340
+ A = -torch.exp(self.A_log.float())
341
+ # [batch, intermediate_size, seq_len, ssm_state_size]
342
+ discrete_A = torch.exp(A[None, :, None, :] * discrete_time_step[:, :, :, None])
343
+ # [batch, intermediate_size, seq_len, ssm_state_size]
344
+ discrete_B = discrete_time_step[:, :, :, None] * B[:, None, :, :].float()
345
+ deltaB_u = discrete_B * hidden_states[:, :, :, None].float()
346
+
347
+ # 3.c perform the recurrence y ← SSM(A, B, C)(x)
348
+ scan_outputs = []
349
+ for i in range(hidden_states.shape[-1]):
350
+ # [batch, intermediade_size, ssm_state]
351
+ ssm_state = discrete_A[:, :, i, :] * ssm_state + deltaB_u[:, :, i, :]
352
+ # [batch, intermediade_size, 1]
353
+ scan_output = torch.matmul(ssm_state.to(dtype), C[:, i, :].unsqueeze(-1))
354
+ scan_outputs.append(scan_output[:, :, 0])
355
+ # [batch, seq_len, intermediade_size]
356
+ scan_output = torch.stack(scan_outputs, dim=-1)
357
+ scan_output = scan_output + (hidden_states * self.D[None, :, None])
358
+ scan_output = (scan_output * self.act(gate))
359
+
360
+ # 4. Final linear projection
361
+ # [batch, seq_len, hidden_size]
362
+ contextualized_states = self.out_proj(scan_output.transpose(1, 2))
363
+ return contextualized_states, conv_state, ssm_state
364
+ # fmt: on
365
+
366
+ def forward(
367
+ self,
368
+ hidden_states: torch.Tensor,
369
+ attention_mask: torch.LongTensor | None = None,
370
+ past_key_values: Cache | None = None,
371
+ use_cache: bool | None = False,
372
+ output_attentions: bool | None = False,
373
+ **kwargs: Unpack[dict],
374
+ ) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
375
+ last_state = get_layer_cache(self, past_key_values)
376
+
377
+ if is_fast_path_available and "cuda" in self.x_proj.weight.device.type:
378
+ output, conv_state, ssm_state = self.cuda_kernels_forward(
379
+ hidden_states, last_state, use_cache, attention_mask, **kwargs
380
+ )
381
+ else:
382
+ output, conv_state, ssm_state = self.slow_forward(
383
+ hidden_states, last_state, use_cache, attention_mask, **kwargs
384
+ )
385
+
386
+ if use_cache and past_key_values is not None:
387
+ update_layer_cache(
388
+ self,
389
+ past_key_values,
390
+ recurrent_state=ssm_state,
391
+ conv_state=conv_state,
392
+ offset=hidden_states.shape[1],
393
+ )
394
+
395
+ return output, None, past_key_values
fla/layers/mamba2.py ADDED
@@ -0,0 +1,649 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+
3
+ from __future__ import annotations
4
+
5
+ import math
6
+ import warnings
7
+ from typing import TYPE_CHECKING
8
+
9
+ import torch
10
+ import torch.nn as nn
11
+ from transformers.utils import logging
12
+
13
+ from fla.layers.utils import get_layer_cache, update_layer_cache
14
+ from fla.modules.activations import ACT2FN
15
+ from fla.modules.layernorm_gated import RMSNormGated
16
+
17
+ with warnings.catch_warnings():
18
+ warnings.simplefilter('ignore')
19
+ try:
20
+ from mamba_ssm.ops.triton.selective_state_update import selective_state_update
21
+ from mamba_ssm.ops.triton.ssd_combined import mamba_chunk_scan_combined, mamba_split_conv1d_scan_combined
22
+ except ImportError:
23
+ selective_state_update, mamba_chunk_scan_combined, mamba_split_conv1d_scan_combined = None, None, None
24
+ try:
25
+ from causal_conv1d import causal_conv1d_fn, causal_conv1d_update
26
+ except ImportError:
27
+ causal_conv1d_update, causal_conv1d_fn = None, None
28
+ is_fast_path_available = selective_state_update is not None
29
+
30
+ if TYPE_CHECKING:
31
+ from fla.models.utils import Cache
32
+
33
+ logger = logging.get_logger(__name__)
34
+
35
+
36
+ def apply_mask_to_padding_states(hidden_states, attention_mask):
37
+ """
38
+ Tunes out the hidden states for padding tokens, see https://github.com/state-spaces/mamba/issues/66
39
+ """
40
+ if attention_mask is not None and attention_mask.shape[1] > 1 and attention_mask.shape[0] > 1:
41
+ dtype = hidden_states.dtype
42
+ hidden_states = (hidden_states * attention_mask[:, :, None]).to(dtype)
43
+
44
+ return hidden_states
45
+
46
+
47
+ def pad_tensor_by_size(input_tensor: torch.Tensor, pad_size: int):
48
+ """
49
+ Padding x tensor with `pad_size` on the seq_len dim (dim=1)
50
+
51
+ Assumes that we only have tensors of either size 4 or 3
52
+ """
53
+ pad_shape = (0, 0, 0, 0, 0, pad_size, 0, 0) if len(input_tensor.shape) == 4 else (0, 0, 0, pad_size, 0, 0)
54
+
55
+ return torch.nn.functional.pad(input_tensor, pad_shape, mode="constant", value=0)
56
+
57
+
58
+ def reshape_into_chunks(input_tensor, pad_size, chunk_size):
59
+ """
60
+ Padding input_tensor with `pad_size` on the seq_len dim (dim=1) and
61
+ simultaneously splitting it into chunk sequences.
62
+
63
+ Assumes that we only have tensors of either size 4 or 3
64
+ """
65
+ # [bsz, seq_len, ...] -> [bsz, seq_len multiple of chunk_size, ...]
66
+ input_tensor = pad_tensor_by_size(input_tensor, pad_size)
67
+
68
+ if len(input_tensor.shape) == 3:
69
+ # [bsz, seq_len multiple of chunk_size, num_heads] -> [bsz, -1, chunk_size, num_heads]
70
+ return input_tensor.reshape(input_tensor.shape[0], -1, chunk_size, input_tensor.shape[2])
71
+ else:
72
+ # [bsz, seq_len multiple of chunk_size, num_heads, head_dim or state_size] ->
73
+ # [bsz, -1, chunk_size, num_heads, head_dim or state_size]
74
+ return input_tensor.reshape(
75
+ input_tensor.shape[0], -1, chunk_size, input_tensor.shape[2], input_tensor.shape[3],
76
+ )
77
+
78
+
79
+ def segment_sum(input_tensor):
80
+ """
81
+ More stable segment sum calculation. Uses cumulative sums and masking instead of direct subtractions.
82
+ """
83
+ chunk_size = input_tensor.size(-1)
84
+ # 1. expand input tensor to have an additional dimension and repeat along that dimension
85
+ # [..., chunk_size] -> [..., chunk_size, chunk_size]
86
+ input_tensor = input_tensor[..., None].expand(*input_tensor.size(), chunk_size)
87
+ # 2. create a lower triangular mask with the diagonal set to 0 to 0 out elements above diag
88
+ mask = torch.tril(torch.ones(chunk_size, chunk_size, device=input_tensor.device, dtype=torch.bool), diagonal=-1)
89
+ input_tensor = input_tensor.masked_fill(~mask, 0)
90
+ # 3. compute actual cumsum
91
+ tensor_segsum = torch.cumsum(input_tensor, dim=-2)
92
+
93
+ # 4. apply mask to keep only the lower triangular part of the cumulative sum result (incl diagonal this time)
94
+ mask = torch.tril(torch.ones(chunk_size, chunk_size, device=input_tensor.device, dtype=torch.bool), diagonal=0)
95
+ tensor_segsum = tensor_segsum.masked_fill(~mask, -torch.inf)
96
+ return tensor_segsum
97
+
98
+
99
+ class Mamba2(nn.Module):
100
+ """
101
+ Compute ∆, A, B, C, and D the state space parameters and compute the `contextualized_states`.
102
+ A, D are input independent (see Mamba paper [1] Section 3.5.2 "Interpretation of A" for why A isn't selective)
103
+ ∆, B, C are input-dependent (this is a key difference between Mamba and the linear time invariant S4,
104
+ and is why Mamba is called **selective** state spaces)
105
+ """
106
+
107
+ def __init__(
108
+ self,
109
+ num_heads: int,
110
+ head_dim: int = 64,
111
+ hidden_size: int = 2048,
112
+ state_size: int = 128,
113
+ expand: int = 2,
114
+ n_groups: int = 1,
115
+ conv_kernel: int = 4,
116
+ use_conv_bias: bool = False,
117
+ hidden_act: str = "silu",
118
+ rms_norm: bool = True,
119
+ chunk_size: int = 256,
120
+ time_step_rank: float = 256,
121
+ time_step_limit: tuple[float, float] = (0.0, float("inf")),
122
+ time_step_min: float = 0.001,
123
+ time_step_max: float = 0.1,
124
+ use_bias: bool = True,
125
+ norm_eps: float = 1e-5,
126
+ layer_idx: int = None,
127
+ backend: str = "cuda",
128
+ ) -> Mamba2:
129
+ super().__init__()
130
+
131
+ self.num_heads = num_heads
132
+ self.head_dim = head_dim
133
+ self.hidden_size = hidden_size
134
+ self.ssm_state_size = state_size
135
+ self.expand = expand
136
+ self.intermediate_size = int(expand * hidden_size)
137
+ self.n_groups = n_groups
138
+
139
+ self.conv_kernel_size = conv_kernel
140
+ self.use_conv_bias = use_conv_bias
141
+ self.activation = hidden_act
142
+ self.act = ACT2FN[hidden_act]
143
+
144
+ self.rms_norm = rms_norm
145
+ self.norm_eps = norm_eps
146
+
147
+ self.chunk_size = chunk_size
148
+
149
+ self.time_step_rank = int(time_step_rank)
150
+ self.time_step_limit = time_step_limit
151
+ self.time_step_min = time_step_min
152
+ self.time_step_max = time_step_max
153
+
154
+ self.conv_dim = self.intermediate_size + 2 * self.n_groups * self.ssm_state_size
155
+ self.conv1d = nn.Conv1d(
156
+ in_channels=self.conv_dim,
157
+ out_channels=self.conv_dim,
158
+ bias=use_conv_bias,
159
+ kernel_size=conv_kernel,
160
+ groups=self.conv_dim,
161
+ padding=conv_kernel - 1,
162
+ )
163
+
164
+ # projection of the input hidden states
165
+ projection_size = self.intermediate_size + self.conv_dim + self.num_heads
166
+ self.in_proj = nn.Linear(
167
+ self.hidden_size,
168
+ projection_size,
169
+ bias=use_bias,
170
+ )
171
+ # selective projection used to make dt, B and C input dependant
172
+
173
+ # time step projection (discretization)
174
+ # instantiate once and copy inv_dt in init_weights of PretrainedModel
175
+ # hard coded for now
176
+ dt_init_floor = 1e-4
177
+ dt = torch.exp(
178
+ torch.rand(self.num_heads) * (
179
+ math.log(self.time_step_max) - math.log(self.time_step_min)
180
+ ) + math.log(self.time_step_min)
181
+ )
182
+ dt = torch.clamp(dt, min=dt_init_floor)
183
+ # Inverse of softplus: https://github.com/pytorch/pytorch/issues/72759
184
+ inv_dt = dt + torch.log(-torch.expm1(-dt))
185
+ self.dt_bias = nn.Parameter(inv_dt)
186
+
187
+ # S4D real initialization. These are not discretized!
188
+ # The core is to load them, compute the discrete states, then write the updated state. Keeps the memory bounded
189
+ A = torch.empty(self.num_heads, dtype=torch.float32).uniform_(0, 16)
190
+ self.A_log = nn.Parameter(torch.log(A))
191
+ self.A_log._no_weight_decay = True
192
+ self.norm = RMSNormGated(
193
+ self.intermediate_size, eps=self.norm_eps, norm_before_gate=False,
194
+ )
195
+ self.D = nn.Parameter(torch.ones(self.num_heads))
196
+ self.D._no_weight_decay = True
197
+
198
+ self.out_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=use_bias)
199
+ self.use_bias = use_bias
200
+
201
+ self.layer_idx = layer_idx
202
+
203
+ if not is_fast_path_available:
204
+ logger.warning_once(
205
+ "The fast path is not available because one of "
206
+ "`(selective_state_update)` is None. "
207
+ "Falling back to the naive implementation. "
208
+ "To install follow https://github.com/state-spaces/mamba/#installation",
209
+ )
210
+ import os
211
+ backend = os.environ.get('FLA_CONV_BACKEND', backend)
212
+ assert backend in ['cuda', 'triton'], f"Unsupported backend: {backend}"
213
+ if backend == 'cuda' and causal_conv1d_fn is None:
214
+ logger.warning_once(
215
+ "The CUDA backend is not available because `causal_conv1d` is None. "
216
+ "Falling back to the Triton backend. "
217
+ "To install follow https://github.com/Dao-AILab/causal-conv1d",
218
+ )
219
+ backend = 'triton'
220
+ if backend == 'triton':
221
+ from fla.modules.convolution import causal_conv1d as causal_conv1d_triton
222
+ from fla.modules.convolution import causal_conv1d_update as causal_conv1d_update_triton
223
+ self.causal_conv1d_fn = causal_conv1d_triton
224
+ self.causal_conv1d_update = causal_conv1d_update_triton
225
+ logger.warning(
226
+ "Mamba2 does not recommend using Triton's conv1d backend, "
227
+ "as it is untested and may contain bugs.",
228
+ )
229
+ else:
230
+ self.causal_conv1d_fn = causal_conv1d_fn
231
+ self.causal_conv1d_update = causal_conv1d_update
232
+ self.backend = backend
233
+
234
+ def cuda_kernels_forward(
235
+ self,
236
+ hidden_states: torch.Tensor,
237
+ last_state: dict | None = None,
238
+ use_cache: bool = False,
239
+ attention_mask: torch.Tensor | None = None,
240
+ ):
241
+ # 1. Gated MLP's linear projection
242
+ projected_states = self.in_proj(hidden_states)
243
+
244
+ # Set up dimensions for reshapes later
245
+ batch_size, seq_len, _ = hidden_states.shape
246
+ groups_time_state_size = self.n_groups * self.ssm_state_size
247
+ d_mlp = (
248
+ projected_states.shape[-1]
249
+ - 2 * self.intermediate_size
250
+ - 2 * self.n_groups * self.ssm_state_size
251
+ - self.num_heads
252
+ ) // 2
253
+
254
+ # Single step calculations via cache (decode)
255
+ if last_state is not None:
256
+ if hidden_states.shape[1] != 1:
257
+ raise ValueError("Mamba2 cached decoding only supports a single new token per step.")
258
+ conv_state = last_state['conv_state']
259
+ ssm_state = last_state['recurrent_state']
260
+
261
+ _, _, gate, hidden_states_B_C, dt = projected_states.squeeze(1).split(
262
+ [d_mlp, d_mlp, self.intermediate_size, self.conv_dim, self.num_heads], dim=-1,
263
+ )
264
+
265
+ # 2. Convolution sequence transformation
266
+ hidden_states_B_C = self.causal_conv1d_update(
267
+ hidden_states_B_C.contiguous(),
268
+ conv_state,
269
+ self.conv1d.weight.squeeze(1),
270
+ self.conv1d.bias,
271
+ self.activation,
272
+ )
273
+
274
+ hidden_states, B, C = torch.split(
275
+ hidden_states_B_C,
276
+ [
277
+ self.intermediate_size,
278
+ groups_time_state_size,
279
+ groups_time_state_size,
280
+ ],
281
+ dim=-1,
282
+ )
283
+
284
+ # 3. SSM transformation
285
+ A = -torch.exp(self.A_log.float()) # (nheads,)
286
+ A = A[:, None, ...][:, :, None].expand(-1, self.head_dim, self.ssm_state_size).to(dtype=torch.float32)
287
+ dt = dt[:, :, None].expand(-1, -1, self.head_dim)
288
+ dt_bias = self.dt_bias[:, None, ...].expand(-1, self.head_dim)
289
+ D = self.D[:, None, ...].expand(-1, self.head_dim)
290
+ B = B.view(batch_size, self.n_groups, B.shape[1] // self.n_groups)
291
+ C = C.view(batch_size, self.n_groups, C.shape[1] // self.n_groups)
292
+ hidden_states_reshaped = hidden_states.view(batch_size, self.num_heads, self.head_dim)
293
+
294
+ hidden_states = selective_state_update(
295
+ ssm_state,
296
+ hidden_states_reshaped,
297
+ dt,
298
+ A,
299
+ B,
300
+ C,
301
+ D,
302
+ z=None,
303
+ dt_bias=dt_bias,
304
+ dt_softplus=True,
305
+ )
306
+ hidden_states = hidden_states.view(batch_size, self.num_heads * self.head_dim)
307
+ hidden_states = self.norm(hidden_states, gate)
308
+
309
+ # 4. Final linear projection
310
+ out = self.out_proj(hidden_states)[:, None, ...]
311
+
312
+ # conv_state is updated in-place by causal_conv1d_update
313
+ # ssm_state is updated in-place by selective_state_update
314
+ return out, conv_state, ssm_state
315
+
316
+ # Fused calculations or step by step if no initialized cache is found (prefill)
317
+ else:
318
+ A = -torch.exp(self.A_log.float()) # (num_heads) or (intermediate_size, state_size)
319
+ dt_limit_kwargs = {} if self.time_step_limit == (0.0, float("inf")) else {"dt_limit": self.time_step_limit}
320
+
321
+ # 2-4. Fused kernel for conv1d, SSM, and the final projection
322
+ if self.training and not use_cache:
323
+ out = mamba_split_conv1d_scan_combined(
324
+ projected_states,
325
+ self.conv1d.weight.squeeze(1),
326
+ self.conv1d.bias,
327
+ self.dt_bias,
328
+ A,
329
+ D=self.D,
330
+ chunk_size=self.chunk_size,
331
+ seq_idx=None, # was seq_idx
332
+ activation=self.activation,
333
+ rmsnorm_weight=self.norm.weight,
334
+ rmsnorm_eps=self.norm.eps,
335
+ outproj_weight=self.out_proj.weight,
336
+ outproj_bias=self.out_proj.bias,
337
+ headdim=self.head_dim,
338
+ ngroups=self.n_groups,
339
+ norm_before_gate=False,
340
+ return_final_states=False,
341
+ **dt_limit_kwargs,
342
+ )
343
+ return out, None, None
344
+
345
+ else:
346
+ _, _, gate, hidden_states_B_C, dt = projected_states.split(
347
+ [d_mlp, d_mlp, self.intermediate_size, self.conv_dim, self.num_heads], dim=-1,
348
+ )
349
+
350
+ # 2. Convolution sequence transformation
351
+ hidden_states_B_C = apply_mask_to_padding_states(hidden_states_B_C, attention_mask)
352
+ # Compute conv_state for cache
353
+ new_conv_state = None
354
+ if use_cache:
355
+ hidden_states_B_C_transposed = hidden_states_B_C.transpose(1, 2)
356
+ new_conv_state = nn.functional.pad(
357
+ hidden_states_B_C_transposed,
358
+ (self.conv_kernel_size - hidden_states_B_C_transposed.shape[-1], 0),
359
+ )
360
+
361
+ if self.activation not in ["silu", "swish"]:
362
+ hidden_states_B_C = self.act(
363
+ self.conv1d(hidden_states_B_C.transpose(1, 2))[..., :seq_len].transpose(1, 2),
364
+ )
365
+ else:
366
+ _conv1d_output = self.causal_conv1d_fn(
367
+ x=hidden_states_B_C.transpose(1, 2).contiguous(),
368
+ weight=self.conv1d.weight.squeeze(1),
369
+ bias=self.conv1d.bias,
370
+ activation=self.activation,
371
+ )
372
+ if self.backend == 'cuda':
373
+ hidden_states_B_C = _conv1d_output
374
+ hidden_states_B_C = hidden_states_B_C.transpose(1, 2)
375
+ elif self.backend == 'triton':
376
+ hidden_states_B_C, _ = _conv1d_output
377
+ hidden_states_B_C = hidden_states_B_C.transpose(1, 2).contiguous()
378
+ else:
379
+ raise ValueError(f"Unsupported backend: {self.backend}")
380
+
381
+ hidden_states_B_C = (hidden_states_B_C * attention_mask[:, :, None]).to(hidden_states_B_C.dtype) \
382
+ if attention_mask is not None and attention_mask.shape[1] > 1 and attention_mask.shape[0] > 1 \
383
+ else hidden_states_B_C
384
+ hidden_states, B, C = torch.split(
385
+ hidden_states_B_C,
386
+ [self.intermediate_size, groups_time_state_size, groups_time_state_size],
387
+ dim=-1,
388
+ )
389
+
390
+ # 3. SSM transformation
391
+ scan_output, ssm_state = mamba_chunk_scan_combined(
392
+ hidden_states.view(batch_size, seq_len, -1, self.head_dim),
393
+ dt,
394
+ A,
395
+ B.view(batch_size, seq_len, self.n_groups, -1),
396
+ C.view(batch_size, seq_len, self.n_groups, -1),
397
+ chunk_size=self.chunk_size,
398
+ D=self.D,
399
+ z=None,
400
+ seq_idx=None,
401
+ return_final_states=True,
402
+ dt_bias=self.dt_bias,
403
+ dt_softplus=True,
404
+ **dt_limit_kwargs,
405
+ )
406
+
407
+ scan_output = scan_output.view(batch_size, seq_len, -1)
408
+ # Multiply "gate" branch and apply extra normalization layer
409
+ scan_output = self.norm(scan_output, gate)
410
+
411
+ # 4. Final linear projection
412
+ out = self.out_proj(scan_output)
413
+
414
+ return out, new_conv_state, ssm_state
415
+
416
+ # fmt: off
417
+ def torch_forward(
418
+ self,
419
+ input_states,
420
+ last_state: dict | None = None,
421
+ use_cache: bool = False,
422
+ attention_mask: torch.Tensor | None = None,
423
+ ):
424
+ batch_size, seq_len, _ = input_states.shape
425
+ dtype = input_states.dtype
426
+
427
+ # 1. Gated MLP's linear projection
428
+ projected_states = self.in_proj(input_states)
429
+ d_mlp = (projected_states.shape[-1] - 2 * self.intermediate_size -
430
+ 2 * self.n_groups * self.ssm_state_size - self.num_heads) // 2
431
+ _, _, gate, hidden_states_B_C, dt = projected_states.split(
432
+ [d_mlp, d_mlp, self.intermediate_size, self.conv_dim, self.num_heads], dim=-1,
433
+ )
434
+
435
+ # 2. Convolution sequence transformation
436
+ if last_state is not None:
437
+ if input_states.shape[1] != 1:
438
+ raise ValueError("Mamba2 cached decoding only supports a single new token per step.")
439
+ # Decode path: single-step update
440
+ conv_state = last_state['conv_state']
441
+ ssm_state = last_state['recurrent_state']
442
+
443
+ conv_state = conv_state.roll(shifts=-1, dims=-1)
444
+ conv_state[:, :, -1] = hidden_states_B_C[:, 0, :].to(conv_state.device)
445
+
446
+ # We need to guarantee that anything regarding the cache is on the same device
447
+ conv_states_for_compute = conv_state.to(device=self.conv1d.weight.device)
448
+
449
+ hidden_states_B_C = torch.sum(
450
+ conv_states_for_compute * self.conv1d.weight.squeeze(1), dim=-1,
451
+ )
452
+ if self.use_conv_bias:
453
+ hidden_states_B_C = hidden_states_B_C + self.conv1d.bias
454
+ hidden_states_B_C = self.act(hidden_states_B_C)
455
+ else:
456
+ # Prefill path
457
+ hidden_states_B_C = apply_mask_to_padding_states(hidden_states_B_C, attention_mask)
458
+ new_conv_state = None
459
+ if use_cache:
460
+ hidden_states_B_C_transposed = hidden_states_B_C.transpose(1, 2)
461
+ new_conv_state = nn.functional.pad(
462
+ hidden_states_B_C_transposed, (self.conv_kernel_size - hidden_states_B_C_transposed.shape[-1], 0),
463
+ )
464
+
465
+ hidden_states_B_C = self.act(self.conv1d(hidden_states_B_C.transpose(1, 2))[..., :seq_len].transpose(1, 2))
466
+
467
+ if last_state is None:
468
+ hidden_states_B_C = (hidden_states_B_C * attention_mask[:, :, None]).to(hidden_states_B_C.dtype) \
469
+ if attention_mask is not None and attention_mask.shape[1] > 1 and attention_mask.shape[0] > 1 \
470
+ else hidden_states_B_C
471
+ hidden_states, B, C = torch.split(
472
+ hidden_states_B_C,
473
+ [self.intermediate_size, self.n_groups * self.ssm_state_size, self.n_groups * self.ssm_state_size],
474
+ dim=-1,
475
+ )
476
+
477
+ # 3. SSM transformation
478
+ A = -torch.exp(self.A_log.float()) # [num_heads]
479
+ if last_state is not None:
480
+ # Decode path
481
+ cache_device = ssm_state.device
482
+
483
+ # Note: there is no need to pad parameter matrices here, as there is just one new token
484
+ # for batched generation
485
+ dt = dt[:, 0, :][:, None, ...]
486
+ dt = dt.transpose(1, 2).expand(batch_size, dt.shape[-1], self.head_dim)
487
+ # [num_heads] -> [num_heads, head_dim]
488
+ dt_bias = self.dt_bias[..., None].expand(self.dt_bias.shape[0], self.head_dim)
489
+
490
+ dt = torch.nn.functional.softplus(dt + dt_bias.to(dt.dtype))
491
+ dt = torch.clamp(dt, self.time_step_limit[0], self.time_step_limit[1])
492
+ A = A[..., None, None].expand(self.num_heads, self.head_dim, self.ssm_state_size).to(dtype=torch.float32)
493
+ # [bsz, num_heads, head_dim, state_size]
494
+ dA = (torch.exp(dt[..., None] * A)).to(device=cache_device)
495
+
496
+ # Discretize B
497
+ # [bsz, n_groups * state_size] -> [bsz, n_groups, 1, state_size] ->
498
+ # -> [bsz, n_groups, group to head repetition factor, state_size] -> [bsz, num_heads, state_size]
499
+ B = B.reshape(batch_size, self.n_groups, -1)[..., None, :]
500
+ B = B.expand(batch_size, self.n_groups, self.num_heads // self.n_groups, B.shape[-1]).contiguous()
501
+ B = B.reshape(batch_size, -1, B.shape[-1])
502
+ # [bsz, num_heads, head_dim, state_size]
503
+ dB = dt[..., None] * B[..., None, :]
504
+
505
+ # Discretize x into dB
506
+ # [bsz, intermediate_size] -> [bsz, num_heads, head_dim]
507
+ hidden_states = hidden_states.reshape(batch_size, -1, self.head_dim)
508
+ dBx = (dB * hidden_states[..., None]).to(device=cache_device)
509
+
510
+ # State calculation
511
+ ssm_state = ssm_state * dA + dBx
512
+
513
+ # Subsequent output
514
+ # [bsz, n_groups * state_size] -> [bsz, num_heads, state_size]
515
+ C = C.reshape(batch_size, self.n_groups, -1)[..., None, :]
516
+ C = C.expand(batch_size, self.n_groups, self.num_heads // self.n_groups, C.shape[-1]).contiguous()
517
+ C = C.reshape(batch_size, -1, C.shape[-1])
518
+ # [bsz, num_heads, head_dim]
519
+
520
+ ssm_states_for_compute = ssm_state.to(device=C.device, dtype=C.dtype) # Shape: [b, h, d, n]
521
+ # Reshape ssm_states to merge the first two dimensions
522
+ # Shape: [b*h, d, n]
523
+ ssm_states_reshaped = ssm_states_for_compute.view(batch_size * self.num_heads, self.head_dim, self.ssm_state_size)
524
+ C_reshaped = C.view(batch_size * self.num_heads, self.ssm_state_size, 1) # Shape: [b*h, n, 1]
525
+ y = torch.bmm(ssm_states_reshaped, C_reshaped)
526
+ y = y.view(batch_size, self.num_heads, self.head_dim)
527
+
528
+ # D skip connection
529
+ # [num_heads] -> [num_heads, head_dim]
530
+ D = self.D[..., None].expand(self.D.shape[0], self.head_dim)
531
+ y = (y + hidden_states * D).to(y.dtype)
532
+
533
+ # [bsz, num_heads, head_dim] -> [bsz, 1, intermediate_size]
534
+ y = y.reshape(batch_size, -1)[:, None, ...]
535
+
536
+ scan_output = self.norm(y, gate)
537
+ contextualized_states = self.out_proj(scan_output.to(dtype))
538
+ return contextualized_states, conv_state, ssm_state
539
+ else:
540
+ # Prefill path
541
+ # begin ssd naive implementation without einsums
542
+ dt = nn.functional.softplus(dt + self.dt_bias)
543
+ dt = torch.clamp(dt, self.time_step_limit[0], self.time_step_limit[1])
544
+ hidden_states = hidden_states.reshape(batch_size, seq_len, -1, self.head_dim).float()
545
+ B = B.reshape(batch_size, seq_len, -1, self.ssm_state_size).float()
546
+ C = C.reshape(batch_size, seq_len, -1, self.ssm_state_size).float()
547
+ B = B.repeat(1, 1, self.num_heads // self.n_groups, 1)
548
+ C = C.repeat(1, 1, self.num_heads // self.n_groups, 1)
549
+ pad_size = (self.chunk_size - seq_len % self.chunk_size) % self.chunk_size
550
+
551
+ D_residual = self.D[..., None] * pad_tensor_by_size(hidden_states, pad_size)
552
+
553
+ # Discretize x and A
554
+ hidden_states = hidden_states * dt[..., None]
555
+ A = A.to(hidden_states.dtype) * dt
556
+
557
+ # Rearrange into blocks/chunks
558
+ hidden_states, A, B, C = [reshape_into_chunks(t, pad_size, self.chunk_size) for t in (hidden_states, A, B, C)]
559
+
560
+ # [bsz, -1, chunk_size, num_heads] -> [bsz, num_heads, -1, chunk_size]
561
+ A = A.permute(0, 3, 1, 2)
562
+ A_cumsum = torch.cumsum(A, dim=-1)
563
+
564
+ # 1. Compute the output for each intra-chunk (diagonal blocks)
565
+ # This is the analog of a causal mask
566
+ L = torch.exp(segment_sum(A))
567
+
568
+ # Contraction of C and B to get G (attention-weights like)
569
+ # shape: (b, c, l, s, h, n)
570
+ G_intermediate = C[:, :, :, None, :, :] * B[:, :, None, :, :, :]
571
+ G = G_intermediate.sum(dim=-1) # shape: (b, c, l, s, h)
572
+
573
+ # Compute M, equivalent to applying attention mask to weights
574
+ M_intermediate = G[..., None] * L.permute(0, 2, 3, 4, 1)[..., None]
575
+ M = M_intermediate.sum(dim=-1)
576
+
577
+ # Compute Y_diag (apply to values)
578
+ Y_diag = (M[..., None] * hidden_states[:, :, None]).sum(dim=3)
579
+
580
+ # 2. Compute the state for each intra-chunk
581
+ # (right term of low-rank factorization of off-diagonal blocks; B terms)
582
+ decay_states = torch.exp(A_cumsum[:, :, :, -1:] - A_cumsum)
583
+ B_decay = B * decay_states.permute(0, -2, -1, 1)[..., None]
584
+ states = (B_decay[..., None, :] * hidden_states[..., None]).sum(dim=2)
585
+
586
+ # 3. Compute the inter-chunk SSM recurrence; produces correct SSM states at chunk boundaries
587
+ # (middle term of factorization of off-diag blocks; A terms)
588
+ previous_states = torch.zeros_like(states[:, :1])
589
+ states = torch.cat([previous_states, states], dim=1)
590
+ decay_chunk = torch.exp(segment_sum(nn.functional.pad(A_cumsum[:, :, :, -1], (1, 0))))
591
+ decay_chunk = decay_chunk.transpose(1, 3)
592
+ new_states = (decay_chunk[..., None, None] * states[:, :, None, ...]).sum(dim=1)
593
+ states, ssm_state = new_states[:, :-1], new_states[:, -1]
594
+
595
+ # 4. Compute state -> output conversion per chunk
596
+ # (left term of low-rank factorization of off-diagonal blocks; C terms)
597
+ state_decay_out = torch.exp(A_cumsum)
598
+ C_times_states = (C[..., None, :] * states[:, :, None, ...])
599
+ state_decay_out_permuted = state_decay_out.permute(0, 2, 3, 1)
600
+ Y_off = (C_times_states.sum(-1) * state_decay_out_permuted[..., None])
601
+
602
+ # Add output of intra-chunk and inter-chunk terms (diagonal and off-diagonal blocks)
603
+ y = Y_diag + Y_off
604
+ # [bsz, -1, self.chunk_size, num_heads, head_dim] -> [bsz, (padded) seq_len, num_heads, head_dim]
605
+ y = y.reshape(batch_size, -1, self.num_heads, self.head_dim)
606
+
607
+ y = y + D_residual
608
+ # Cutting off padded chunks
609
+ if pad_size > 0:
610
+ y = y[:, :seq_len, :, :]
611
+ y = y.reshape(batch_size, seq_len, -1)
612
+
613
+ scan_output = self.norm(y, gate)
614
+
615
+ # end ssd naive
616
+
617
+ # 4. Final linear projection
618
+ contextualized_states = self.out_proj(scan_output.to(dtype)) # [batch, seq_len, hidden_size]
619
+ return contextualized_states, new_conv_state if use_cache else None, ssm_state
620
+ # fmt: on
621
+
622
+ def forward(
623
+ self,
624
+ hidden_states: torch.Tensor,
625
+ attention_mask: torch.Tensor | None = None,
626
+ past_key_values: Cache | None = None,
627
+ use_cache: bool | None = False,
628
+ output_attentions: bool | None = False,
629
+ **kwargs,
630
+ ) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
631
+ last_state = get_layer_cache(self, past_key_values)
632
+
633
+ if is_fast_path_available and "cuda" in self.in_proj.weight.device.type:
634
+ output, conv_state, ssm_state = self.cuda_kernels_forward(hidden_states, last_state, use_cache, attention_mask)
635
+ else:
636
+ dtype = hidden_states.dtype
637
+ if last_state is None and attention_mask is not None and attention_mask.shape[1] > 1 and attention_mask.shape[0] > 1:
638
+ hidden_states = (hidden_states * attention_mask[:, :, None]).to(dtype)
639
+ output, conv_state, ssm_state = self.torch_forward(hidden_states, last_state, use_cache, attention_mask)
640
+
641
+ update_layer_cache(
642
+ self,
643
+ past_key_values,
644
+ recurrent_state=ssm_state,
645
+ conv_state=conv_state,
646
+ offset=hidden_states.shape[1],
647
+ )
648
+
649
+ return output, None, past_key_values
fla/layers/mesa_net.py ADDED
@@ -0,0 +1,221 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ import torch
8
+ import torch.nn as nn
9
+ from einops import rearrange
10
+ from torch.nn import functional as F
11
+
12
+ from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, update_layer_cache
13
+ from fla.modules import FusedRMSNormGated, RMSNorm, ShortConvolution
14
+ from fla.modules.l2norm import l2_norm
15
+ from fla.ops.mesa_net import chunk_mesa_net, mesa_net_decoding_one_step
16
+
17
+ if TYPE_CHECKING:
18
+ from transformers.processing_utils import Unpack
19
+
20
+ from fla.models.utils import Cache
21
+
22
+
23
+ class MesaNet(nn.Module):
24
+ """
25
+ The layer implementaion for [MesaNet: Sequence Modeling by Locally Optimal Test-Time Training]. # noqa
26
+
27
+ Args:
28
+ hidden_size (int, Optional):
29
+ The hidden size of the input. Default: 2048.
30
+ expand_v (float, Optional):
31
+ The expansion ratio for the value dim. Default: 1.
32
+ num_heads (int, Optional):
33
+ The number of heads. Default: 16.
34
+ mode (str, Optional):
35
+ Which MesaNet kernel to use.
36
+ Currently available: `chunk`.
37
+ Default: `chunk`.
38
+ use_output_gate (bool, Optional):
39
+ Whether to use output gate. Default: `False`.
40
+ conv_size (int):
41
+ The kernel size of the short convolution. Default: 4.
42
+ layer_idx (int, Optional):
43
+ The index of the layer. Default: None.
44
+ norm_eps (float, Optional):
45
+ The epsilon value for the normalization layer. Default: 1e-5.
46
+ lambda_lower_bound (float):
47
+ The lower bound for the lambda parameter. Default: 0.25.
48
+ max_cg_step_training (int):
49
+ The maximum number of CG steps for training. Default: 30.
50
+ max_cg_step_decoding (int):
51
+ The maximum number of CG steps for decoding. Default: 30.
52
+ """
53
+
54
+ def __init__(
55
+ self,
56
+ hidden_size: int = 2048,
57
+ num_heads: int = 16,
58
+ head_dim: int = 128,
59
+ mode: str = 'chunk',
60
+ use_output_gate: bool = False,
61
+ use_short_conv: bool = True,
62
+ conv_size: int = 4,
63
+ conv_bias: bool = False,
64
+ layer_idx: int = None,
65
+ norm_eps: float = 1e-5,
66
+ lambda_lower_bound: float = 0.25,
67
+ max_cg_step_training: int = 30,
68
+ max_cg_step_decoding: int = 30,
69
+ **kwargs,
70
+ ) -> MesaNet:
71
+ super().__init__()
72
+
73
+ self.mode = mode
74
+ self.hidden_size = hidden_size
75
+ self.use_output_gate = use_output_gate
76
+ self.use_short_conv = use_short_conv
77
+ self.conv_size = conv_size
78
+ self.conv_bias = conv_bias
79
+ self.num_heads = num_heads
80
+ self.head_dim = head_dim
81
+ self.key_dim = self.num_heads * self.head_dim
82
+ self.value_dim = self.key_dim
83
+ self.head_k_dim = self.head_dim
84
+ self.head_v_dim = self.head_dim
85
+ self.layer_idx = layer_idx
86
+ self.lambda_lower_bound = lambda_lower_bound
87
+ self.max_cg_step_training = max_cg_step_training
88
+ self.max_cg_step_decoding = max_cg_step_decoding
89
+
90
+ self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
91
+ self.k_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
92
+ self.v_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
93
+ self.a_proj = nn.Linear(hidden_size, self.num_heads, bias=True)
94
+ self.b_proj = nn.Linear(hidden_size, self.num_heads, bias=True)
95
+
96
+ lambda_initial_value = 1.0
97
+ init_lamb_value = torch.log(torch.exp(torch.tensor(lambda_initial_value - lambda_lower_bound)) - 1.0)
98
+ init_lamb_params = torch.empty(self.key_dim, dtype=torch.float32).fill_(init_lamb_value)
99
+
100
+ self.lambda_params = nn.Parameter(init_lamb_params)
101
+ self.lambda_params._no_weight_decay = True
102
+
103
+ self.conv_size = conv_size
104
+ self.q_conv1d = ShortConvolution(
105
+ hidden_size=self.key_dim,
106
+ kernel_size=conv_size,
107
+ bias=self.conv_bias,
108
+ activation='silu',
109
+ )
110
+ self.k_conv1d = ShortConvolution(
111
+ hidden_size=self.key_dim,
112
+ kernel_size=conv_size,
113
+ bias=self.conv_bias,
114
+ activation='silu',
115
+ )
116
+ if use_output_gate:
117
+ self.g_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
118
+ self.o_norm = FusedRMSNormGated(self.head_v_dim, eps=norm_eps)
119
+ else:
120
+ self.o_norm = RMSNorm(self.head_v_dim, eps=norm_eps, dtype=torch.float32)
121
+ self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
122
+
123
+ def forward(
124
+ self,
125
+ hidden_states: torch.Tensor,
126
+ attention_mask: torch.Tensor | None = None,
127
+ past_key_values: Cache | None = None,
128
+ use_cache: bool | None = False,
129
+ output_attentions: bool | None = False,
130
+ **kwargs: Unpack[dict],
131
+ ) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
132
+ if attention_mask is not None:
133
+ assert len(attention_mask.shape) == 2, (
134
+ "Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
135
+ "for padding purposes (0 indicating padding). "
136
+ "Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
137
+ )
138
+
139
+ batch_size, q_len, _ = hidden_states.shape
140
+ last_state = get_layer_cache(self, past_key_values)
141
+
142
+ cu_seqlens = kwargs.get('cu_seqlens')
143
+ if attention_mask is not None:
144
+ indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
145
+ hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
146
+
147
+ conv_state_q, conv_state_k = None, None
148
+ if last_state is not None:
149
+ conv_state_q, conv_state_k = last_state['conv_state']
150
+ q, conv_state_q = self.q_conv1d(
151
+ x=self.q_proj(hidden_states),
152
+ cache=conv_state_q,
153
+ output_final_state=use_cache,
154
+ cu_seqlens=cu_seqlens,
155
+ )
156
+ k, conv_state_k = self.k_conv1d(
157
+ x=self.k_proj(hidden_states),
158
+ cache=conv_state_k,
159
+ output_final_state=use_cache,
160
+ cu_seqlens=cu_seqlens,
161
+ )
162
+ v = self.v_proj(hidden_states)
163
+
164
+ q, k = map(lambda x: rearrange(x, '... (h d) -> ... h d', d=self.head_k_dim), (q, k))
165
+ v = rearrange(v, '... (h d) -> ... h d', d=self.head_v_dim)
166
+ beta = self.b_proj(hidden_states).float().sigmoid()
167
+ g = F.logsigmoid(self.a_proj(hidden_states).float())
168
+ lamb = F.softplus(self.lambda_params.float()) + self.lambda_lower_bound
169
+ lamb = lamb.reshape(self.num_heads, -1)
170
+
171
+ last_h_kk, last_h_kv = last_state['recurrent_state'] if last_state is not None else (None, None)
172
+
173
+ # prefilling or training
174
+ # Note that QK will be normalized inside the kernel to avoid saving the activations, thereby reducing the memory usage.
175
+ if last_state is None:
176
+ o, h_kk, h_kv = chunk_mesa_net(
177
+ q=q,
178
+ k=k,
179
+ v=v,
180
+ g=g,
181
+ beta=beta,
182
+ lamb=lamb,
183
+ output_final_state=use_cache,
184
+ max_CG_iteration=self.max_cg_step_training,
185
+ use_qk_l2norm_in_kernel=True,
186
+ cu_seqlens=cu_seqlens,
187
+ )
188
+ # decoding
189
+ else:
190
+ q = l2_norm(q)
191
+ k = l2_norm(k)
192
+ o, h_kk, h_kv = mesa_net_decoding_one_step(
193
+ q=q.squeeze(0),
194
+ k=k.squeeze(0),
195
+ v=v.squeeze(0),
196
+ g=g.squeeze(0),
197
+ beta=beta.squeeze(0),
198
+ lamb=lamb,
199
+ prev_h_kk=last_h_kk,
200
+ prev_h_kv=last_h_kv,
201
+ max_CG_iteration=self.max_cg_step_decoding,
202
+ )
203
+ o = o.unsqueeze(0).to(q)
204
+
205
+ update_layer_cache(
206
+ self,
207
+ past_key_values,
208
+ recurrent_state=(h_kk, h_kv),
209
+ conv_state=(conv_state_q, conv_state_k),
210
+ offset=q_len,
211
+ )
212
+ if self.use_output_gate:
213
+ g = rearrange(self.g_proj(hidden_states), '... (h d) -> ... h d', d=self.head_v_dim)
214
+ o = self.o_norm(o, g)
215
+ else:
216
+ o = self.o_norm(o)
217
+ o = rearrange(o, 'b t h d -> b t (h d)')
218
+ o = self.o_proj(o)
219
+ if attention_mask is not None:
220
+ o = pad_input(o.squeeze(0), indices, batch_size, q_len)
221
+ return o, None, past_key_values
fla/layers/mla.py ADDED
@@ -0,0 +1,225 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+
3
+ """ Implementing the Deepseek Multi Latent Attention (MLA) module. Reference:
4
+
5
+ https://github.com/huggingface/transformers/blob/main/src/transformers/models/deepseek_v3/modeling_deepseek_v3.py#L328
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import math
11
+ import warnings
12
+ from typing import TYPE_CHECKING
13
+
14
+ import torch
15
+ import torch.nn as nn
16
+ import torch.nn.functional as F
17
+ from einops import rearrange, repeat
18
+ from transformers.utils import logging
19
+
20
+ from fla.layers.utils import pad_input, unpad_input
21
+ from fla.modules import RMSNorm, RotaryEmbedding
22
+ from fla.ops.utils.index import prepare_lens_from_mask
23
+
24
+ if TYPE_CHECKING:
25
+ from fla.models.utils import Cache
26
+
27
+ try:
28
+ from flash_attn import flash_attn_func, flash_attn_varlen_func
29
+ except ImportError:
30
+ warnings.warn(
31
+ "Flash Attention is not installed. Please install it via `pip install flash-attn --no-build-isolation`",
32
+ category=ImportWarning,
33
+ )
34
+ flash_attn_func = None
35
+
36
+ logger = logging.get_logger(__name__)
37
+
38
+
39
+ def yarn_get_mscale(scale=1, mscale=1):
40
+ if scale <= 1:
41
+ return 1.0
42
+ return 0.1 * mscale * math.log(scale) + 1.0
43
+
44
+
45
+ class MultiheadLatentAttention(nn.Module):
46
+ r"""
47
+ Multi-headed attention from [Deepseek V2](https://arxiv.org/abs/2405.04434)
48
+ """
49
+
50
+ def __init__(
51
+ self,
52
+ hidden_size: int = 2048,
53
+ num_heads: int = 16,
54
+ q_lora_rank: int | None = 1536, # q lora rank is optional, None indicates no q lora
55
+ qk_rope_head_dim: int = 64,
56
+ kv_lora_rank: int = 512, # following the original Deepseek paper
57
+ v_head_dim: int = 128,
58
+ qk_nope_head_dim: int = 128,
59
+ qk_head_dim: int | None = 192, # qk_nope_head_dim + qk_rope_head_dim
60
+ window_size: int | None = None,
61
+ rope_theta: float = 10000.,
62
+ max_position_embeddings: int | None = None,
63
+ rope_scaling: dict | None = None,
64
+ layer_idx: int = None,
65
+ ) -> MultiheadLatentAttention:
66
+ super().__init__()
67
+
68
+ # sanity check
69
+ if qk_head_dim is not None:
70
+ assert qk_head_dim == qk_nope_head_dim + qk_rope_head_dim, \
71
+ f"qk_head_dim {qk_head_dim} != qk_nope_head_dim {qk_nope_head_dim} + qk_rope_head_dim {qk_rope_head_dim}"
72
+ else:
73
+ qk_head_dim = qk_nope_head_dim + qk_rope_head_dim
74
+
75
+ # attention params info
76
+ self.hidden_size = hidden_size
77
+ self.num_heads = num_heads
78
+ self.q_lora_rank = q_lora_rank
79
+ self.qk_rope_head_dim = qk_rope_head_dim
80
+ self.kv_lora_rank = kv_lora_rank
81
+ self.v_head_dim = v_head_dim
82
+ self.qk_nope_head_dim = qk_nope_head_dim
83
+ self.qk_head_dim = qk_head_dim
84
+
85
+ self.window_size = window_size
86
+ self.rope_theta = rope_theta
87
+ self.max_position_embeddings = max_position_embeddings
88
+ self.layer_idx = layer_idx
89
+
90
+ if flash_attn_func is None:
91
+ raise ImportError("Please install Flash Attention via `pip install flash-attn --no-build-isolation` first")
92
+
93
+ if q_lora_rank is not None:
94
+ self.q_proj = nn.Sequential(
95
+ nn.Linear(hidden_size, q_lora_rank, bias=False),
96
+ RMSNorm(q_lora_rank, dtype=torch.float32),
97
+ nn.Linear(q_lora_rank, self.num_heads * self.qk_head_dim, bias=False),
98
+ )
99
+ else:
100
+ self.q_proj = nn.Linear(hidden_size, self.num_heads * self.qk_head_dim, bias=False)
101
+
102
+ self.k_rope = nn.Linear(hidden_size, self.qk_rope_head_dim, bias=False)
103
+ self.kv_proj = nn.Sequential(
104
+ nn.Linear(hidden_size, self.kv_lora_rank, bias=False),
105
+ RMSNorm(self.kv_lora_rank, dtype=torch.float32),
106
+ nn.Linear(self.kv_lora_rank, self.num_heads * (self.qk_nope_head_dim + self.v_head_dim), bias=False),
107
+ )
108
+
109
+ self.o_proj = nn.Linear(self.num_heads * self.v_head_dim, hidden_size, bias=False)
110
+
111
+ self.scaling = self.qk_head_dim ** (-0.5)
112
+ if rope_scaling is not None and rope_scaling.get("rope_type", "default") != "default":
113
+ mscale_all_dim = rope_scaling.get("mscale_all_dim", 0)
114
+ scaling_factor = rope_scaling["factor"]
115
+ if mscale_all_dim:
116
+ mscale = yarn_get_mscale(scaling_factor, mscale_all_dim)
117
+ self.scaling = self.scaling * mscale * mscale
118
+
119
+ self.rotary = RotaryEmbedding(dim=self.qk_rope_head_dim, base=self.rope_theta)
120
+
121
+ def forward(
122
+ self,
123
+ hidden_states: torch.Tensor,
124
+ attention_mask: torch.Tensor | None,
125
+ past_key_values: Cache | None = None,
126
+ output_attentions: bool = False,
127
+ use_cache: bool = False,
128
+ **kwargs,
129
+ ) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
130
+ # if attention_mask is not None, this is doing inference
131
+ if attention_mask is not None:
132
+ assert len(attention_mask.shape) == 2, (
133
+ "Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
134
+ "for padding purposes (0 indicating padding). "
135
+ "Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
136
+ )
137
+
138
+ # prepare q, k, v
139
+ batch_size, q_len, _ = hidden_states.shape
140
+
141
+ q_states = self.q_proj(hidden_states)
142
+ q_states = rearrange(q_states, '... (h d) -> ... h d', d=self.qk_head_dim)
143
+ q_pass, q_rot = torch.split(q_states, [self.qk_nope_head_dim, self.qk_rope_head_dim], dim=-1)
144
+ k_pass, k_rot = self.kv_proj(hidden_states), self.k_rope(hidden_states)
145
+
146
+ k_rot = rearrange(k_rot, 'b t d -> b t 1 d')
147
+ k_pass = rearrange(k_pass, '... (h d) -> ... h d', d=self.qk_nope_head_dim + self.v_head_dim)
148
+ k_pass, v = torch.split(k_pass, [self.qk_nope_head_dim, self.v_head_dim], dim=-1)
149
+
150
+ # apply rotary position embedding
151
+ seqlen_offset, max_seqlen = 0, q_len
152
+ if past_key_values is not None:
153
+ seqlen_offset = past_key_values.get_seq_length(self.layer_idx)
154
+ max_seqlen = q_len + seqlen_offset
155
+
156
+ if attention_mask is not None:
157
+ seqlen_offset = seqlen_offset + prepare_lens_from_mask(attention_mask) - attention_mask.shape[-1]
158
+ max_seqlen = q_len + max(seqlen_offset)
159
+
160
+ if self.max_position_embeddings is not None:
161
+ max_seqlen = max(max_seqlen, self.max_position_embeddings)
162
+ cu_seqlens = kwargs.get("cu_seqlens")
163
+ q_rot, k_rot = self.rotary(
164
+ q_rot, k_rot, seqlen_offset=seqlen_offset, max_seqlen=max_seqlen, cu_seqlens=cu_seqlens,
165
+ )
166
+
167
+ k_rot = repeat(k_rot, 'b t 1 d -> b t h d', h=self.num_heads)
168
+ q = torch.cat((q_pass, q_rot), dim=-1)
169
+ k = torch.cat((k_pass, k_rot), dim=-1)
170
+
171
+ # TODO: instead of caching the full k, v, we can actually only cache the compressed_kv and k_rot
172
+ # and recover the full k, v from compressed_kv and k_rot
173
+ if past_key_values is not None:
174
+ cache_has_content = past_key_values.get_seq_length(self.layer_idx) > 0
175
+ k_cached, v_cached = past_key_values.update(
176
+ attn_state=(k, v),
177
+ layer_idx=self.layer_idx,
178
+ offset=q_len,
179
+ )['attn_state']
180
+ if cache_has_content:
181
+ k, v = k_cached, v_cached
182
+
183
+ # Head dim match to use flash-attn
184
+ if self.qk_head_dim != self.v_head_dim:
185
+ v = F.pad(v, [0, self.qk_head_dim - self.v_head_dim])
186
+
187
+ # Contains at least one padding token in the sequence
188
+ if attention_mask is not None:
189
+ if q.shape[1] == 1 and self.window_size is not None:
190
+ attention_mask = attention_mask[:, -self.window_size:]
191
+ q, (k, v), indices_q, cu_seqlens, max_seq_lens = unpad_input(q, (k, v), attention_mask, q_len)
192
+ cu_seqlens_q, cu_seqlens_k = cu_seqlens
193
+ max_seqlen_q, max_seqlen_k = max_seq_lens
194
+ o = flash_attn_varlen_func(
195
+ q, k, v,
196
+ cu_seqlens_q=cu_seqlens_q,
197
+ cu_seqlens_k=cu_seqlens_k,
198
+ max_seqlen_q=max_seqlen_q,
199
+ max_seqlen_k=max_seqlen_k,
200
+ causal=True,
201
+ window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
202
+ )
203
+ o = pad_input(o, indices_q, batch_size, q_len)
204
+ elif cu_seqlens is not None:
205
+ o = flash_attn_varlen_func(
206
+ q.squeeze(0), k.squeeze(0), v.squeeze(0),
207
+ cu_seqlens_q=cu_seqlens,
208
+ cu_seqlens_k=cu_seqlens,
209
+ max_seqlen_q=max_seqlen,
210
+ max_seqlen_k=max_seqlen,
211
+ causal=True,
212
+ window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
213
+ ).unsqueeze(0)
214
+ else:
215
+ o = flash_attn_func(
216
+ q, k, v,
217
+ causal=True,
218
+ window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
219
+ )
220
+
221
+ if self.qk_head_dim != self.v_head_dim:
222
+ o = o[:, :, :, :self.v_head_dim]
223
+ o = o.reshape(batch_size, q_len, -1)
224
+ o = self.o_proj(o)
225
+ return o, None, past_key_values
fla/layers/mom.py ADDED
@@ -0,0 +1,831 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ from __future__ import annotations
3
+
4
+ import math
5
+ from typing import TYPE_CHECKING
6
+
7
+ import torch
8
+ import torch.nn as nn
9
+ from einops import rearrange
10
+ from torch.nn import functional as F
11
+
12
+ from fla.modules import FusedRMSNormGated, RMSNorm, ShortConvolution
13
+ from fla.ops.gated_delta_rule import chunk_gated_delta_rule, fused_recurrent_gated_delta_rule
14
+
15
+ if TYPE_CHECKING:
16
+ from transformers.processing_utils import Unpack
17
+
18
+ from fla.models.utils import Cache
19
+
20
+ from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, unpad_input, update_layer_cache
21
+
22
+
23
+ def _upad_input(
24
+ query_layer: torch.Tensor,
25
+ key_layer: torch.Tensor,
26
+ value_layer: torch.Tensor,
27
+ gate_layer: torch.Tensor,
28
+ beta_layer: torch.Tensor,
29
+ attention_mask: torch.Tensor,
30
+ ):
31
+ """
32
+ Unpads query, key, and values tensors, using a single dimension for all tokens even though they belong to
33
+ different batches.
34
+
35
+ This function is used instead of `flash_attn.bert_padding.unpad_input` in order to avoid the recomputation
36
+ of the same intermediary
37
+ tensors for query, key, value tensors.
38
+
39
+ Arguments:
40
+ query_layer (`torch.Tensor`):
41
+ Query state with padding. Shape: (batch_size, query_length, num_heads, head_dim).
42
+ key_layer (`torch.Tensor`):
43
+ Key state with padding. Shape: (batch_size, kv_seq_len, num_key_value_heads, head_dim).
44
+ value_layer (`torch.Tensor`):
45
+ Value state with padding. Shape: (batch_size, kv_seq_len, num_key_value_heads, head_dim).
46
+ attention_mask (`torch.Tensor`):
47
+ Boolean or int tensor of shape (batch_size, sequence_length), 1 means valid and 0 means not valid.
48
+ query_length (`int`):
49
+ Target length.
50
+
51
+ Return:
52
+ query_layer (`torch.Tensor`):
53
+ Query state without padding. Shape: (total_target_length, num_heads, head_dim).
54
+ key_layer (`torch.Tensor`):
55
+ Key state with padding. Shape: (total_source_length, num_key_value_heads, head_dim).
56
+ value_layer (`torch.Tensor`):
57
+ Value state with padding. Shape: (total_source_length, num_key_value_heads, head_dim).
58
+ indices_q (`torch.Tensor`):
59
+ The indices of non-masked tokens from the flattened input target sequence.
60
+ (cu_seqlens_q, cu_seqlens_k) (`Tuple[int]`):
61
+ The cumulative sequence lengths for the target (query) and source (key, value), used to index
62
+ into ragged (unpadded) tensors. `cu_seqlens` shape is (batch_size + 1,).
63
+ (max_seqlen_in_batch_q, max_seqlen_in_batch_k) (`Tuple[int]`):
64
+ Maximum sequence length in batch (`max_seqlen_in_batch_q` for the target sequence i.e. query,
65
+ `max_seqlen_in_batch_k` for the source sequence i.e. key/value).
66
+ """
67
+ query_length = query_layer.shape[1]
68
+ indices_k, cu_seqlens_k, max_seqlen_in_batch_k = get_unpad_data(attention_mask)
69
+ batch_size, kv_seq_len, dim = key_layer.shape
70
+ v_dim = value_layer.shape[-1]
71
+
72
+ key_layer = index_first_axis(key_layer.reshape(batch_size * kv_seq_len, dim), indices_k)
73
+ value_layer = index_first_axis(
74
+ value_layer.reshape(batch_size * kv_seq_len, v_dim), indices_k,
75
+ )
76
+ gate_layer = index_first_axis(gate_layer.reshape(batch_size * kv_seq_len, -1), indices_k)
77
+ beta_layer = index_first_axis(beta_layer.reshape(batch_size * kv_seq_len, -1), indices_k)
78
+ if query_length == kv_seq_len:
79
+ query_layer = index_first_axis(query_layer.reshape(batch_size * kv_seq_len, dim), indices_k)
80
+ cu_seqlens_q = cu_seqlens_k
81
+ max_seqlen_in_batch_q = max_seqlen_in_batch_k
82
+ indices_q = indices_k
83
+ elif query_length == 1:
84
+ max_seqlen_in_batch_q = 1
85
+ cu_seqlens_q = torch.arange(
86
+ batch_size + 1, dtype=torch.int32, device=query_layer.device,
87
+ ) # There is a memcpy here, that is very bad.
88
+ indices_q = cu_seqlens_q[:-1]
89
+ query_layer = query_layer.squeeze(1)
90
+ else:
91
+ # The -q_len: slice assumes left padding.
92
+ attention_mask = attention_mask[:, -query_length:]
93
+ query_layer, indices_q, cu_seqlens_q, max_seqlen_in_batch_q = unpad_input(query_layer, attention_mask)
94
+
95
+ return (
96
+ query_layer,
97
+ key_layer,
98
+ value_layer,
99
+ gate_layer,
100
+ beta_layer,
101
+ indices_q,
102
+ (cu_seqlens_q, cu_seqlens_k),
103
+ (max_seqlen_in_batch_q, max_seqlen_in_batch_k),
104
+ )
105
+
106
+
107
+ def transform(
108
+ x: torch.Tensor,
109
+ routing_mask: torch.Tensor,
110
+ num_memories: int,
111
+ selected_memories: torch.Tensor,
112
+ attention_mask: torch.Tensor,
113
+ ):
114
+ """
115
+ Reorganize token embeddings into memory-aligned chunks.
116
+
117
+ Steps:
118
+ - Expand for top-k routing if needed.
119
+ - Mask out padded tokens via `attention_mask`.
120
+ - Sort tokens by (batch, memory).
121
+ - Gather and pad tokens per memory slot.
122
+
123
+ Args:
124
+ x: (batch, seq, hidden) input embeddings.
125
+ routing_mask: (batch, seq, num_memories) binary routing mask.
126
+ num_memories: number of memory slots.
127
+ selected_memories: memory indices per token,
128
+ (batch, seq) if k=1 else (batch, seq, topk).
129
+ attention_mask: (batch, seq) valid-token mask.
130
+
131
+ Returns:
132
+ transformed_x: (num_memories, batch, max_len, hidden) reorganized tokens.
133
+ truncation_indices: (batch*num_memories, max_len) gather indices.
134
+ sorted_indices: (batch*seq*topk,) global sort order.
135
+ max_len: int, max tokens per memory.
136
+ mask: (batch*num_memories, max_len) validity mask.
137
+ mask_2: (num_memories, batch, max_len) validity mask reshaped.
138
+ """
139
+ if selected_memories.dim() == 3:
140
+ # (batch, seq, topk)
141
+ topk = selected_memories.shape[2]
142
+ # x (batch, seq, hidden)
143
+ x = x.repeat_interleave(topk, dim=1)
144
+ # x (batch, seq * topk, hidden)
145
+ # (batch, seq, topk)
146
+ selected_memories = selected_memories.reshape(selected_memories.shape[0], -1)
147
+ # (batch, seq * topk)
148
+
149
+ if attention_mask is not None:
150
+ attention_mask = attention_mask[:, -routing_mask.shape[1]:]
151
+ # mask out the masked tokens
152
+ routing_mask[attention_mask.bitwise_not().unsqueeze(-1).expand(-1, -1, num_memories)] = 0
153
+
154
+ b, s, d = x.shape
155
+ x_flat = x.reshape(b * s, d) # [b*s, d]
156
+
157
+ with torch.no_grad():
158
+ batch_indices = torch.arange(b, device=x.device).unsqueeze(-1)
159
+ batch_indices = batch_indices.repeat(1, s).reshape(-1)
160
+ if attention_mask is not None:
161
+ # sort the masked tokens to the end
162
+ batch_indices[attention_mask.repeat_interleave(topk, dim=1).bitwise_not().flatten()] = b
163
+ # (b * s)
164
+ memories_flat = selected_memories.reshape(-1) # [b*s]
165
+
166
+ combined = batch_indices * (memories_flat.max() + 1) + memories_flat
167
+ sorted_indices = combined.argsort()
168
+
169
+ x_sorted = x_flat[sorted_indices] # [b*s, d]
170
+ # (b*s, hidden) -> (b, s, hidd)
171
+ with torch.no_grad():
172
+ # routing_mask (b, s, num_memories)
173
+ batch_memory_tokens = routing_mask.sum(dim=1)
174
+ # (b, num_memories)
175
+ flatten_offset = batch_memory_tokens.flatten().cumsum(dim=0)
176
+ max_len = batch_memory_tokens.max()
177
+ indices = (
178
+ torch.arange(max_len, device=flatten_offset.device).unsqueeze(0).expand(b * num_memories, -1)
179
+ + torch.cat([torch.tensor([0], device=flatten_offset.device), flatten_offset[:-1]], dim=0).unsqueeze(1)
180
+ )
181
+ mask = indices < flatten_offset.unsqueeze(-1)
182
+ truncation_indices = torch.where(mask, indices, torch.zeros_like(indices))
183
+
184
+ gathered_x = torch.gather(x_sorted, 0, truncation_indices.reshape(-1).unsqueeze(-1).expand(-1, d))
185
+ transformed_x = gathered_x.reshape(b * num_memories, -1, d).reshape((b, num_memories, max_len, d)).transpose(0, 1)
186
+ # transformed_x = transformed_x * mask.unsqueeze(-1).expand_as(transformed_x)
187
+ # pad_x = torch.zeros((b * num_memories, capacity_len-max_len, d), dtype=transformed_x.dtype, device=transformed_x.device)
188
+ # pad_mask = torch.zeros((b * num_memories, capacity_len-max_len), dtype=transformed_x.dtype, device=transformed_x.device)
189
+ # left pad
190
+ # transformed_x = torch.cat((pad_x, transformed_x), dim=1).reshape((b, num_memories, capacity_len, d)).transpose(0, 1)
191
+ mask_2 = mask.reshape((b, num_memories, max_len)).transpose(0, 1)
192
+ # truncation_indices += capacity_len-max_len
193
+ # if attention_mask is not None:
194
+ # mask_2
195
+
196
+ return transformed_x, truncation_indices, sorted_indices, max_len, mask, mask_2
197
+
198
+
199
+ def reconstruct(
200
+ transformed_x,
201
+ indices: torch.Tensor,
202
+ sorted_indices: torch.Tensor,
203
+ batch_size: int,
204
+ seq_len: int,
205
+ topk: int,
206
+ routing_weights: torch.Tensor,
207
+ mask: torch.Tensor,
208
+ ):
209
+ '''
210
+ Reconstruct and mix transformed outputs back into the original input sequence shape.
211
+
212
+ Key operations:
213
+ 1. Reshapes and transposes `transformed_x` to prepare for scattering.
214
+ 2. Applies the `mask` to zero out invalid positions.
215
+ 3. Uses `torch.scatter_add_` to scatter and sum the transformed outputs back to their original positions
216
+ based on `indices`.
217
+ 4. Rearranges the scattered outputs using `sorted_indices` to ensure correct ordering.
218
+ 5. Applies the `routing_weights` to weight the outputs.
219
+ 6. Sums over the `topk` dimension to produce the final reconstructed output.
220
+
221
+ Args:
222
+ transformed_x (torch.Tensor):
223
+ The transformed output tensor from memory units or experts.
224
+ Shape: (num_memories, batch_size, capacity_len, hidden_size)
225
+ indices (torch.Tensor):
226
+ Indices used for scattering the transformed outputs back to their corresponding positions.
227
+ Shape: (batch*num_memories, max_len)
228
+ sorted_indices (torch.Tensor):
229
+ Sorting indices used to rearrange the scattered outputs back into the original sequence order.
230
+ Shape: (batch_size*seq_len*topk)
231
+ batch_size (int):
232
+ The size of the batch.
233
+ seq_len (int):
234
+ The length of the input sequence.
235
+ topk (int):
236
+ The number of top elements selected (`topk`) per token during the selection process.
237
+ routing_weights (torch.Tensor):
238
+ Routing weights assigned to the top-k selected outputs when reconstructing the final output.
239
+ Shape: (batch_size, seq_len, topk)
240
+ mask (torch.Tensor):
241
+ Boolean mask indicating valid positions in the sequence.
242
+ Shape: (batch*num_memories, max_len)
243
+
244
+ Returns:
245
+ restored_x (torch.Tensor):
246
+ The reconstructed output tensor in the original input sequence shape.
247
+ Shape: (batch_size, seq_len, hidden_size)
248
+ '''
249
+ transformed_x = transformed_x.transpose(0, 1).reshape(
250
+ (-1, transformed_x.shape[2], transformed_x.shape[3]))
251
+ b, s, k, d = batch_size, seq_len, topk, transformed_x.shape[2]
252
+ gathered_x = transformed_x.reshape(
253
+ (transformed_x.shape[0] * transformed_x.shape[1], transformed_x.shape[2]))
254
+ mask_expanded = mask.reshape(-1).unsqueeze(-1).expand_as(gathered_x)
255
+ gathered_x = gathered_x * mask_expanded
256
+
257
+ assert (indices >= 0).all(), "Indices should be non-negative"
258
+
259
+ resortd_x = torch.zeros((b * s * k, d), device=gathered_x.device, dtype=gathered_x.dtype).scatter_add_(
260
+ 0,
261
+ indices.reshape(-1).unsqueeze(-1).expand(-1, d),
262
+ gathered_x,
263
+ )
264
+ assert (indices < resortd_x.size(0)).all(), "Indices should be less than resortd_x size"
265
+
266
+ inverse_indices = sorted_indices.argsort()
267
+ rearranged_x_flat = resortd_x[inverse_indices]
268
+ restored_x = rearranged_x_flat.reshape((b, s * k, d))
269
+ restored_x = restored_x.reshape(b, s, k, d) * routing_weights.reshape(b, s, k).unsqueeze(-1)
270
+ restored_x = restored_x.sum(dim=2)
271
+ return restored_x
272
+
273
+
274
+ class MomAttention(nn.Module):
275
+ """
276
+ The layer implementaion for [MoM: Linear Sequence Modeling with Mixture-of-Memories](https://arxiv.org/abs/2502.13685).
277
+ """
278
+
279
+ def __init__(
280
+ self,
281
+ hidden_size: int = 2048,
282
+ head_dim: int = 256,
283
+ num_heads: int = 4,
284
+ expand_v: float = 2,
285
+ mode: str = 'chunk',
286
+ use_output_gate: bool = True,
287
+ use_short_conv: bool = True,
288
+ conv_size: int = 4,
289
+ conv_bias: bool = False,
290
+ layer_idx: int = None,
291
+ norm_eps: float = 1e-5,
292
+ num_memories: int = 8,
293
+ topk: int = 2,
294
+ capacity: float = 1.0,
295
+ shared_mem: bool = False,
296
+ single_kv_proj: bool = False,
297
+ **kwargs,
298
+ ) -> MomAttention:
299
+ super().__init__()
300
+ self.num_memories = num_memories
301
+ self.topk = topk
302
+ self.capacity = capacity
303
+ self.shared_mem = shared_mem
304
+ self.single_kv_proj = single_kv_proj
305
+
306
+ self.mode = mode
307
+
308
+ self.hidden_size = hidden_size
309
+ self.expand_v = expand_v
310
+
311
+ self.use_output_gate = use_output_gate
312
+ self.use_short_conv = use_short_conv
313
+ self.conv_size = conv_size
314
+ self.conv_bias = conv_bias
315
+
316
+ self.head_dim = head_dim
317
+ self.num_heads = num_heads
318
+
319
+ self.key_dim = int(self.num_heads * self.head_dim)
320
+ self.value_dim = int(self.key_dim * self.expand_v)
321
+ self.head_qk_dim = head_dim
322
+ self.head_v_dim = int(head_dim * self.expand_v)
323
+ self.layer_idx = layer_idx
324
+ self.silu = nn.SiLU()
325
+
326
+ assert mode in ['chunk', 'fused_recurrent'], f"Not suppoerted mode `{mode}`."
327
+
328
+ self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
329
+ self.gate = nn.Linear(self.hidden_size, self.num_memories, bias=False)
330
+ if self.single_kv_proj:
331
+ self.shared_k = nn.Linear(hidden_size, self.key_dim, bias=False)
332
+ self.shared_v = nn.Linear(hidden_size, self.value_dim, bias=False)
333
+ self.shared_b = nn.Linear(hidden_size, self.num_heads, bias=False)
334
+ self.shared_a = nn.Linear(hidden_size, self.num_heads, bias=False)
335
+ else:
336
+ self.k_proj = nn.ModuleList([
337
+ nn.Linear(self.hidden_size, self.key_dim, bias=False)
338
+ for _ in range(self.num_memories)
339
+ ])
340
+ self.v_proj = nn.ModuleList([
341
+ nn.Linear(self.hidden_size, self.value_dim, bias=False)
342
+ for _ in range(self.num_memories)
343
+ ])
344
+ self.b_proj = nn.ModuleList([
345
+ nn.Linear(self.hidden_size, self.num_heads, bias=False)
346
+ for _ in range(self.num_memories)
347
+ ])
348
+ self.a_proj = nn.ModuleList([
349
+ nn.Linear(self.hidden_size, self.num_heads, bias=False)
350
+ for _ in range(self.num_memories)
351
+ ])
352
+ if self.shared_mem:
353
+ self.shared_k = nn.Linear(hidden_size, self.key_dim, bias=False)
354
+ self.shared_v = nn.Linear(hidden_size, self.value_dim, bias=False)
355
+ self.shared_b = nn.Linear(hidden_size, self.num_heads, bias=False)
356
+ self.shared_a = nn.Linear(hidden_size, self.num_heads, bias=False)
357
+
358
+ A = torch.empty(self.num_heads, dtype=torch.float32).uniform_(0, 16)
359
+ self.A_log = nn.Parameter(torch.log(A))
360
+ self.A_log._no_weight_decay = True
361
+ # hard coded for now
362
+ dt_min = 0.001
363
+ dt_max = 0.1
364
+ dt_init_floor = 1e-4
365
+ dt = torch.exp(
366
+ torch.rand(self.num_heads) * (math.log(dt_max) - math.log(dt_min))
367
+ + math.log(dt_min),
368
+ )
369
+ dt = torch.clamp(dt, min=dt_init_floor)
370
+ # Inverse of softplus: https://github.com/pytorch/pytorch/issues/72759
371
+ inv_dt = dt + torch.log(-torch.expm1(-dt))
372
+ self.dt_bias = nn.Parameter(inv_dt)
373
+ # Just to be explicit. Without this we already don't put wd on dt_bias because of the check
374
+ # name.endswith("bias") in param_grouping.py
375
+ self.dt_bias._no_weight_decay = True
376
+
377
+ if use_short_conv:
378
+ self.conv_size = conv_size
379
+ self.q_conv1d = ShortConvolution(
380
+ hidden_size=self.key_dim,
381
+ kernel_size=conv_size,
382
+ bias=conv_bias,
383
+ activation='silu',
384
+ )
385
+ self.k_conv1d = ShortConvolution(
386
+ hidden_size=self.key_dim,
387
+ kernel_size=conv_size,
388
+ bias=conv_bias,
389
+ activation='silu',
390
+ )
391
+ self.v_conv1d = ShortConvolution(
392
+ hidden_size=self.value_dim,
393
+ kernel_size=conv_size,
394
+ bias=conv_bias,
395
+ activation='silu',
396
+ )
397
+ else:
398
+ raise UserWarning(
399
+ "ShortConvolution is crucial to the performance. "
400
+ "Do not turn it off, i.e., setting `use_short_conv=False` unless you know what you are doing.",
401
+ )
402
+ if use_output_gate:
403
+ self.g_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
404
+ self.o_norm = FusedRMSNormGated(self.head_v_dim, eps=norm_eps)
405
+ else:
406
+ self.o_norm = RMSNorm(self.head_v_dim, eps=norm_eps, dtype=torch.float32)
407
+ self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
408
+ self.apply(self._initialize_weights)
409
+
410
+ def _initialize_weights(self, module: nn.Module):
411
+ if getattr(module, "_is_hf_initialized", False):
412
+ return
413
+ if isinstance(module, nn.Linear):
414
+ nn.init.xavier_uniform_(module.weight, gain=2 ** -2.5)
415
+ if module.bias is not None:
416
+ nn.init.zeros_(module.bias)
417
+ module._is_hf_initialized = True
418
+
419
+ def forward(
420
+ self,
421
+ hidden_states: torch.Tensor,
422
+ attention_mask: torch.Tensor | None = None,
423
+ past_key_values: Cache | None = None,
424
+ use_cache: bool | None = False,
425
+ output_attentions: bool | None = False,
426
+ **kwargs: Unpack[dict],
427
+ ) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
428
+ if attention_mask is not None:
429
+ attention_mask = (attention_mask == 1)
430
+ assert len(attention_mask.shape) == 2, (
431
+ "Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
432
+ "for padding purposes (0 indicating padding). "
433
+ "Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
434
+ )
435
+
436
+ origin_cu_seqlens = kwargs.get('cu_seqlens')
437
+ if origin_cu_seqlens is not None:
438
+ hidden_states, attention_mask = self.cu2pad(hidden_states, origin_cu_seqlens)
439
+
440
+ mode = 'fused_recurrent' if (hidden_states.shape[1] <= 64 and not self.training) else self.mode
441
+ if self.training:
442
+ assert mode == 'chunk', "Only chunk mode is supported in training."
443
+
444
+ last_state = get_layer_cache(self, past_key_values)
445
+ # _, q_len = hidden_states.shape[0], hidden_states.shape[1]
446
+
447
+ # 🔍 topk gating
448
+ router_logits = self.gate(hidden_states) # (bsz, q_len, num_memories)
449
+ scores = F.softmax(router_logits, dim=2, dtype=torch.float)
450
+ routing_weights, selected_memories = torch.topk(scores, self.topk, dim=-1) # (bsz, seq, topk)
451
+ routing_weights /= routing_weights.sum(dim=-1, keepdim=True)
452
+ routing_weights = routing_weights.to(hidden_states.dtype) # we cast back to the input dtype
453
+ routing_weights_full = torch.zeros(
454
+ routing_weights.shape[0],
455
+ routing_weights.shape[1],
456
+ self.num_memories,
457
+ dtype=routing_weights.dtype,
458
+ device=routing_weights.device,
459
+ ).scatter(-1, selected_memories, routing_weights)
460
+ routing_mask = routing_weights_full.bool().int()
461
+
462
+ # if self.use_output_gate:
463
+ # o_g = self.g_proj(hidden_states)
464
+
465
+ batch_size, seq_len = hidden_states.shape[0], hidden_states.shape[1]
466
+
467
+ shared_hidden_states = hidden_states
468
+ hidden_states, indices, sorted_indices, max_len, mask, mask_2 = transform(
469
+ hidden_states, routing_mask, self.num_memories, selected_memories, attention_mask)
470
+
471
+ q = self.q_proj(hidden_states)
472
+ if self.single_kv_proj:
473
+ k = self.shared_k(hidden_states)
474
+ v = self.shared_v(hidden_states)
475
+ beta = self.shared_b(hidden_states).sigmoid()
476
+ g = -self.A_log.float().exp() * F.softplus(self.shared_a(hidden_states).float() + self.dt_bias)
477
+ else:
478
+ k = torch.stack([k_expert(hidden_states[i]) for i, k_expert in enumerate(self.k_proj)], dim=0)
479
+ v = torch.stack([v_expert(hidden_states[i]) for i, v_expert in enumerate(self.v_proj)], dim=0)
480
+ beta = torch.stack([b_expert(hidden_states[i]).sigmoid() for i, b_expert in enumerate(self.b_proj)], dim=0)
481
+ g = torch.stack([-self.A_log.float().exp() * F.softplus(a_expert(hidden_states[i]).float() + self.dt_bias)
482
+ for i, a_expert in enumerate(self.a_proj)], dim=0)
483
+
484
+ q, k, v, g, beta, mask_2 = (rearrange(x, 'e b l ... -> (e b) l ...') for x in (q, k, v, g, beta, mask_2))
485
+ cu_q, cu_k, cu_v, cu_g, cu_beta, indices_q, cu_seqlen_all, max_seq_lens = _upad_input(q, k, v, g, beta, mask_2)
486
+ cu_seqlens, reverse_indices = cu_seqlen_all[0].to(torch.long).unique(return_inverse=True)
487
+ cu_q, cu_k, cu_v, cu_g, cu_beta = (x.unsqueeze(0).contiguous() for x in (cu_q, cu_k, cu_v, cu_g, cu_beta))
488
+
489
+ if self.use_short_conv:
490
+ conv_state_q, conv_state_k, conv_state_v = [None, None], [None, None], [None, None]
491
+ if last_state is not None:
492
+ conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
493
+
494
+ conv_cu_seqlens = cu_seqlens
495
+ padded = False
496
+ if self.training:
497
+ conv_cu_seqlens = None
498
+ elif seq_len != 1 and (cu_seqlens[1:] - cu_seqlens[:-1]).min().item() < self.conv_size:
499
+ padded = True
500
+ conv_cu_seqlens, cu_q, cu_k, cu_v, pad_lengths = self.pad_for_conv(cu_seqlens, cu_q, cu_k, cu_v)
501
+
502
+ conv_q = self.prepare_recurrent_state(
503
+ conv_state_q[0],
504
+ conv_cu_seqlens,
505
+ cu_seqlen_all[0],
506
+ reverse_indices,
507
+ batch_size,
508
+ )
509
+ cu_q, conv_q_new = self.q_conv1d(
510
+ x=cu_q,
511
+ cache=conv_q,
512
+ output_final_state=use_cache,
513
+ cu_seqlens=conv_cu_seqlens,
514
+ )
515
+ conv_state_q[0] = self.handle_recurrent_state(
516
+ conv_state_q[0],
517
+ conv_q_new,
518
+ conv_cu_seqlens,
519
+ cu_seqlen_all[0],
520
+ reverse_indices,
521
+ )
522
+ conv_k = self.prepare_recurrent_state(
523
+ conv_state_k[0],
524
+ conv_cu_seqlens,
525
+ cu_seqlen_all[0],
526
+ reverse_indices,
527
+ batch_size,
528
+ )
529
+ cu_k, conv_k_new = self.k_conv1d(
530
+ x=cu_k,
531
+ cache=conv_k,
532
+ output_final_state=use_cache,
533
+ cu_seqlens=conv_cu_seqlens,
534
+ )
535
+ conv_state_k[0] = self.handle_recurrent_state(
536
+ conv_state_k[0],
537
+ conv_k_new,
538
+ conv_cu_seqlens,
539
+ cu_seqlen_all[0],
540
+ reverse_indices,
541
+ )
542
+ conv_v = self.prepare_recurrent_state(
543
+ conv_state_v[0],
544
+ conv_cu_seqlens,
545
+ cu_seqlen_all[0],
546
+ reverse_indices,
547
+ batch_size,
548
+ )
549
+ cu_v, conv_v_new = self.v_conv1d(
550
+ x=cu_v,
551
+ cache=conv_v,
552
+ output_final_state=use_cache,
553
+ cu_seqlens=conv_cu_seqlens,
554
+ )
555
+ conv_state_v[0] = self.handle_recurrent_state(
556
+ conv_state_v[0],
557
+ conv_v_new, conv_cu_seqlens,
558
+ cu_seqlen_all[0],
559
+ reverse_indices,
560
+ )
561
+
562
+ if padded:
563
+ cu_q, cu_k, cu_v = self.unpad_after_conv(conv_cu_seqlens, cu_seqlens, cu_q, cu_k, cu_v, pad_lengths)
564
+
565
+ else:
566
+ q, k, v = self.silu(q), self.silu(k), self.silu(v)
567
+
568
+ cu_q, cu_k, cu_v = map(lambda x: rearrange(x, 'b t (h d) -> b t h d', h=self.num_heads), (cu_q, cu_k, cu_v))
569
+
570
+ recurrent_state = last_state['recurrent_state'] if last_state is not None else [
571
+ None for _ in range(1 + self.shared_mem)]
572
+ if mode == 'chunk':
573
+ o, recurrent_state_ = chunk_gated_delta_rule(
574
+ q=cu_q,
575
+ k=cu_k,
576
+ v=cu_v,
577
+ g=cu_g,
578
+ beta=cu_beta,
579
+ initial_state=recurrent_state[0],
580
+ output_final_state=use_cache,
581
+ use_qk_l2norm_in_kernel=True,
582
+ cu_seqlens=cu_seqlens,
583
+ )
584
+ recurrent_state[0] = self.handle_recurrent_state(
585
+ recurrent_state[0],
586
+ recurrent_state_,
587
+ cu_seqlens,
588
+ cu_seqlen_all[0],
589
+ reverse_indices,
590
+ )
591
+
592
+ elif mode == 'fused_recurrent':
593
+ memories = self.prepare_recurrent_state(
594
+ recurrent_state[0],
595
+ cu_seqlens, cu_seqlen_all[0],
596
+ reverse_indices, batch_size,
597
+ )
598
+ o, recurrent_state_ = fused_recurrent_gated_delta_rule(
599
+ q=cu_q,
600
+ k=cu_k,
601
+ v=cu_v,
602
+ g=cu_g,
603
+ beta=cu_beta,
604
+ initial_state=memories,
605
+ output_final_state=use_cache,
606
+ use_qk_l2norm_in_kernel=True,
607
+ cu_seqlens=cu_seqlens,
608
+ )
609
+ recurrent_state[0] = self.handle_recurrent_state(
610
+ recurrent_state[0],
611
+ recurrent_state_,
612
+ cu_seqlens,
613
+ cu_seqlen_all[0],
614
+ reverse_indices,
615
+ )
616
+
617
+ o = o.squeeze(0).contiguous()
618
+ o = pad_input(o, indices_q, batch_size*self.num_memories, max_len)
619
+ o = rearrange(o, '(e b) l h d -> e b l (h d)', b=batch_size)
620
+ o = reconstruct(o, indices=indices, sorted_indices=sorted_indices, batch_size=batch_size,
621
+ seq_len=seq_len, topk=self.topk, routing_weights=routing_weights, mask=mask)
622
+ o = rearrange(o, 'b l (h d) -> b l h d', h=self.num_heads)
623
+
624
+ if self.shared_mem:
625
+ shared_o = self.shared_o(shared_hidden_states, attention_mask, recurrent_state,
626
+ use_cache, conv_state_q, conv_state_k, conv_state_v)
627
+ o += shared_o
628
+
629
+ update_layer_cache(
630
+ self,
631
+ past_key_values,
632
+ recurrent_state=recurrent_state,
633
+ conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
634
+ offset=q.shape[2],
635
+ )
636
+
637
+ if self.use_output_gate:
638
+ g = rearrange(self.g_proj(shared_hidden_states), '... (h d) -> ... h d', d=self.head_v_dim)
639
+ o = self.o_norm(o, g)
640
+ else:
641
+ o = self.o_norm(o)
642
+ o = rearrange(o, 'b t h d -> b t (h d)')
643
+ o = self.o_proj(o)
644
+
645
+ if origin_cu_seqlens is not None:
646
+ indices, _, _ = get_unpad_data(attention_mask[:, -seq_len:])
647
+ o = index_first_axis(rearrange(o, "b s ... -> (b s) ..."), indices).unsqueeze(0)
648
+
649
+ return o, None, past_key_values, router_logits.view(-1, self.num_memories)
650
+
651
+ def shared_o(
652
+ self,
653
+ hidden_states: torch.Tensor,
654
+ attention_mask: torch.Tensor | None = None,
655
+ recurrent_state=None,
656
+ use_cache: bool | None = False,
657
+ conv_state_q=[None, None],
658
+ conv_state_k=[None, None],
659
+ conv_state_v=[None, None],
660
+ **kwargs,
661
+ ) -> torch.Tensor:
662
+ if attention_mask is not None:
663
+ assert len(attention_mask.shape) == 2, (
664
+ "Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
665
+ "for padding purposes (0 indicating padding). "
666
+ "Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
667
+ )
668
+
669
+ mode = 'fused_recurrent' if hidden_states.shape[1] <= 64 else self.mode
670
+ if self.training:
671
+ assert mode == 'chunk', "Only chunk mode is supported in training."
672
+
673
+ cu_seqlens = None
674
+ if attention_mask is not None:
675
+ batch_size, q_len = hidden_states.shape[0], hidden_states.shape[1]
676
+ indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
677
+ hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
678
+
679
+ if self.use_short_conv:
680
+ q, conv_state_q[1] = self.q_conv1d(
681
+ x=self.q_proj(hidden_states),
682
+ cache=conv_state_q[1],
683
+ output_final_state=use_cache,
684
+ cu_seqlens=cu_seqlens,
685
+ )
686
+ k, conv_state_k[1] = self.k_conv1d(
687
+ x=self.shared_k(hidden_states),
688
+ cache=conv_state_k[1],
689
+ output_final_state=use_cache,
690
+ cu_seqlens=cu_seqlens,
691
+ )
692
+ v, conv_state_v[1] = self.v_conv1d(
693
+ x=self.shared_v(hidden_states),
694
+ cache=conv_state_v[1],
695
+ output_final_state=use_cache,
696
+ cu_seqlens=cu_seqlens,
697
+ )
698
+ else:
699
+ q = self.silu(self.q_proj(hidden_states))
700
+ k = self.silu(self.shared_k(hidden_states))
701
+ v = self.silu(self.shared_v(hidden_states))
702
+
703
+ q, k, v = map(lambda x: rearrange(x, 'b t (h d) -> b t h d', h=self.num_heads), (q, k, v))
704
+ beta = self.shared_b(hidden_states).sigmoid()
705
+ g = -self.A_log.float().exp() * F.softplus(self.shared_a(hidden_states).float() + self.dt_bias)
706
+
707
+ if mode == 'chunk':
708
+ o, recurrent_state[-1] = chunk_gated_delta_rule(
709
+ q=q,
710
+ k=k,
711
+ v=v,
712
+ g=g,
713
+ beta=beta,
714
+ initial_state=recurrent_state[-1],
715
+ output_final_state=use_cache,
716
+ cu_seqlens=cu_seqlens,
717
+ use_qk_l2norm_in_kernel=True,
718
+ )
719
+ elif mode == 'fused_recurrent':
720
+ o, recurrent_state[-1] = fused_recurrent_gated_delta_rule(
721
+ q=q,
722
+ k=k,
723
+ v=v,
724
+ g=g,
725
+ beta=beta,
726
+ initial_state=recurrent_state[-1],
727
+ output_final_state=use_cache,
728
+ cu_seqlens=cu_seqlens,
729
+ use_qk_l2norm_in_kernel=True,
730
+ )
731
+ else:
732
+ raise NotImplementedError(f"Not supported mode `{mode}`.")
733
+
734
+ if attention_mask is not None:
735
+ o = pad_input(o.squeeze(0), indices, batch_size, q_len)
736
+ return o
737
+
738
+ def cu2pad(self, x, cu_seqlens):
739
+ batch_size = cu_seqlens.shape[0] - 1
740
+ max_len = (cu_seqlens[1:] - cu_seqlens[:-1]).max().item()
741
+ indices = torch.tensor([], dtype=torch.long, device=x.device)
742
+ attention_mask = torch.ones((batch_size, max_len), dtype=torch.bool, device=x.device)
743
+ for i in range(batch_size):
744
+ seq_len = cu_seqlens[i+1] - cu_seqlens[i]
745
+ pad_len = max_len - seq_len
746
+ batch_indices = torch.arange(pad_len, max_len, device=x.device)
747
+ batch_indices = batch_indices + i * max_len
748
+ indices = torch.cat([indices, batch_indices])
749
+ attention_mask[i, :pad_len] = False
750
+ x = pad_input(x.squeeze(0), indices, batch_size, max_len)
751
+ return x, attention_mask
752
+
753
+ def pad_for_conv(self, cu_seqlens, cu_q, cu_k, cu_v):
754
+ lengths = cu_seqlens[1:] - cu_seqlens[:-1]
755
+ pad_lengths = torch.clamp(self.conv_size - lengths, min=0)
756
+ new_lengths = lengths + pad_lengths
757
+ new_cu_seqlens = torch.cat([
758
+ torch.tensor([0], device=cu_seqlens.device, dtype=cu_seqlens.dtype),
759
+ torch.cumsum(new_lengths, dim=0),
760
+ ])
761
+ final_total_len = new_cu_seqlens[-1].item()
762
+ new_q = torch.zeros((1, final_total_len, cu_q.shape[-1]), dtype=cu_q.dtype, device=cu_q.device)
763
+ new_k = torch.zeros((1, final_total_len, cu_k.shape[-1]), dtype=cu_k.dtype, device=cu_k.device)
764
+ new_v = torch.zeros((1, final_total_len, cu_v.shape[-1]), dtype=cu_v.dtype, device=cu_v.device)
765
+ num_sequences = len(lengths)
766
+ for i in range(num_sequences):
767
+ src_start = cu_seqlens[i]
768
+ src_end = cu_seqlens[i+1]
769
+ dest_start = new_cu_seqlens[i] + pad_lengths[i]
770
+ dest_end = new_cu_seqlens[i+1]
771
+ new_q[:, dest_start:dest_end, ...] = cu_q[:, src_start:src_end, ...]
772
+ new_k[:, dest_start:dest_end, ...] = cu_k[:, src_start:src_end, ...]
773
+ new_v[:, dest_start:dest_end, ...] = cu_v[:, src_start:src_end, ...]
774
+
775
+ return new_cu_seqlens, new_q, new_k, new_v, pad_lengths
776
+
777
+ def unpad_after_conv(self, conv_cu_seqlens, cu_seqlens, cu_q, cu_k, cu_v, pad_lengths):
778
+ original_total_len = cu_seqlens[-1].item()
779
+ orig_q = torch.empty((1, original_total_len, cu_q.shape[-1]), dtype=cu_q.dtype, device=cu_q.device)
780
+ orig_k = torch.empty((1, original_total_len, cu_k.shape[-1]), dtype=cu_k.dtype, device=cu_k.device)
781
+ orig_v = torch.empty((1, original_total_len, cu_v.shape[-1]), dtype=cu_v.dtype, device=cu_v.device)
782
+
783
+ num_sequences = len(pad_lengths)
784
+ for i in range(num_sequences):
785
+ dest_start = cu_seqlens[i]
786
+ dest_end = cu_seqlens[i+1]
787
+ src_start = conv_cu_seqlens[i] + pad_lengths[i]
788
+ src_end = conv_cu_seqlens[i+1]
789
+
790
+ orig_q[:, dest_start:dest_end, ...] = cu_q[:, src_start:src_end, ...]
791
+ orig_k[:, dest_start:dest_end, ...] = cu_k[:, src_start:src_end, ...]
792
+ orig_v[:, dest_start:dest_end, ...] = cu_v[:, src_start:src_end, ...]
793
+ return orig_q, orig_k, orig_v
794
+
795
+ def prepare_recurrent_state(self, recurrent_state, cu_seqlens, cu_seqlen_all, reverse_indices, batch_size):
796
+ if recurrent_state is None:
797
+ return None
798
+
799
+ if cu_seqlens is None:
800
+ return recurrent_state
801
+
802
+ total_len = len(cu_seqlen_all)
803
+ if len(cu_seqlens) != total_len:
804
+ # select memories that are activated
805
+ memories = torch.zeros_like(recurrent_state[:self.topk*batch_size])
806
+ mem_id = 0
807
+ for i in range(total_len-1):
808
+ if cu_seqlen_all[i] != cu_seqlen_all[i+1]:
809
+ memories[mem_id] = recurrent_state[i]
810
+ mem_id += 1
811
+ assert mem_id == self.topk * batch_size, f"The number of memories {mem_id} is not correct."
812
+ else:
813
+ memories = recurrent_state
814
+
815
+ return memories
816
+
817
+ def handle_recurrent_state(self, recurrent_state, recurrent_state_new, cu_seqlens, cu_seqlen_all, reverse_indices):
818
+ if recurrent_state_new is None:
819
+ return None
820
+ if cu_seqlens is None:
821
+ return recurrent_state_new
822
+ if recurrent_state is None:
823
+ recurrent_state = torch.zeros_like(recurrent_state_new[reverse_indices[1:]-1])
824
+ total_len = len(cu_seqlen_all)
825
+ if len(cu_seqlens) != total_len:
826
+ for i in range(total_len-1):
827
+ if cu_seqlen_all[i] != cu_seqlen_all[i+1]:
828
+ recurrent_state[i] = recurrent_state_new[reverse_indices[i+1]-1]
829
+ else:
830
+ recurrent_state = recurrent_state_new
831
+ return recurrent_state
fla/layers/multiscale_retention.py ADDED
@@ -0,0 +1,303 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ import torch
8
+ import torch.nn as nn
9
+ from einops import rearrange, repeat
10
+ from transformers.activations import ACT2FN
11
+
12
+ from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, update_layer_cache
13
+ from fla.modules import FusedRMSNormGated, RMSNorm, ShortConvolution
14
+ from fla.modules.rotary import RotaryEmbedding
15
+ from fla.ops.retention import chunk_retention, fused_chunk_retention, fused_recurrent_retention, parallel_retention
16
+ from fla.ops.utils.index import prepare_lens_from_mask
17
+
18
+ if TYPE_CHECKING:
19
+ from transformers.processing_utils import Unpack
20
+
21
+ from fla.models.utils import Cache
22
+
23
+
24
+ class MultiScaleRetention(nn.Module):
25
+ r"""
26
+ The layer implementaion for [Retentive Network: A Successor to Transformer for Large Language Models](https://arxiv.org/pdf/2307.08621.pdf). # noqa
27
+
28
+ Args:
29
+ mode (str, Optional):
30
+ Which Retention kernel to use.
31
+ Currently available: `chunk`, `fused_recurrent`, `parallel`, and `fused_chunk`.
32
+ Default: `chunk`.
33
+ hidden_size (int, Optional):
34
+ The hidden size of the input. Default: 1024.
35
+ expand_k (float, Optional):
36
+ The expansion ratio for the key dim. Default: 1.0.
37
+ expand_v (float, Optional):
38
+ The expansion ratio for the value dim. Default: 2.0.
39
+ num_heads (int, Optional):
40
+ The number of heads. Default: 8.
41
+ num_kv_heads (int, Optional):
42
+ The number of key/value heads, used for MQA. Default: None.
43
+ feature_map (str, Optional):
44
+ Feature map function applied to queries/keys. Default: None.
45
+ use_short_conv (bool, Optional):
46
+ Whether to use short convolutions. Default: `False`.
47
+ conv_size (int, Optional):
48
+ The kernel size of the short convolution, only used when `use_short_conv` is `True`. Default: 4.
49
+ conv_bias (bool, Optional):
50
+ Whether to use bias in the short convolution, only used when `use_short_conv` is `True`. Default: `False`.
51
+ use_output_gate (bool, Optional):
52
+ Whether to use output gate. Default: `True`.
53
+ gate_fn (str, Optional):
54
+ The activation function for the output gate. Default: `swish`.
55
+ elementwise_affine (bool, Optional):
56
+ If `True`, applies elementwise affine to LayerNorm with learnable parameters. Default: `True`.
57
+ norm_eps (float, Optional):
58
+ The epsilon value for the layernorm/rmsnorm layer. Default: 1e-5.
59
+ fuse_norm (bool, Optional):
60
+ Whether to fuse the norm and the output gate for better memory footprint. Default: `True`.
61
+ layer_idx (int, Optional):
62
+ The index of the layer. Default: None.
63
+ """
64
+
65
+ def __init__(
66
+ self,
67
+ mode: str = 'chunk',
68
+ hidden_size: int = 1024,
69
+ expand_k: float = 1.0,
70
+ expand_v: float = 2.0,
71
+ num_heads: int = 8,
72
+ num_kv_heads: int | None = None,
73
+ feature_map: str | None = None,
74
+ use_short_conv: bool = False,
75
+ conv_size: int = 4,
76
+ conv_bias: bool = False,
77
+ use_output_gate: bool = True,
78
+ gate_fn: str = 'swish',
79
+ elementwise_affine: bool | None = True,
80
+ norm_eps: float = 1e-5,
81
+ fuse_norm: bool = True,
82
+ layer_idx: int = None,
83
+ **kwargs,
84
+ ) -> MultiScaleRetention:
85
+ super().__init__()
86
+
87
+ self.mode = mode
88
+ self.hidden_size = hidden_size
89
+ self.expand_k = expand_k
90
+ self.expand_v = expand_v
91
+ self.num_heads = num_heads
92
+ self.num_kv_heads = num_kv_heads if num_kv_heads is not None else num_heads
93
+ self.num_kv_groups = self.num_heads // self.num_kv_heads
94
+ self.feature_map_fn = ACT2FN[feature_map] if feature_map is not None else None
95
+
96
+ self.use_short_conv = use_short_conv
97
+ self.conv_size = conv_size
98
+ self.conv_bias = conv_bias
99
+ self.use_output_gate = use_output_gate
100
+
101
+ self.key_dim = int(hidden_size * expand_k)
102
+ self.value_dim = int(hidden_size * expand_v)
103
+ self.key_dim_per_group = self.key_dim // self.num_kv_groups
104
+ self.value_dim_per_group = self.value_dim // self.num_kv_groups
105
+ self.layer_idx = layer_idx
106
+
107
+ assert mode in ['chunk', 'fused_chunk', 'parallel', 'fused_recurrent'], f"Not supported mode `{mode}`."
108
+ assert self.key_dim % num_heads == 0, f"key dim must be divisible by num_heads of {num_heads}"
109
+ assert self.value_dim % num_heads == 0, f"value dim must be divisible by num_heads of {num_heads}"
110
+
111
+ self.head_k_dim = self.key_dim // num_heads
112
+ self.head_v_dim = self.value_dim // num_heads
113
+
114
+ self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
115
+ self.k_proj = nn.Linear(hidden_size, self.key_dim_per_group, bias=False)
116
+ self.v_proj = nn.Linear(hidden_size, self.value_dim_per_group, bias=False)
117
+ if self.use_output_gate:
118
+ self.g_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
119
+
120
+ if use_short_conv:
121
+ self.conv_size = conv_size
122
+ self.q_conv1d = ShortConvolution(
123
+ hidden_size=self.key_dim,
124
+ kernel_size=conv_size,
125
+ bias=conv_bias,
126
+ activation='silu',
127
+ )
128
+ self.k_conv1d = ShortConvolution(
129
+ hidden_size=self.key_dim_per_group,
130
+ kernel_size=conv_size,
131
+ bias=conv_bias,
132
+ activation='silu',
133
+ )
134
+ self.v_conv1d = ShortConvolution(
135
+ hidden_size=self.value_dim_per_group,
136
+ kernel_size=conv_size,
137
+ bias=conv_bias,
138
+ activation='silu',
139
+ )
140
+
141
+ self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
142
+
143
+ if gate_fn == 'swish' and fuse_norm and use_output_gate:
144
+ self.g_norm_swish_gate = FusedRMSNormGated(
145
+ hidden_size=self.head_v_dim,
146
+ elementwise_affine=elementwise_affine,
147
+ eps=norm_eps,
148
+ )
149
+ self.fuse_norm_and_gate = True
150
+ else:
151
+ self.fuse_norm_and_gate = False
152
+ self.g_norm = RMSNorm(
153
+ hidden_size=self.head_v_dim,
154
+ elementwise_affine=elementwise_affine,
155
+ eps=norm_eps,
156
+ dtype=torch.float32
157
+ )
158
+ self.gate_fn = ACT2FN[gate_fn]
159
+
160
+ # TODO: fix this issue
161
+ # https://github.com/Dao-AILab/flash-attention/blob/main/flash_attn/ops/triton/rotary.py#L180
162
+ # Ideally, we would want to support arbitrary d_head_qk
163
+ assert self.head_k_dim <= 256, "head_k_dim must be less than or equal to 256"
164
+ self.rotary = RotaryEmbedding(dim=self.head_k_dim)
165
+
166
+ def forward(
167
+ self,
168
+ hidden_states: torch.Tensor,
169
+ attention_mask: torch.Tensor | None = None,
170
+ past_key_values: Cache | None = None,
171
+ use_cache: bool | None = False,
172
+ output_attentions: bool | None = False,
173
+ **kwargs: Unpack[dict],
174
+ ) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
175
+ if attention_mask is not None:
176
+ assert len(attention_mask.shape) == 2, (
177
+ "Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
178
+ "for padding purposes (0 indicating padding). "
179
+ "Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
180
+ )
181
+
182
+ batch_size, q_len, _ = hidden_states.shape
183
+ mode = 'fused_recurrent' if q_len <= 64 else self.mode
184
+
185
+ last_state = get_layer_cache(self, past_key_values)
186
+
187
+ cu_seqlens = kwargs.get('cu_seqlens')
188
+ if attention_mask is not None:
189
+ indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
190
+ hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
191
+
192
+ if self.use_short_conv:
193
+ conv_state_q, conv_state_k, conv_state_v = None, None, None
194
+ if last_state is not None:
195
+ conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
196
+ q, conv_state_q = self.q_conv1d(
197
+ x=self.q_proj(hidden_states),
198
+ cache=conv_state_q,
199
+ output_final_state=use_cache,
200
+ cu_seqlens=cu_seqlens,
201
+ )
202
+ k, conv_state_k = self.k_conv1d(
203
+ x=self.k_proj(hidden_states),
204
+ cache=conv_state_k,
205
+ output_final_state=use_cache,
206
+ cu_seqlens=cu_seqlens,
207
+ )
208
+ v, conv_state_v = self.v_conv1d(
209
+ x=self.v_proj(hidden_states),
210
+ cache=conv_state_v,
211
+ output_final_state=use_cache,
212
+ cu_seqlens=cu_seqlens,
213
+ )
214
+ else:
215
+ q = self.q_proj(hidden_states)
216
+ k = self.k_proj(hidden_states)
217
+ v = self.v_proj(hidden_states)
218
+
219
+ q = rearrange(q, '... (h d) -> ... h d', d=self.head_k_dim)
220
+ k = rearrange(k, '... (h d) -> ... h d', d=self.head_k_dim)
221
+ if self.feature_map_fn is not None:
222
+ q, k = map(self.feature_map_fn, (q, k))
223
+
224
+ seqlen_offset, max_seqlen = 0, q.shape[1]
225
+ if past_key_values is not None:
226
+ seqlen_offset = past_key_values.get_seq_length(self.layer_idx)
227
+ max_seqlen = q.shape[1] + seqlen_offset
228
+
229
+ if attention_mask is not None and seqlen_offset > 0:
230
+ # to deliminate the offsets of padding tokens
231
+ seqlen_offset = prepare_lens_from_mask(attention_mask) - q_len
232
+ max_seqlen = q.shape[1] + seqlen_offset.max().item()
233
+
234
+ q, k = self.rotary(q, k, seqlen_offset=seqlen_offset, max_seqlen=max_seqlen, cu_seqlens=cu_seqlens)
235
+
236
+ if self.num_kv_groups > 1:
237
+ k = repeat(k, '... h d -> ... (h g) d', g=self.num_kv_groups)
238
+ v = repeat(v, '... (h d) -> ... (h g) d', d=self.head_v_dim, g=self.num_kv_groups)
239
+ else:
240
+ v = rearrange(v, '... (h d) -> ... h d', d=self.head_v_dim)
241
+
242
+ recurrent_state = last_state['recurrent_state'] if last_state is not None else None
243
+ if mode == 'chunk':
244
+ o, recurrent_state = chunk_retention(
245
+ q=q,
246
+ k=k,
247
+ v=v,
248
+ initial_state=recurrent_state,
249
+ output_final_state=use_cache,
250
+ cu_seqlens=cu_seqlens,
251
+ )
252
+ elif mode == 'fused_chunk':
253
+ o, recurrent_state = fused_chunk_retention(
254
+ q=q,
255
+ k=k,
256
+ v=v,
257
+ initial_state=recurrent_state,
258
+ output_final_state=use_cache,
259
+ cu_seqlens=cu_seqlens,
260
+ )
261
+ elif mode == 'parallel':
262
+ o, recurrent_state = parallel_retention(
263
+ q=q,
264
+ k=k,
265
+ v=v,
266
+ cu_seqlens=cu_seqlens,
267
+ )
268
+ elif mode == 'fused_recurrent':
269
+ o, recurrent_state = fused_recurrent_retention(
270
+ q=q,
271
+ k=k,
272
+ v=v,
273
+ initial_state=recurrent_state,
274
+ output_final_state=use_cache,
275
+ cu_seqlens=cu_seqlens,
276
+ )
277
+ else:
278
+ raise NotImplementedError(f"Not supported mode `{mode}`.")
279
+
280
+ update_layer_cache(
281
+ self,
282
+ past_key_values,
283
+ recurrent_state=recurrent_state,
284
+ conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
285
+ offset=q_len,
286
+ )
287
+
288
+ if self.use_output_gate:
289
+ g = self.g_proj(hidden_states)
290
+ if self.fuse_norm_and_gate:
291
+ g = rearrange(g, '... (h d) -> ... h d', d=self.head_v_dim)
292
+ o = self.g_norm_swish_gate(o, g)
293
+ o = rearrange(o, '... h d -> ... (h d)')
294
+ else:
295
+ o = rearrange(self.g_norm(o), '... h d -> ... (h d)')
296
+ o = o * self.gate_fn(g)
297
+ else:
298
+ o = rearrange(self.g_norm(o), '... h d -> ... (h d)')
299
+ o = self.o_proj(o)
300
+ if attention_mask is not None:
301
+ o = pad_input(o.squeeze(0), indices, batch_size, q_len)
302
+
303
+ return o, None, past_key_values
fla/layers/nsa.py ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ import torch
8
+ import torch.nn as nn
9
+ from einops import rearrange
10
+ from transformers.utils import logging
11
+
12
+ from fla.modules import RotaryEmbedding
13
+ from fla.ops.nsa.parallel import parallel_nsa
14
+ from fla.ops.utils.index import prepare_lens_from_mask
15
+
16
+ if TYPE_CHECKING:
17
+ from fla.models.utils import Cache
18
+
19
+ logger = logging.get_logger(__name__)
20
+
21
+
22
+ class NativeSparseAttention(nn.Module):
23
+
24
+ def __init__(
25
+ self,
26
+ hidden_size: int = 2048,
27
+ num_heads: int = 64,
28
+ num_kv_heads: int | None = 4,
29
+ head_dim: int = 64,
30
+ qkv_bias: bool = False,
31
+ block_size: int | None = 64,
32
+ block_counts: torch.LongTensor | int | None = 16,
33
+ window_size: int | None = 512,
34
+ rope_theta: float | None = 10000.,
35
+ max_position_embeddings: int | None = None,
36
+ layer_idx: int = None,
37
+ ):
38
+ super().__init__()
39
+
40
+ self.hidden_size = hidden_size
41
+ self.num_heads = num_heads
42
+ if num_kv_heads is None:
43
+ self.num_kv_heads = self.num_heads
44
+ else:
45
+ self.num_kv_heads = num_kv_heads
46
+ self.num_kv_groups = num_heads // self.num_kv_heads
47
+ self.head_dim = head_dim
48
+ self.kv_dim = self.num_kv_heads * self.head_dim
49
+ self.qkv_bias = qkv_bias
50
+
51
+ self.block_size = block_size
52
+ self.block_counts = block_counts
53
+ self.window_size = window_size
54
+ self.rope_theta = rope_theta
55
+ self.max_position_embeddings = max_position_embeddings
56
+ self.layer_idx = layer_idx
57
+
58
+ self.q_proj = nn.Linear(self.hidden_size, self.num_heads * self.head_dim, bias=self.qkv_bias)
59
+ self.k_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=self.qkv_bias)
60
+ self.v_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=self.qkv_bias)
61
+ self.g_proj = nn.Linear(self.hidden_size, self.num_heads * 3, bias=False)
62
+ self.o_proj = nn.Linear(self.num_heads * self.head_dim, self.hidden_size, bias=False)
63
+
64
+ self.rotary = RotaryEmbedding(dim=self.head_dim, base=self.rope_theta)
65
+
66
+ def forward(
67
+ self,
68
+ hidden_states: torch.Tensor,
69
+ attention_mask: torch.LongTensor | None = None,
70
+ past_key_values: Cache | None = None,
71
+ output_attentions: bool = False,
72
+ use_cache: bool = False,
73
+ **kwargs,
74
+ ) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
75
+ if attention_mask is not None:
76
+ assert len(attention_mask.shape) == 2, (
77
+ "Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
78
+ "for padding purposes (0 indicating padding). "
79
+ "Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
80
+ )
81
+
82
+ batch_size, seq_len, _ = hidden_states.size()
83
+
84
+ q = rearrange(self.q_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
85
+ k = rearrange(self.k_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
86
+ v = rearrange(self.v_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
87
+ g = rearrange(self.g_proj(hidden_states), '... (h d) -> ... h d', d=3)
88
+ g_cmp, g_slc, g_swa = g.sigmoid().unbind(-1)
89
+
90
+ cu_seqlens = kwargs.get('cu_seqlens')
91
+
92
+ seqlen_offset, max_seqlen = 0, seq_len
93
+ if past_key_values is not None:
94
+ seqlen_offset = past_key_values.get_seq_length(self.layer_idx)
95
+ max_seqlen = q.shape[1] + seqlen_offset
96
+
97
+ if attention_mask is not None:
98
+ # to deliminate the offsets of padding tokens
99
+ seqlen_offset = seqlen_offset + prepare_lens_from_mask(attention_mask) - attention_mask.shape[-1]
100
+ max_seqlen = q.shape[1] + max(seqlen_offset)
101
+
102
+ if self.max_position_embeddings is not None:
103
+ max_seqlen = max(max_seqlen, self.max_position_embeddings)
104
+ q, k = self.rotary(q, k, seqlen_offset=seqlen_offset, max_seqlen=max_seqlen, cu_seqlens=cu_seqlens)
105
+
106
+ if past_key_values is not None:
107
+ cache_has_content = past_key_values.get_seq_length(self.layer_idx) > 0
108
+ k_cached, v_cached = past_key_values.update(
109
+ attn_state=(k.flatten(-2, -1), v.flatten(-2, -1)),
110
+ layer_idx=self.layer_idx,
111
+ offset=seq_len,
112
+ cache_kwargs=dict(window_size=self.window_size),
113
+ )['attn_state']
114
+ if cache_has_content:
115
+ k, v = k_cached, v_cached
116
+ k = rearrange(k, '... (h d) -> ... h d', d=self.head_dim)
117
+ v = rearrange(v, '... (h d) -> ... h d', d=self.head_dim)
118
+
119
+ o = parallel_nsa(
120
+ q=q,
121
+ k=k,
122
+ v=v,
123
+ g_cmp=g_cmp,
124
+ g_slc=g_slc,
125
+ g_swa=g_swa,
126
+ block_size=self.block_size,
127
+ block_counts=self.block_counts,
128
+ window_size=self.window_size,
129
+ cu_seqlens=cu_seqlens,
130
+ )
131
+ o = o.reshape(batch_size, seq_len, -1)
132
+ o = self.o_proj(o)
133
+
134
+ if not output_attentions:
135
+ attentions = None
136
+
137
+ return o, attentions, past_key_values
fla/layers/path_attn.py ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ import torch
8
+ import torch.nn as nn
9
+ import torch.nn.functional as F
10
+ from einops import rearrange
11
+ from transformers.utils import logging
12
+
13
+ from fla.layers.utils import pad_input, unpad_input
14
+ from fla.modules import RMSNorm, ShortConvolution
15
+ from fla.modules.l2norm import l2_norm
16
+ from fla.ops.attn.decoding import attn_decoding_one_step
17
+ from fla.ops.path_attn.parallel import parallel_path_attn
18
+
19
+ if TYPE_CHECKING:
20
+ from fla.models.utils import Cache
21
+
22
+ logger = logging.get_logger(__name__)
23
+
24
+
25
+ class PaTHAttention(nn.Module):
26
+ def __init__(
27
+ self,
28
+ hidden_size: int = 2048,
29
+ num_heads: int = 32,
30
+ num_kv_heads: int | None = None,
31
+ use_forget_gate: bool = False,
32
+ use_qk_norm: bool = False,
33
+ layer_idx: int = None,
34
+ use_low_rank_w: bool = True,
35
+ use_w_shortconv: bool = True,
36
+ conv_size: int = 3,
37
+ conv_bias: bool = False,
38
+ ):
39
+ super().__init__()
40
+
41
+ self.hidden_size = hidden_size
42
+ self.num_heads = num_heads
43
+ if num_kv_heads is None:
44
+ self.num_kv_heads = self.num_heads
45
+ else:
46
+ self.num_kv_heads = num_kv_heads
47
+ self.head_dim = self.hidden_size // self.num_heads
48
+ self.kv_dim = self.num_kv_heads * self.head_dim
49
+
50
+ self.layer_idx = layer_idx
51
+
52
+ self.q_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=False)
53
+ self.k_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=False)
54
+ self.v_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=False)
55
+
56
+ # We use low-rank parameterization for the w_proj to reduce parameters in MHA settings.
57
+ if use_low_rank_w:
58
+ self.w_proj = nn.Sequential(
59
+ nn.Linear(self.hidden_size, 32, bias=False),
60
+ nn.Linear(32, self.kv_dim, bias=False),
61
+ )
62
+ # In MQA/GQA settings, key/value heads are shared, so we use a standard linear projection
63
+ # which doesn't introduce too many parameters
64
+ else:
65
+ self.w_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=False)
66
+
67
+ # per head norm
68
+ if use_qk_norm:
69
+ self.maybe_q_norm = RMSNorm(self.head_dim, dtype=torch.float32)
70
+ self.maybe_k_norm = RMSNorm(self.head_dim, dtype=torch.float32)
71
+ else:
72
+ self.maybe_q_norm = nn.Identity()
73
+ self.maybe_k_norm = nn.Identity()
74
+
75
+ if use_w_shortconv:
76
+ self.w_conv1d = ShortConvolution(hidden_size=self.kv_dim, kernel_size=conv_size, bias=conv_bias, activation='silu')
77
+ self.use_w_shortconv = use_w_shortconv
78
+ self.bt_proj = nn.Linear(self.hidden_size, self.num_kv_heads, bias=True)
79
+ self.use_forget_gate = use_forget_gate
80
+ if use_forget_gate:
81
+ self.g_proj = nn.Linear(self.hidden_size, self.num_heads, bias=True)
82
+ self.o_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=False)
83
+
84
+ def forward(
85
+ self,
86
+ hidden_states: torch.Tensor,
87
+ attention_mask: torch.LongTensor | None = None,
88
+ past_key_values: Cache | None = None,
89
+ output_attentions: bool = False,
90
+ use_cache: bool = False,
91
+ **kwargs,
92
+ ) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
93
+ if use_cache:
94
+ assert past_key_values is not None, "past_key_values must be provided when use_cache is True"
95
+ if attention_mask is not None:
96
+ assert len(attention_mask.shape) == 2, (
97
+ "Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
98
+ "for padding purposes (0 indicating padding). "
99
+ "Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
100
+ )
101
+ batch_size, q_len, _ = hidden_states.size()
102
+ q = self.q_proj(hidden_states)
103
+ k = self.k_proj(hidden_states)
104
+ v = self.v_proj(hidden_states)
105
+ w = self.w_proj(hidden_states)
106
+ beta = self.bt_proj(hidden_states).float().sigmoid() * 2 # allowing negative eigenvalues
107
+ g = F.logsigmoid(self.g_proj(hidden_states).float()) if self.use_forget_gate else None
108
+ cu_seqlens = kwargs.get('cu_seqlens')
109
+ assert not (cu_seqlens is not None and attention_mask is not None), (
110
+ "cu_seqlens should not be provided when attention_mask is not None"
111
+ )
112
+ # Training
113
+ if attention_mask is None:
114
+ assert use_cache is False, "use_cache should be False in training"
115
+ if self.use_w_shortconv:
116
+ w, _ = self.w_conv1d(w, cache=None, output_final_state=False, cu_seqlens=cu_seqlens)
117
+ q = rearrange(q, '... (h d) -> ... h d', d=self.head_dim)
118
+ k = rearrange(k, '... (h d) -> ... h d', d=self.head_dim)
119
+ v = rearrange(v, '... (h d) -> ... h d', d=self.head_dim)
120
+ w = rearrange(w, '... (h d) -> ... h d', d=self.head_dim)
121
+ q, k = self.maybe_q_norm(q), self.maybe_k_norm(k)
122
+ w = l2_norm(w, output_dtype=torch.float32)
123
+ o, _ = parallel_path_attn(q=q, k=k, v=v, w=w, beta=beta, g=g, cu_seqlens=cu_seqlens)
124
+
125
+ # Prefilling or decoding
126
+ else:
127
+ assert self.training is False, "attention mask is not supported in training. Please use variable length input."
128
+ try:
129
+ last_state = past_key_values[self.layer_idx]
130
+ except KeyError:
131
+ last_state = None
132
+ # Decoding
133
+ if last_state is not None:
134
+ if g is not None:
135
+ past_k, past_v, past_g = last_state['attn_state']
136
+ else:
137
+ past_k, past_v = last_state['attn_state']
138
+ past_g = None
139
+ w_conv_state = last_state['conv_state']
140
+ past_k = rearrange(past_k, '... (h d) -> ... h d', d=self.head_dim)
141
+ if self.use_w_shortconv:
142
+ w, w_conv_state = self.w_conv1d(w, cache=w_conv_state, output_final_state=use_cache, cu_seqlens=cu_seqlens)
143
+ w = rearrange(w, '... (h d) -> ... h d', d=self.head_dim)
144
+ w = l2_norm(w, output_dtype=torch.float32)
145
+
146
+ @torch.compile
147
+ def rank_one_update(k, w, beta):
148
+ original_dtype = k.dtype
149
+ k = k.float()
150
+ w = w.float()
151
+ beta = beta.float()
152
+ k = k - beta[..., None].float() * (k * w).sum(-1, keepdim=True) * w
153
+ return k.to(original_dtype)
154
+
155
+ past_k = rank_one_update(past_k, w, beta)
156
+ past_k = rearrange(past_k, '... h d -> ... (h d)')
157
+ k = torch.cat([past_k, k], dim=1)
158
+ v = torch.cat([past_v, v], dim=1)
159
+ g = torch.cat([past_g, g], dim=1) if g is not None else None
160
+ past_key_values[self.layer_idx]['attn_state'] = (k, v, g) if g is not None else (k, v)
161
+ past_key_values.update(
162
+ conv_state=w_conv_state,
163
+ layer_idx=self.layer_idx,
164
+ offset=q_len,
165
+ )
166
+ if g is not None:
167
+ q, (k, v, g), indices_q, cu_seqlens, max_seq_lens = unpad_input(
168
+ q, (k, v, g), attention_mask, q_len, keepdim=True)
169
+ max_seqlen_q, max_seqlen_k = max_seq_lens
170
+ else:
171
+ q, (k, v), indices_q, cu_seqlens, max_seq_lens = unpad_input(
172
+ q, (k, v), attention_mask, q_len, keepdim=True)
173
+ max_seqlen_q, max_seqlen_k = max_seq_lens
174
+ _, cu_seqlens = cu_seqlens
175
+ q = rearrange(q, '... (h d) -> ... h d', d=self.head_dim)
176
+ k = rearrange(k, '... (h d) -> ... h d', d=self.head_dim)
177
+ v = rearrange(v, '... (h d) -> ... h d', d=self.head_dim)
178
+ assert max_seqlen_q == 1, "only support q_len == 1 for decoding"
179
+ o = attn_decoding_one_step(q, k, v, g, cu_seqlens=cu_seqlens, do_gate_scale=True) # reduced to fox's decoding
180
+ # Prefilling
181
+ else:
182
+ v_cache = v.clone()
183
+ g_cache = g.clone() if g is not None else None
184
+ if g is None:
185
+ q, (k, v, w, beta), indices_q, cu_seqlens, max_seq_lens = unpad_input(
186
+ q, (k, v, w, beta), attention_mask, q_len, keepdim=True)
187
+ else:
188
+ q, (k, v, w, beta, g), indices_q, cu_seqlens, max_seq_lens = unpad_input(
189
+ q, (k, v, w, beta, g), attention_mask, q_len, keepdim=True)
190
+ max_seqlen_q, max_seqlen_k = max_seq_lens
191
+ assert max_seqlen_q == max_seqlen_k, "max_seqlen_q should be equal to max_seqlen_k in prefilling"
192
+ _, cu_seqlens = cu_seqlens
193
+ if self.use_w_shortconv:
194
+ w, w_conv_state = self.w_conv1d(w, cache=None, output_final_state=use_cache, cu_seqlens=cu_seqlens)
195
+ else:
196
+ w_conv_state = None
197
+ q = rearrange(q, '... (h d) -> ... h d', d=self.head_dim)
198
+ k = rearrange(k, '... (h d) -> ... h d', d=self.head_dim)
199
+ v = rearrange(v, '... (h d) -> ... h d', d=self.head_dim)
200
+ w = rearrange(w, '... (h d) -> ... h d', d=self.head_dim)
201
+ w = l2_norm(w, output_dtype=torch.float32)
202
+ o, k_cache = parallel_path_attn(q=q, k=k, v=v, w=w, beta=beta, g=g,
203
+ cu_seqlens=cu_seqlens, use_cache=use_cache)
204
+ if use_cache:
205
+ k_cache = pad_input(k_cache.squeeze(0), indices_q, batch_size, q_len)
206
+ k_cache = rearrange(k_cache, '... h d -> ... (h d)')
207
+ past_key_values.update(
208
+ attn_state=(k_cache, v_cache, g_cache) if g_cache is not None else (k_cache, v_cache),
209
+ conv_state=w_conv_state,
210
+ layer_idx=self.layer_idx,
211
+ offset=q_len,
212
+ )
213
+ o = pad_input(o.squeeze(0), indices_q, batch_size, q_len)
214
+ o = rearrange(o, '... h d -> ... (h d)')
215
+ o = self.o_proj(o)
216
+ return o, None, past_key_values
fla/layers/quasar.py ADDED
@@ -0,0 +1,439 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+ # Modified for QuasarAttention
3
+
4
+ from __future__ import annotations
5
+
6
+ import contextlib
7
+ import math
8
+ import os
9
+ from typing import TYPE_CHECKING
10
+
11
+ import torch
12
+ import torch.nn as nn
13
+ from einops import rearrange, repeat
14
+ from torch.nn import functional as F
15
+
16
+ from fla.layers.utils import get_unpad_data, index_first_axis, pad_input
17
+
18
+
19
+ def _quasar_debug_tensor(name: str, tensor: torch.Tensor, layer_idx: int | None) -> None:
20
+ if os.environ.get("QUASAR_DEBUG_FINITE", "0") != "1":
21
+ return
22
+ if tensor is None or torch.isfinite(tensor).all():
23
+ return
24
+ with torch.no_grad():
25
+ t = torch.nan_to_num(tensor.detach().float(), nan=0.0, posinf=0.0, neginf=0.0)
26
+ nonfinite = int((~torch.isfinite(tensor)).sum().item())
27
+ print(
28
+ f"[QUASAR DEBUG] layer={layer_idx} stage={name} nonfinite={nonfinite} "
29
+ f"min={float(t.min())} max={float(t.max())} mean={float(t.mean())}",
30
+ flush=True,
31
+ )
32
+
33
+
34
+ class _TorchRMSNormGated(nn.Module):
35
+ def __init__(self, hidden_size: int, activation: str = "sigmoid", eps: float = 1e-5):
36
+ super().__init__()
37
+ self.weight = nn.Parameter(torch.ones(hidden_size))
38
+ self.activation = activation
39
+ self.eps = eps
40
+
41
+ def reset_parameters(self) -> None:
42
+ self.weight.data.fill_(1.0)
43
+
44
+ def forward(self, x: torch.Tensor, gate: torch.Tensor) -> torch.Tensor:
45
+ dtype = x.dtype
46
+ y = torch.nan_to_num(
47
+ x.float(),
48
+ nan=0.0,
49
+ posinf=1e4,
50
+ neginf=-1e4,
51
+ ).clamp_(min=-1e4, max=1e4)
52
+ y = y * torch.rsqrt(y.square().mean(dim=-1, keepdim=True) + self.eps)
53
+ weight = torch.nan_to_num(
54
+ self.weight.float(),
55
+ nan=1.0,
56
+ posinf=1.0,
57
+ neginf=1.0,
58
+ ).clamp_(min=0.0, max=4.0)
59
+ y = y.to(dtype) * weight.to(dtype=dtype, device=x.device)
60
+ gate = torch.nan_to_num(
61
+ gate.float(),
62
+ nan=0.0,
63
+ posinf=30.0,
64
+ neginf=-30.0,
65
+ ).clamp_(min=-30.0, max=30.0)
66
+ if self.activation in {"swish", "silu"}:
67
+ gate = gate * torch.sigmoid(gate)
68
+ elif self.activation == "sigmoid":
69
+ gate = torch.sigmoid(gate)
70
+ return y * gate.to(dtype=dtype, device=x.device)
71
+
72
+
73
+ def rotate_half(x):
74
+ """Rotates half the hidden dims of the input."""
75
+ x1 = x[..., : x.shape[-1] // 2]
76
+ x2 = x[..., x.shape[-1] // 2 :]
77
+ return torch.cat((-x2, x1), dim=-1)
78
+
79
+ def apply_rotary_pos_emb(q, k, cos, sin, position_ids=None):
80
+ """Applies Rotary Position Embedding to the query and key tensors."""
81
+ # cos, sin: [1, 1, seq_len, rotary_dim]
82
+ # q, k: [batch_size, seq_len, n_heads, head_dim]
83
+ rotary_dim = cos.shape[-1]
84
+ q_rot, q_pass = q[..., :rotary_dim], q[..., rotary_dim:]
85
+ k_rot, k_pass = k[..., :rotary_dim], k[..., rotary_dim:]
86
+ cos = cos.transpose(1, 2) # [1, seq_len, 1, rotary_dim]
87
+ sin = sin.transpose(1, 2) # [1, seq_len, 1, rotary_dim]
88
+ q_embed = (q_rot * cos) + (rotate_half(q_rot) * sin)
89
+ k_embed = (k_rot * cos) + (rotate_half(k_rot) * sin)
90
+ return torch.cat([q_embed, q_pass], dim=-1), torch.cat([k_embed, k_pass], dim=-1)
91
+
92
+ if TYPE_CHECKING:
93
+ from transformers.processing_utils import Unpack
94
+
95
+ from fla.models.utils import Cache
96
+
97
+
98
+ class QuasarAttention(nn.Module):
99
+ """
100
+ QuasarAttention layer implementation.
101
+
102
+ Args:
103
+ hidden_size (int, Optional):
104
+ The hidden size of the input. Default: 2048.
105
+ head_dim (int, Optional):
106
+ The dimension of each head. Default: 128.
107
+ num_heads (int, Optional):
108
+ The number of heads. Default: 16.
109
+ mode (str, Optional):
110
+ Which QuasarAttention kernel to use.
111
+ Currently available: `chunk` and `fused_recurrent`.
112
+ Default: `chunk`.
113
+ use_short_conv (bool, Optional):
114
+ Whether to use short convolutions. Default: `True`.
115
+ conv_size (int, Optional):
116
+ The kernel size of the short convolution, only used when `use_short_conv` is `True`. Default: 4.
117
+ conv_bias (bool, Optional):
118
+ Whether to use bias in the short convolution, only used when `use_short_conv` is `True`. Default: `False`.
119
+ layer_idx (int, Optional):
120
+ The index of the layer. Default: None.
121
+ norm_eps (float, Optional):
122
+ The epsilon value for the normalization layer. Default: 1e-5.
123
+ """
124
+
125
+ def __init__(
126
+ self,
127
+ hidden_size: int = 2048,
128
+ head_dim: int = 128,
129
+ num_heads: int = 16,
130
+ mode: str = "chunk",
131
+ use_short_conv: bool = True,
132
+ conv_size: int = 4,
133
+ conv_bias: bool = False,
134
+ layer_idx: int = None,
135
+ norm_eps: float = 1e-5,
136
+ **kwargs,
137
+ ) -> QuasarAttention:
138
+ super().__init__()
139
+
140
+ self.mode = mode
141
+ self.hidden_size = hidden_size
142
+
143
+ self.use_short_conv = use_short_conv
144
+ self.conv_size = conv_size
145
+ self.conv_bias = conv_bias
146
+
147
+ self.head_dim = head_dim
148
+ self.num_heads = num_heads
149
+ self.key_dim = int(self.num_heads * self.head_dim)
150
+ self.value_dim = int(self.num_heads * self.head_dim)
151
+ self.layer_idx = layer_idx
152
+
153
+ assert mode in ["chunk", "fused_recurrent"], f"Not supported mode `{mode}`."
154
+
155
+ self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
156
+ self.k_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
157
+ self.v_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
158
+
159
+ # KDA matching: Use SiLU on q, k, v for better learning if not using short conv
160
+ # (Short conv already has its own activation)
161
+ self.q_act = nn.SiLU()
162
+ self.k_act = nn.SiLU()
163
+ self.v_act = nn.SiLU()
164
+
165
+ if use_short_conv:
166
+ from fla.modules.convolution import ShortConvolution
167
+
168
+ self.q_conv1d = ShortConvolution(
169
+ hidden_size=self.key_dim,
170
+ kernel_size=conv_size,
171
+ bias=conv_bias,
172
+ activation="silu",
173
+ )
174
+ self.k_conv1d = ShortConvolution(
175
+ hidden_size=self.key_dim,
176
+ kernel_size=conv_size,
177
+ bias=conv_bias,
178
+ activation="silu",
179
+ )
180
+ self.v_conv1d = ShortConvolution(
181
+ hidden_size=self.value_dim,
182
+ kernel_size=conv_size,
183
+ bias=conv_bias,
184
+ activation="silu",
185
+ )
186
+
187
+ # Data-dependent Beta (Adaptive Decay)
188
+ # Instead of a static per-head parameter, we use a linear projection
189
+ # to allow the model to learn contextual importance (read/write sharpness).
190
+ self.b_proj = nn.Linear(hidden_size, self.num_heads, bias=False)
191
+
192
+ # Learnable state decay (like KDA/Mamba A matrix)
193
+ self.A_log = nn.Parameter(torch.log(torch.empty(self.num_heads, dtype=torch.float32).uniform_(1, 16)))
194
+ self.A_log._no_weight_decay = True
195
+ self.dt_bias = nn.Parameter(torch.zeros(self.key_dim, dtype=torch.float32))
196
+ self.dt_bias._no_weight_decay = True
197
+
198
+ # KIMI matches: separate f_proj for kernel and g_proj for final output gating
199
+ self.f_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
200
+ self.g_proj = nn.Sequential(
201
+ nn.Linear(hidden_size, self.head_dim, bias=False),
202
+ nn.Linear(self.head_dim, self.value_dim, bias=True),
203
+ )
204
+
205
+ self.o_norm = _TorchRMSNormGated(self.head_dim, activation="sigmoid", eps=norm_eps)
206
+ self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
207
+
208
+ def reset_parameters(self) -> None:
209
+ for module in self.children():
210
+ reset = getattr(module, "reset_parameters", None)
211
+ if callable(reset):
212
+ reset()
213
+
214
+ def forward(
215
+ self,
216
+ hidden_states: torch.Tensor,
217
+ attention_mask: torch.Tensor | None = None,
218
+ past_key_values: Cache | None = None,
219
+ use_cache: bool | None = False,
220
+ output_attentions: bool | None = False,
221
+ **kwargs: Unpack[dict],
222
+ ) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
223
+ if attention_mask is not None:
224
+ assert len(attention_mask.shape) == 2, (
225
+ "Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
226
+ "for padding purposes (0 indicating padding). "
227
+ "Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
228
+ )
229
+
230
+ batch_size, q_len, _ = hidden_states.shape
231
+ mode = self.mode
232
+ if self.training and mode == "fused_recurrent":
233
+ # The fused recurrent Quasar path is forward-only in this tree.
234
+ # Training must use the chunk kernel until its backward exists.
235
+ mode = "chunk"
236
+
237
+ # Bailing hidden states can be very large after MoE/FSDP checkpoint
238
+ # restore. Quasar's delta-rule triangular solve is much more sensitive
239
+ # to projection scale than GQA/GLA, so sanitize and RMS-normalize only
240
+ # the Quasar branch input. The residual model path remains untouched.
241
+ input_dtype = hidden_states.dtype
242
+ hidden_states = torch.nan_to_num(
243
+ hidden_states.float(),
244
+ nan=0.0,
245
+ posinf=60.0,
246
+ neginf=-60.0,
247
+ ).clamp_(min=-60.0, max=60.0)
248
+ hidden_states = hidden_states * torch.rsqrt(
249
+ hidden_states.square().mean(dim=-1, keepdim=True) + 1e-6
250
+ )
251
+ hidden_states = hidden_states.to(dtype=input_dtype)
252
+ _quasar_debug_tensor("input_normed", hidden_states, self.layer_idx)
253
+
254
+ last_state = None
255
+ recurrent_state = None
256
+ conv_state_q, conv_state_k, conv_state_v = None, None, None
257
+
258
+ if past_key_values is not None and self.layer_idx is not None:
259
+ if hasattr(past_key_values, "recurrent_states") and self.layer_idx in past_key_values.recurrent_states:
260
+ recurrent_state = past_key_values.recurrent_states[self.layer_idx]
261
+ if hasattr(past_key_values, "conv_states") and self.layer_idx in past_key_values.conv_states:
262
+ conv_state_q, conv_state_k, conv_state_v = past_key_values.conv_states[self.layer_idx]
263
+ else:
264
+ try:
265
+ # Standard list/tuple cache (FLA style fallback)
266
+ if len(past_key_values) > self.layer_idx:
267
+ last_state = past_key_values[self.layer_idx]
268
+ if isinstance(last_state, dict):
269
+ recurrent_state = last_state.get("recurrent_state", None)
270
+ convs = last_state.get("conv_state", None)
271
+ if convs is not None:
272
+ conv_state_q, conv_state_k, conv_state_v = convs
273
+ except TypeError:
274
+ pass
275
+
276
+ cu_seqlens = kwargs.get("cu_seqlens")
277
+ if attention_mask is not None:
278
+ # Optimization: Skip unpadding if all tokens are valid (common in packed distillation)
279
+ if attention_mask.all():
280
+ indices, cu_seqlens = None, None
281
+ else:
282
+ indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
283
+ hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
284
+ else:
285
+ indices = None
286
+
287
+ if self.use_short_conv:
288
+ q, conv_state_q = self.q_conv1d(
289
+ x=self.q_proj(hidden_states),
290
+ cache=conv_state_q,
291
+ output_final_state=use_cache,
292
+ cu_seqlens=cu_seqlens,
293
+ )
294
+ k, conv_state_k = self.k_conv1d(
295
+ x=self.k_proj(hidden_states),
296
+ cache=conv_state_k,
297
+ output_final_state=use_cache,
298
+ cu_seqlens=cu_seqlens,
299
+ )
300
+ v, conv_state_v = self.v_conv1d(
301
+ x=self.v_proj(hidden_states),
302
+ cache=conv_state_v,
303
+ output_final_state=use_cache,
304
+ cu_seqlens=cu_seqlens,
305
+ )
306
+ else:
307
+ q = self.q_act(self.q_proj(hidden_states))
308
+ k = self.k_act(self.k_proj(hidden_states))
309
+ v = self.v_act(self.v_proj(hidden_states))
310
+ _quasar_debug_tensor("q_proj", q, self.layer_idx)
311
+ _quasar_debug_tensor("k_proj", k, self.layer_idx)
312
+ _quasar_debug_tensor("v_proj", v, self.layer_idx)
313
+
314
+ q = rearrange(q, "... (h d) -> ... h d", d=self.head_dim)
315
+ k = rearrange(k, "... (h d) -> ... h d", d=self.head_dim)
316
+ v = rearrange(v, "... (h d) -> ... h d", d=self.head_dim)
317
+
318
+ # Apply RoPE if provided
319
+ cos = kwargs.get("cos")
320
+ sin = kwargs.get("sin")
321
+ if cos is not None and sin is not None:
322
+ if attention_mask is not None:
323
+ # Unpad cos/sin using the same indices
324
+ # cos/sin shape is [1, 1, seq_len, head_dim] or [batch_size, seq_len, head_dim]
325
+ if cos.shape[0] == 1 and cos.shape[1] == 1:
326
+ # Broadcastable/Shared RoPE [1, 1, seq_len, head_dim]
327
+ # We need to expand to [batch_size, seq_len, head_dim] before unpadding
328
+ cos_expanded = cos.squeeze(1).expand(batch_size, -1, -1)
329
+ sin_expanded = sin.squeeze(1).expand(batch_size, -1, -1)
330
+ cos = index_first_axis(rearrange(cos_expanded, "b s d -> (b s) d"), indices).unsqueeze(0).unsqueeze(1)
331
+ sin = index_first_axis(rearrange(sin_expanded, "b s d -> (b s) d"), indices).unsqueeze(0).unsqueeze(1)
332
+ else:
333
+ # Already [batch_size, 1, seq_len, head_dim] or [batch_size, seq_len, head_dim]
334
+ if cos.dim() == 4:
335
+ cos = cos.squeeze(1)
336
+ sin = sin.squeeze(1)
337
+ cos = index_first_axis(rearrange(cos, "b s d -> (b s) d"), indices).unsqueeze(0).unsqueeze(1)
338
+ sin = index_first_axis(rearrange(sin, "b s d -> (b s) d"), indices).unsqueeze(0).unsqueeze(1)
339
+
340
+ q, k = apply_rotary_pos_emb(q, k, cos, sin)
341
+
342
+ # QK Normalization AFTER RoPE — ensures kernel receives unit-norm vectors
343
+ # regardless of any precision drift introduced by the rotation
344
+ q = F.normalize(q, p=2, dim=-1)
345
+ k = F.normalize(k, p=2, dim=-1)
346
+ _quasar_debug_tensor("q_norm", q, self.layer_idx)
347
+ _quasar_debug_tensor("k_norm", k, self.layer_idx)
348
+
349
+ # Adaptive Beta: Sigmoid(b_proj(x)) is bounded to (0, 1) to prevent explosions.
350
+ beta = self.b_proj(hidden_states).sigmoid()
351
+ _quasar_debug_tensor("beta", beta, self.layer_idx)
352
+
353
+ if mode == "chunk":
354
+ from fla.ops.quasar.chunk import chunk_quasar
355
+
356
+ o, recurrent_state = chunk_quasar(
357
+ q=q,
358
+ k=k,
359
+ v=v,
360
+ beta=beta,
361
+ A_log=self.A_log,
362
+ dt_bias=self.dt_bias,
363
+ initial_state=recurrent_state,
364
+ output_final_state=use_cache,
365
+ cu_seqlens=cu_seqlens,
366
+ use_qk_l2norm_in_kernel=True,
367
+ )
368
+ _quasar_debug_tensor("chunk_kernel_o", o, self.layer_idx)
369
+ elif mode == "fused_recurrent":
370
+ from fla.ops.quasar.fused_recurrent import fused_recurrent_quasar
371
+
372
+ # Use f_proj for kernel gate in fused mode
373
+ f_gate = self.f_proj(hidden_states)
374
+ f_gate = rearrange(f_gate, "... (h d) -> ... h d", d=self.head_dim)
375
+ o, recurrent_state = fused_recurrent_quasar(
376
+ q=q,
377
+ k=k,
378
+ v=v,
379
+ g=f_gate,
380
+ beta=beta,
381
+ A_log=self.A_log,
382
+ dt_bias=self.dt_bias,
383
+ initial_state=recurrent_state,
384
+ output_final_state=use_cache,
385
+ use_qk_l2norm_in_kernel=True,
386
+ )
387
+ _quasar_debug_tensor("fused_kernel_o", o, self.layer_idx)
388
+ else:
389
+ raise NotImplementedError(f"Not supported mode `{mode}`.")
390
+
391
+ o = torch.nan_to_num(
392
+ o.float(),
393
+ nan=0.0,
394
+ posinf=1e4,
395
+ neginf=-1e4,
396
+ ).clamp_(min=-1e4, max=1e4).to(dtype=v.dtype)
397
+ _quasar_debug_tensor("kernel_o_clamped", o, self.layer_idx)
398
+
399
+ if past_key_values is not None:
400
+ if hasattr(past_key_values, "update_quasar_state"):
401
+ past_key_values.update_quasar_state(
402
+ self.layer_idx,
403
+ recurrent_state,
404
+ (conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None
405
+ )
406
+ else:
407
+ with contextlib.suppress(TypeError):
408
+ past_key_values.update(
409
+ recurrent_state=recurrent_state,
410
+ conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
411
+ layer_idx=self.layer_idx,
412
+ offset=q_len,
413
+ )
414
+
415
+ # Final output gating using g_proj
416
+ # Handle flattened inputs (unpadded) from FSDP/Flash-Linear-Attention
417
+ if hidden_states.dim() == 2:
418
+ # (N, D) -> (N, H, D/H)
419
+ g = self.g_proj(hidden_states)
420
+ g = rearrange(g, "n (h d) -> n h d", d=self.head_dim)
421
+ _quasar_debug_tensor("output_gate", g, self.layer_idx)
422
+ o = self.o_norm(o, g)
423
+ o = rearrange(o, "n h d -> n (h d)")
424
+ else:
425
+ # (B, S, D) -> (B, S, H, D/H)
426
+ g = self.g_proj(hidden_states)
427
+ g = rearrange(g, "b s (h d) -> b s h d", d=self.head_dim)
428
+ _quasar_debug_tensor("output_gate", g, self.layer_idx)
429
+ o = self.o_norm(o, g)
430
+ o = rearrange(o, "b s h d -> b s (h d)")
431
+ _quasar_debug_tensor("post_norm_gate", o, self.layer_idx)
432
+
433
+ o = self.o_proj(o)
434
+ _quasar_debug_tensor("o_proj", o, self.layer_idx)
435
+ if attention_mask is not None:
436
+ o = pad_input(o.squeeze(0), indices, batch_size, q_len)
437
+
438
+ # LFM2 expects 2 return values (hidden_states, _)
439
+ return o, None
fla/layers/rebased.py ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+
3
+ """
4
+ https://github.com/corl-team/rebased/blob/main/flash_linear_attention/fla/layers/rebased_fast.py
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import torch
10
+ import torch.nn as nn
11
+ from einops import rearrange
12
+
13
+ from fla.modules.feature_map import RebasedFeatureMap
14
+ from fla.ops.linear_attn import chunk_linear_attn, fused_chunk_linear_attn
15
+ from fla.ops.rebased import parallel_rebased
16
+
17
+
18
+ class ReBasedLinearAttention(nn.Module):
19
+
20
+ def __init__(
21
+ self,
22
+ hidden_size: int,
23
+ l_max: int = 2048,
24
+ feature_dim: int = 16,
25
+ num_key_value_heads: int = 16,
26
+ num_heads: int = 16,
27
+ use_gamma: bool | None = True,
28
+ use_beta: bool | None = True,
29
+ normalize: bool | None = True,
30
+ causal: bool = True,
31
+ eps: float = 1e-5,
32
+ mode: str = "parallel",
33
+ layer_idx: int | None = None,
34
+ **kwargs,
35
+ ) -> ReBasedLinearAttention:
36
+ super().__init__()
37
+ self.hidden_size = hidden_size
38
+ self.l_max = l_max
39
+ self.mode = mode
40
+ assert self.mode in ["fused_chunk", "parallel", 'chunk']
41
+
42
+ self.feature_dim = feature_dim
43
+ self.num_key_value_heads = num_key_value_heads
44
+ self.num_heads = num_heads
45
+ self.head_dim = self.hidden_size // self.num_key_value_heads
46
+ self.use_gamma = use_gamma
47
+ self.use_beta = use_beta
48
+ self.normalize = normalize
49
+ self.causal = causal
50
+ self.eps = eps
51
+ self.mode = mode
52
+ self.layer_idx = layer_idx
53
+
54
+ self.feature_map = RebasedFeatureMap(self.feature_dim, use_gamma, use_beta, normalize)
55
+ self.q_proj = nn.Linear(self.hidden_size, self.feature_dim * self.num_heads, bias=False)
56
+ self.k_proj = nn.Linear(self.hidden_size, self.feature_dim * self.num_heads, bias=False)
57
+ self.v_proj = nn.Linear(self.hidden_size, self.num_key_value_heads * self.head_dim, bias=False)
58
+ self.o_proj = nn.Linear(self.num_heads * self.head_dim, self.hidden_size, bias=False)
59
+ self.dropout = nn.Identity()
60
+
61
+ def forward(self, hidden_states: torch.Tensor, **kwargs):
62
+ mode = self.mode
63
+ q = rearrange(
64
+ self.q_proj(hidden_states),
65
+ "... (h d) -> ... h d",
66
+ h=self.num_heads,
67
+ d=self.feature_dim,
68
+ )
69
+ k = rearrange(
70
+ self.k_proj(hidden_states),
71
+ "... (h d) -> ... h d",
72
+ h=self.num_heads,
73
+ d=self.feature_dim,
74
+ )
75
+ v = rearrange(
76
+ self.v_proj(hidden_states),
77
+ "... (h d) -> ... h d",
78
+ h=self.num_key_value_heads,
79
+ d=self.head_dim,
80
+ )
81
+ q, k = self.feature_map(q, flatten=(mode != 'parallel')), self.feature_map(k, flatten=(mode != 'parallel'))
82
+ if mode == "fused_chunk":
83
+ o = fused_chunk_linear_attn(
84
+ q=q,
85
+ k=k,
86
+ v=v,
87
+ normalize=True,
88
+ scale=1,
89
+ )
90
+ elif mode == 'chunk':
91
+ o = chunk_linear_attn(
92
+ q=q,
93
+ k=k,
94
+ v=v,
95
+ normalize=True,
96
+ scale=1,
97
+ )
98
+ elif mode == 'parallel':
99
+ assert q.shape[-1] <= 128
100
+ o = parallel_rebased(
101
+ q=q,
102
+ k=k,
103
+ v=v,
104
+ eps=self.eps,
105
+ use_scale=True,
106
+ use_normalize=True,
107
+ )
108
+ o = rearrange(o, "... h d -> ... (h d)")
109
+ o = self.o_proj(o)
110
+ o = self.dropout(o)
111
+ return o
112
+
113
+ # https://github.com/HazyResearch/zoology/blob/main/zoology/mixers/based.py#L119
114
+ def forward_reference(
115
+ self,
116
+ hidden_states: torch.Tensor,
117
+ filters: torch.Tensor = None,
118
+ *args,
119
+ **kwargs,
120
+ ):
121
+ """
122
+ x (torch.Tensor): tensor of shape (b, d, t)
123
+ y (torch.Tensor): tensor of shape (b, d, t)
124
+ """
125
+ b, t, _ = hidden_states.size()
126
+ q, k, v = self.q_proj(hidden_states), self.k_proj(hidden_states), self.v_proj(hidden_states)
127
+
128
+ q = q.view(b, t, -1, self.feature_dim).transpose(1, 2)
129
+ k = k.view(b, t, -1, self.feature_dim).transpose(1, 2)
130
+ v = v.view(b, t, -1, self.head_dim).transpose(1, 2)
131
+
132
+ # Linear attention
133
+ q, k = self.feature_map(q), self.feature_map(k)
134
+ q, k, v = q.unsqueeze(-2), k.unsqueeze(-2), v.unsqueeze(-1)
135
+
136
+ # Compute attention
137
+ if self.causal:
138
+ y = ((q * (k * v).cumsum(2)).sum(-1) / ((q * k.cumsum(2)).sum(-1) + self.eps))
139
+ else:
140
+ y = ((q * (k * v).sum(2, True)).sum(-1) / ((q * k.sum(2, True)).sum(-1) + self.eps))
141
+ y = rearrange(y, 'b h t d -> b t (h d)')
142
+ y = self.o_proj(y.to(hidden_states.dtype))
143
+ y = self.dropout(y)
144
+ return y.to(hidden_states.dtype)
fla/layers/rodimus.py ADDED
@@ -0,0 +1,397 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+
3
+ from __future__ import annotations
4
+
5
+ import warnings
6
+ from typing import TYPE_CHECKING
7
+
8
+ import torch
9
+ import torch.nn as nn
10
+ import torch.nn.functional as F
11
+ import torch.utils.checkpoint
12
+ from einops import rearrange, repeat
13
+ from transformers.utils import logging
14
+
15
+ from fla.layers.utils import (
16
+ get_layer_cache,
17
+ get_unpad_data,
18
+ index_first_axis,
19
+ pad_input,
20
+ require_cache_layer_idx,
21
+ unpad_input,
22
+ update_layer_cache,
23
+ )
24
+ from fla.modules import RMSNorm, RotaryEmbedding, ShortConvolution
25
+ from fla.modules.layernorm_gated import RMSNormGated
26
+ from fla.ops.gla import chunk_gla, fused_chunk_gla, fused_recurrent_gla
27
+
28
+ if TYPE_CHECKING:
29
+ from transformers.processing_utils import Unpack
30
+
31
+ from fla.models.utils import Cache
32
+
33
+ try:
34
+ from flash_attn import flash_attn_func, flash_attn_varlen_func
35
+ except ImportError:
36
+ warnings.warn(
37
+ "Flash Attention is not installed. Please install it via `pip install flash-attn --no-build-isolation`",
38
+ category=ImportWarning,
39
+ )
40
+ flash_attn_func = None
41
+
42
+ logger = logging.get_logger(__name__)
43
+
44
+
45
+ def align_multiple(value, multiple_size=8):
46
+ if value % multiple_size != 0:
47
+ value += multiple_size - (value % multiple_size)
48
+ return value
49
+
50
+
51
+ def autocast_to_fp16(x):
52
+ if x.dtype not in {torch.float16, torch.bfloat16}:
53
+ return x.to(dtype=torch.bfloat16)
54
+ else:
55
+ return x
56
+
57
+
58
+ class RodimusAttention(nn.Module):
59
+ def __init__(
60
+ self,
61
+ block_type: str = 'rodimus',
62
+ mode: str = 'chunk',
63
+ hidden_size: int = 1024,
64
+ input_gate_low_rank: float | str | None = 'auto',
65
+ expand_ratio: int = 64,
66
+ use_short_conv: bool = True,
67
+ conv_size: int = 4,
68
+ conv_bias: bool = True,
69
+ norm_eps: float = 1e-5,
70
+ k_norm_eps: float | None = None,
71
+ residual_in_fp32: bool = True,
72
+ layer_idx: int = None,
73
+ ):
74
+ super().__init__()
75
+
76
+ self.block_type = block_type
77
+ self.mode = mode
78
+ self.hidden_size = hidden_size
79
+ self.d_inner = align_multiple(int(self.hidden_size * 2), 8)
80
+
81
+ self.expand_ratio = expand_ratio
82
+ self.input_gate_low_rank = max(self.hidden_size // 64, 16) if input_gate_low_rank == "auto" else input_gate_low_rank
83
+
84
+ self.use_short_conv = use_short_conv
85
+ self.conv_size = conv_size
86
+ self.conv_bias = conv_bias
87
+
88
+ self.norm_eps = norm_eps
89
+ self.k_norm_eps = k_norm_eps if k_norm_eps is not None else 1e-12
90
+ self.mem_size = expand_ratio
91
+
92
+ self.residual_in_fp32 = residual_in_fp32
93
+ self.layer_idx = layer_idx
94
+
95
+ assert mode in ['chunk', 'fused_recurrent', 'fused_chunk'], f"Not supported mode `{mode}`."
96
+
97
+ self.gate_proj = nn.Linear(self.hidden_size, self.d_inner, bias=False)
98
+ self.up_proj = nn.Linear(self.hidden_size, self.d_inner, bias=False)
99
+ self.activation_norm = RMSNormGated(hidden_size=self.d_inner, eps=norm_eps, norm_before_gate=False)
100
+ self.down_proj = nn.Linear(self.d_inner, self.hidden_size, bias=False)
101
+
102
+ if use_short_conv:
103
+ self.short_conv = ShortConvolution(
104
+ hidden_size=self.d_inner,
105
+ kernel_size=conv_size,
106
+ bias=conv_bias,
107
+ activation='silu',
108
+ )
109
+
110
+ self.residual_weight = nn.Parameter(torch.ones(
111
+ (self.d_inner, ), dtype=torch.float32 if self.residual_in_fp32 else None), requires_grad=True)
112
+
113
+ self.k_proj = nn.Linear(self.d_inner, self.mem_size, bias=False)
114
+ self.q_proj = nn.Linear(self.d_inner, self.mem_size, bias=False)
115
+
116
+ self.g_gate_proj = nn.Linear(self.d_inner, self.mem_size, bias=True)
117
+ self.tau_gate_proj = nn.Linear(self.d_inner, self.mem_size, bias=True)
118
+ self.i_gate_proj = nn.Sequential(
119
+ nn.Linear(self.d_inner, self.input_gate_low_rank, bias=False),
120
+ nn.Linear(self.input_gate_low_rank, self.d_inner, bias=True),
121
+ nn.Sigmoid(),
122
+ )
123
+
124
+ def forward(
125
+ self,
126
+ hidden_states: torch.Tensor,
127
+ attention_mask: torch.Tensor | None = None,
128
+ past_key_values: Cache | None = None,
129
+ use_cache: bool | None = False,
130
+ output_attentions: bool | None = False,
131
+ **kwargs: Unpack[dict],
132
+ ) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
133
+ if attention_mask is not None:
134
+ assert len(attention_mask.shape) == 2, (
135
+ "Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
136
+ "for padding purposes (0 indicating padding). "
137
+ "Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
138
+ )
139
+
140
+ batch_size, q_len, _ = hidden_states.shape
141
+ # mode = 'fused_recurrent' if hidden_states.shape[1] <= 64 else self.mode
142
+ mode = 'fused_recurrent' if hidden_states.shape[1] == 1 else self.mode
143
+
144
+ last_state = get_layer_cache(self, past_key_values)
145
+
146
+ cu_seqlens = kwargs.get('cu_seqlens')
147
+ if attention_mask is not None:
148
+ indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
149
+ hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
150
+
151
+ hidden_states, final_gate = self.up_proj(hidden_states), self.gate_proj(hidden_states)
152
+
153
+ if self.use_short_conv:
154
+ conv_state = None
155
+ if last_state is not None:
156
+ conv_state = last_state['conv_state']
157
+ shift_hidden_states, conv_state = self.short_conv(
158
+ x=hidden_states,
159
+ cache=conv_state,
160
+ output_final_state=use_cache,
161
+ cu_seqlens=cu_seqlens,
162
+ )
163
+ else:
164
+ shift_hidden_states = hidden_states
165
+
166
+ q = self.q_proj(shift_hidden_states)
167
+ k = self.k_proj(shift_hidden_states)
168
+ v = self.i_gate_proj(hidden_states) * hidden_states
169
+
170
+ g_gate = F.linear(shift_hidden_states, self.g_gate_proj.weight) + self.g_gate_proj.bias.float()
171
+ tau_gate = F.linear(shift_hidden_states, self.tau_gate_proj.weight) + self.tau_gate_proj.bias.float()
172
+
173
+ g_gate = F.softplus(g_gate)
174
+ it_gate = g_gate
175
+ rt_gate_log = -g_gate
176
+
177
+ tau_gate = F.sigmoid(tau_gate)
178
+ it_gate = it_gate ** tau_gate
179
+ rt_gate_log = rt_gate_log * tau_gate
180
+
181
+ k = F.normalize(k.float(), dim=-1, eps=self.k_norm_eps) * it_gate
182
+ q, k, v, rt_gate_log = map(lambda x: x.unsqueeze(1).transpose(1, 2), (q, k, v, rt_gate_log))
183
+
184
+ recurrent_state = last_state['recurrent_state'] if last_state is not None else None
185
+ if mode == 'fused_recurrent':
186
+ o, recurrent_state = fused_recurrent_gla(
187
+ q=q,
188
+ k=k,
189
+ v=v,
190
+ gk=rt_gate_log,
191
+ initial_state=recurrent_state,
192
+ output_final_state=use_cache,
193
+ cu_seqlens=cu_seqlens,
194
+ head_first=False,
195
+ )
196
+ elif mode == 'fused_chunk':
197
+ o, recurrent_state = fused_chunk_gla(
198
+ q=q,
199
+ k=k,
200
+ v=v,
201
+ g=rt_gate_log,
202
+ initial_state=recurrent_state,
203
+ output_final_state=use_cache,
204
+ head_first=False,
205
+ )
206
+ elif mode == 'chunk':
207
+ q, k, rt_gate_log = map(lambda x: x.to(v.dtype), (q, k, rt_gate_log))
208
+ o, recurrent_state = chunk_gla(
209
+ q=q,
210
+ k=k,
211
+ v=v,
212
+ g=rt_gate_log,
213
+ initial_state=recurrent_state,
214
+ output_final_state=use_cache,
215
+ cu_seqlens=cu_seqlens,
216
+ head_first=False,
217
+ )
218
+ else:
219
+ raise NotImplementedError(f"Not supported mode `{mode}`.")
220
+
221
+ rodimus_caches = None
222
+ if past_key_values is not None:
223
+ if self.block_type == 'rodimus':
224
+ update_layer_cache(
225
+ self,
226
+ past_key_values,
227
+ recurrent_state=recurrent_state,
228
+ conv_state=conv_state if self.use_short_conv else None,
229
+ offset=q_len,
230
+ )
231
+ else:
232
+ rodimus_caches = (recurrent_state, conv_state if self.use_short_conv else None)
233
+
234
+ o = (o.transpose(1, 2).squeeze(1) + (shift_hidden_states.float()
235
+ if self.residual_in_fp32 else shift_hidden_states) * self.residual_weight).to(o.dtype)
236
+
237
+ o = self.activation_norm(o, final_gate)
238
+ o = self.down_proj(o)
239
+
240
+ if attention_mask is not None:
241
+ o = pad_input(o.squeeze(0), indices, batch_size, q_len)
242
+
243
+ if self.block_type == 'rodimus':
244
+ return o, None, past_key_values
245
+ else:
246
+ return o, None, (past_key_values, rodimus_caches)
247
+
248
+
249
+ class SlidingWindowSharedKeyAttention(nn.Module):
250
+ def __init__(
251
+ self,
252
+ hidden_size: int = 2048,
253
+ num_heads: int = 32,
254
+ qkv_bias: bool = False,
255
+ qk_norm: bool = False,
256
+ window_size: int = 2048,
257
+ rope_theta: float | None = 10000.,
258
+ max_position_embeddings: int | None = None,
259
+ layer_idx: int = None,
260
+ ):
261
+ super().__init__()
262
+
263
+ self.hidden_size = hidden_size
264
+ self.num_heads = num_heads
265
+ self.head_dim = self.hidden_size // self.num_heads
266
+ self.qkv_bias = qkv_bias
267
+ self.qk_norm = qk_norm
268
+
269
+ self.window_size = window_size
270
+ self.rope_theta = rope_theta
271
+ self.max_position_embeddings = max_position_embeddings
272
+ self.layer_idx = layer_idx
273
+
274
+ self.q_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=self.qkv_bias)
275
+ self.k_proj = nn.Linear(self.hidden_size, self.head_dim, bias=self.qkv_bias)
276
+ self.v_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=self.qkv_bias)
277
+ self.o_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=False)
278
+
279
+ if qk_norm:
280
+ self.q_norm = RMSNorm(self.head_dim, dtype=torch.float32)
281
+ self.k_norm = RMSNorm(self.head_dim, dtype=torch.float32)
282
+
283
+ self.rotary = RotaryEmbedding(dim=self.head_dim, base=self.rope_theta)
284
+
285
+ def forward(
286
+ self,
287
+ hidden_states: torch.Tensor,
288
+ attention_mask: torch.LongTensor | None = None,
289
+ past_key_values: Cache | None = None,
290
+ output_attentions: bool = False,
291
+ use_cache: bool = False,
292
+ **kwargs,
293
+ ) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
294
+ rodimus_caches = kwargs.get('rodimus_caches')
295
+
296
+ if attention_mask is not None:
297
+ assert len(attention_mask.shape) == 2, (
298
+ "Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
299
+ "for padding purposes (0 indicating padding). "
300
+ "Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
301
+ )
302
+
303
+ batch_size, q_len, _ = hidden_states.size()
304
+
305
+ q = rearrange(self.q_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
306
+ k = rearrange(self.k_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
307
+ v = rearrange(self.v_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
308
+
309
+ if self.qk_norm:
310
+ q, k = self.q_norm(q), self.k_norm(k)
311
+
312
+ # equivalent to cu_seqlens in `flash_attn`
313
+ cu_seqlens = kwargs.get('cu_seqlens')
314
+
315
+ layer_idx = require_cache_layer_idx(self, past_key_values)
316
+ seqlen_offset, max_seqlen = 0, q.shape[1]
317
+ if past_key_values is not None:
318
+ seqlen_offset = past_key_values.get_seq_length(layer_idx)
319
+ max_seqlen = q.shape[1] + seqlen_offset
320
+
321
+ if attention_mask is not None:
322
+ # to deliminate the offsets of padding tokens
323
+ seqlen_offset = seqlen_offset + attention_mask.sum(-1) - attention_mask.shape[-1]
324
+ max_seqlen = q.shape[1] + max(seqlen_offset)
325
+
326
+ if self.max_position_embeddings is not None:
327
+ max_seqlen = max(max_seqlen, self.max_position_embeddings)
328
+ q, k = self.rotary(q, k, seqlen_offset=seqlen_offset, max_seqlen=max_seqlen, cu_seqlens=cu_seqlens)
329
+
330
+ if past_key_values is not None:
331
+ if rodimus_caches is not None:
332
+ recurrent_state, conv_state = rodimus_caches
333
+ else:
334
+ recurrent_state, conv_state = None, None
335
+
336
+ cache_has_content = past_key_values.get_seq_length(layer_idx) > 0
337
+ k_cached, v_cached = past_key_values.update(
338
+ recurrent_state=recurrent_state,
339
+ conv_state=conv_state,
340
+ attn_state=[k.flatten(-2, -1), v.flatten(-2, -1)],
341
+ layer_idx=layer_idx,
342
+ offset=q_len,
343
+ cache_kwargs=dict(window_size=self.window_size),
344
+ )['attn_state']
345
+ if cache_has_content:
346
+ k, v = k_cached, v_cached
347
+ k = rearrange(k, '... (h d) -> ... h d', d=self.head_dim)
348
+ v = rearrange(v, '... (h d) -> ... h d', d=self.head_dim)
349
+
350
+ if flash_attn_func is None:
351
+ raise ImportError("Please install Flash Attention via `pip install flash-attn --no-build-isolation` first")
352
+
353
+ q, k, v = map(autocast_to_fp16, (q, k, v))
354
+ k = repeat(k, "... h d -> ... (n h) d", n=self.num_heads)
355
+ # Contains at least one padding token in the sequence
356
+ if attention_mask is not None:
357
+ q, (k, v), indices_q, cu_seqlens, max_seq_lens = unpad_input(
358
+ q=q,
359
+ states=(k, v),
360
+ attention_mask=attention_mask[:, -max(self.window_size, q_len):],
361
+ q_len=q_len,
362
+ )
363
+ cu_seqlens_q, cu_seqlens_k = cu_seqlens
364
+ max_seqlen_q, max_seqlen_k = max_seq_lens
365
+ o = flash_attn_varlen_func(
366
+ q, k, v,
367
+ cu_seqlens_q=cu_seqlens_q,
368
+ cu_seqlens_k=cu_seqlens_k,
369
+ max_seqlen_q=max_seqlen_q,
370
+ max_seqlen_k=max_seqlen_k,
371
+ causal=True,
372
+ window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
373
+ )
374
+ o = pad_input(o, indices_q, batch_size, q_len)
375
+ elif cu_seqlens is not None:
376
+ o = flash_attn_varlen_func(
377
+ q.squeeze(0), k.squeeze(0), v.squeeze(0),
378
+ cu_seqlens_q=cu_seqlens,
379
+ cu_seqlens_k=cu_seqlens,
380
+ max_seqlen_q=max_seqlen,
381
+ max_seqlen_k=max_seqlen,
382
+ causal=True,
383
+ window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
384
+ ).unsqueeze(0)
385
+ else:
386
+ o = flash_attn_func(
387
+ q, k, v,
388
+ causal=True,
389
+ window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
390
+ )
391
+ o = o.reshape(batch_size, q_len, -1)
392
+ o = self.o_proj(o.to(dtype=self.o_proj.weight.dtype))
393
+
394
+ if not output_attentions:
395
+ attentions = None
396
+
397
+ return o, attentions, past_key_values
fla/layers/rwkv6.py ADDED
@@ -0,0 +1,360 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+
3
+ # "Eagle and Finch: RWKV with Matrix-Valued States and Dynamic Recurrence"[https://arxiv.org/abs/2404.05892]
4
+
5
+ from __future__ import annotations
6
+
7
+ import math
8
+ import warnings
9
+ from typing import TYPE_CHECKING
10
+
11
+ import torch
12
+ import torch.nn as nn
13
+ from einops import rearrange
14
+
15
+ from fla.layers.utils import get_layer_cache, update_layer_cache
16
+ from fla.modules import GroupNorm
17
+ from fla.modules.activations import ACT2FN
18
+ from fla.modules.token_shift import token_shift
19
+ from fla.ops.rwkv6 import chunk_rwkv6, fused_recurrent_rwkv6
20
+
21
+ if TYPE_CHECKING:
22
+ from fla.models.utils import Cache
23
+
24
+
25
+ class RWKV6Attention(nn.Module):
26
+
27
+ def __init__(
28
+ self,
29
+ mode: str = 'chunk',
30
+ hidden_size: int = 1024,
31
+ expand_k: float = 0.5,
32
+ expand_v: float = 1.0,
33
+ num_heads: int = 4,
34
+ gate_fn: str = 'swish',
35
+ proj_low_rank_dim: int = 32,
36
+ gate_low_rank_dim: int = 64,
37
+ fuse_norm: bool = True,
38
+ elementwise_affine: bool | None = True,
39
+ norm_eps: float = 1e-5,
40
+ layer_idx: int = None,
41
+ **kwargs,
42
+ ) -> RWKV6Attention:
43
+ super().__init__()
44
+
45
+ self.mode = mode
46
+ self.hidden_size = hidden_size
47
+ self.expand_k = expand_k
48
+ self.expand_v = expand_v
49
+ self.num_heads = num_heads
50
+ self.proj_low_rank_dim = proj_low_rank_dim
51
+ self.gate_low_rank_dim = gate_low_rank_dim
52
+
53
+ self.key_dim = int(hidden_size * expand_k)
54
+ self.value_dim = int(hidden_size * expand_v)
55
+ self.layer_idx = layer_idx
56
+
57
+ assert mode in ['chunk', 'fused_recurrent'], f"Not supported mode `{mode}`."
58
+ assert self.key_dim % num_heads == 0, f"key dim must be divisible by num_heads of {num_heads}"
59
+ assert self.value_dim % num_heads == 0, f"value dim must be divisible by num_heads of {num_heads}"
60
+
61
+ self.head_k_dim = self.key_dim // num_heads
62
+ self.head_v_dim = self.value_dim // num_heads
63
+
64
+ self.time_shift = nn.ZeroPad2d((0, 0, 1, -1))
65
+ self.x_proj = nn.Sequential(
66
+ LerpLinear(hidden_size, proj_low_rank_dim * 5),
67
+ nn.Tanh(),
68
+ nn.Linear(proj_low_rank_dim * 5, hidden_size, bias=False),
69
+ )
70
+ self.x_bias = nn.Parameter(torch.zeros(5, hidden_size))
71
+
72
+ self.r_proj = DDLerpLinear(hidden_size, self.key_dim)
73
+ self.w_proj = DDLerpLinear(hidden_size, self.key_dim, low_rank_dim=gate_low_rank_dim)
74
+ self.k_proj = DDLerpLinear(hidden_size, self.key_dim)
75
+ self.v_proj = DDLerpLinear(hidden_size, self.value_dim)
76
+ self.g_proj = DDLerpLinear(hidden_size, self.value_dim)
77
+ self.bonus = nn.Parameter(torch.zeros(num_heads, self.head_k_dim))
78
+
79
+ # TODO: fuse GroupNorm and output gate
80
+ self.g_norm = GroupNorm(self.num_heads, self.value_dim, elementwise_affine=elementwise_affine, bias=True, eps=norm_eps)
81
+ self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
82
+ self.gate_fn = ACT2FN[gate_fn]
83
+
84
+ try:
85
+ from transformers.modeling_utils import _init_weights
86
+ except ImportError:
87
+ _init_weights = True
88
+ if _init_weights:
89
+ self.apply(self._initialize_weights)
90
+
91
+ warnings.warn(
92
+ "According to Bo, you are using a potentially buggy FLA implementation of RWKV. "
93
+ "If you plan to report any numbers based on this implementation, we strongly recommend "
94
+ "cross-checking with the official repo: https://github.com/BlinkDL/RWKV-LM. "
95
+ "Bo may disagree with results reported from this version.",
96
+ )
97
+
98
+ def _initialize_weights(self, module: nn.Module):
99
+ if getattr(module, "_is_hf_initialized", False):
100
+ return
101
+ if isinstance(module, nn.Linear):
102
+ nn.init.xavier_uniform_(module.weight, gain=2 ** -2.5)
103
+ if module.bias is not None:
104
+ nn.init.zeros_(module.bias)
105
+ if isinstance(module, nn.Parameter):
106
+ nn.init.xavier_uniform_(module, gain=2 ** -2.5)
107
+ module._is_hf_initialized = True
108
+
109
+ def forward(
110
+ self,
111
+ hidden_states: torch.Tensor,
112
+ attention_mask: torch.Tensor | None = None,
113
+ past_key_values: Cache | None = None,
114
+ use_cache: bool | None = False,
115
+ output_attentions: bool | None = False,
116
+ cu_seqlens: torch.LongTensor | None = None,
117
+ **kwargs,
118
+ ) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
119
+ if attention_mask is not None:
120
+ assert len(attention_mask.shape) == 2, (
121
+ "Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
122
+ "for padding purposes (0 indicating padding). "
123
+ "Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
124
+ )
125
+
126
+ batch_size, seq_len, hidden_size = hidden_states.shape
127
+ # launching the triton kernel for just one token will actually be slower
128
+ mode = 'fused_recurrent' if hidden_states.shape[1] <= 64 else self.mode
129
+
130
+ last_state = get_layer_cache(self, past_key_values)
131
+
132
+ if attention_mask is not None:
133
+ hidden_states = hidden_states.mul(attention_mask[:, -hidden_states.shape[-2]:, None])
134
+
135
+ if hidden_states.shape[1] == 1 and last_state is not None:
136
+ shifted = last_state['conv_state'].unsqueeze(1)
137
+ delta = shifted - hidden_states
138
+ elif last_state is None:
139
+ delta = token_shift(hidden_states, cu_seqlens)
140
+ else:
141
+ shifted = self.time_shift(hidden_states)
142
+ shifted[:, 0] = last_state['conv_state']
143
+ delta = shifted - hidden_states
144
+
145
+ x = self.x_proj[0](hidden_states, delta, cu_seqlens).view(batch_size, seq_len, -1, self.proj_low_rank_dim)
146
+ x = torch.einsum('b t n r, h n r-> b t n h', self.x_proj[1](x), self.x_proj[2].weight.view(hidden_size, 5, -1))
147
+
148
+ r, w, k, v, g = x.add_(self.x_bias).unbind(-2)
149
+ r = self.r_proj(hidden_states, r, delta, cu_seqlens)
150
+ w = self.w_proj(hidden_states, w, delta, cu_seqlens)
151
+ k = self.k_proj(hidden_states, k, delta, cu_seqlens)
152
+ v = self.v_proj(hidden_states, v, delta, cu_seqlens)
153
+ g = self.g_proj(hidden_states, g, delta, cu_seqlens)
154
+
155
+ # dealing with left-padding
156
+ if attention_mask is not None:
157
+ v = v.mul(attention_mask[:, -v.shape[-2]:, None])
158
+ r, w, k = map(lambda x: rearrange(x, 'b t (h d) -> b t h d', d=self.head_k_dim), (r, w, k))
159
+ v = rearrange(v, 'b t (h d) -> b t h d', d=self.head_v_dim)
160
+ w = -torch.exp(w)
161
+ u = self.bonus
162
+
163
+ recurrent_state = last_state['recurrent_state'] if last_state is not None else None
164
+
165
+ if mode == 'fused_recurrent':
166
+ o, recurrent_state = fused_recurrent_rwkv6(
167
+ r=r,
168
+ k=k,
169
+ v=v,
170
+ w=w,
171
+ u=u,
172
+ scale=1.,
173
+ initial_state=recurrent_state,
174
+ output_final_state=use_cache,
175
+ cu_seqlens=cu_seqlens,
176
+ )
177
+ elif mode == 'chunk':
178
+ o, recurrent_state = chunk_rwkv6(
179
+ r=r,
180
+ k=k,
181
+ v=v,
182
+ w=w,
183
+ u=u,
184
+ scale=1.,
185
+ initial_state=recurrent_state,
186
+ output_final_state=use_cache,
187
+ cu_seqlens=cu_seqlens,
188
+ )
189
+ else:
190
+ raise NotImplementedError(f"Not supported mode `{mode}`.")
191
+
192
+ update_layer_cache(
193
+ self,
194
+ past_key_values,
195
+ recurrent_state=recurrent_state,
196
+ conv_state=hidden_states[:, -1],
197
+ offset=seq_len,
198
+ )
199
+
200
+ o = self.g_norm(rearrange(o, '... h d -> ... (h d)')) * self.gate_fn(g)
201
+ o = self.o_proj(o)
202
+
203
+ return o, None, past_key_values
204
+
205
+
206
+ class LoRA(nn.Module):
207
+
208
+ def __init__(
209
+ self,
210
+ input_dim: int,
211
+ output_dim: int,
212
+ low_rank_dim: int,
213
+ bias: bool | None = True,
214
+ activation: str | None = 'tanh',
215
+ ):
216
+ super().__init__()
217
+
218
+ self.input_dim = input_dim
219
+ self.output_dim = output_dim
220
+ self.low_rank_dim = low_rank_dim
221
+ self.bias = bias
222
+
223
+ if activation is None:
224
+ self.activation = nn.Identity()
225
+ elif activation == 'sigmoid':
226
+ self.activation = nn.Sigmoid()
227
+ elif activation == 'tanh':
228
+ self.activation = nn.Tanh()
229
+ elif activation == 'relu':
230
+ self.activation = nn.ReLU()
231
+ else:
232
+ raise ValueError(f"Not supported activation `{activation}`.")
233
+
234
+ self.lora = nn.Sequential(
235
+ nn.Linear(input_dim, low_rank_dim, bias=False),
236
+ self.activation,
237
+ nn.Linear(low_rank_dim, output_dim, bias=bias),
238
+ )
239
+ try:
240
+ from transformers.modeling_utils import _init_weights
241
+ except ImportError:
242
+ _init_weights = True
243
+ if _init_weights:
244
+ self.apply(self._initialize_weights)
245
+
246
+ def __repr__(self) -> str:
247
+ s = f"{self.__class__.__name__}("
248
+ s += f"input_dim={self.input_dim}, low_rank_dim={self.low_rank_dim}, output_dim={self.output_dim}"
249
+ if not self.bias:
250
+ s += f", bias={self.bias}"
251
+ s += ")"
252
+ return s
253
+
254
+ def _initialize_weights(self, module: nn.Module):
255
+ if getattr(module, "_is_hf_initialized", False):
256
+ return
257
+
258
+ # Initialize weights to zero as in original code
259
+ nn.init.zeros_(self.lora[0].weight)
260
+ original_dtype = self.lora[2].weight.dtype
261
+ shape = self.lora[2].weight.shape
262
+ # Convert to float32 for numerical stability in orthogonal init
263
+ weight_fp32 = self.lora[2].weight.float()
264
+
265
+ # Calculate gain based on dimensions
266
+ gain = math.sqrt(shape[1] / shape[0]) if shape[1] > shape[0] else 1
267
+
268
+ # Apply orthogonal initialization with scaling factor 0.1
269
+ nn.init.orthogonal_(weight_fp32, gain=gain * 0.1)
270
+
271
+ # Convert back to original dtype
272
+ self.lora[2].weight.data.copy_(weight_fp32.to(original_dtype))
273
+ # Set Lora[2] bias to zero
274
+ if self.lora[2].bias is not None:
275
+ nn.init.zeros_(self.lora[2].bias)
276
+
277
+ module._is_hf_initialized = True
278
+
279
+ def set_bias_value(self, value):
280
+ """Set bias to a specific value (for v0, w0 etc.)"""
281
+ if self.bias and self.lora[2].bias is not None:
282
+ if isinstance(value, torch.Tensor):
283
+ # Handle tensor values
284
+ self.lora[2].bias.data.copy_(value.to(self.lora[2].bias.dtype))
285
+ else:
286
+ # Handle scalar values
287
+ nn.init.constant_(self.lora[2].bias, value)
288
+
289
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
290
+ return self.lora(x)
291
+
292
+
293
+ class LerpLinear(nn.Module):
294
+
295
+ def __init__(
296
+ self,
297
+ input_dim: int,
298
+ output_dim: int,
299
+ low_rank_dim: int | None = None,
300
+ ):
301
+ super().__init__()
302
+
303
+ self.input_dim = input_dim
304
+ self.output_dim = output_dim
305
+ self.low_rank_dim = low_rank_dim
306
+
307
+ self.time_shift = nn.ZeroPad2d((0, 0, 1, -1))
308
+ if low_rank_dim is None:
309
+ self.linear = nn.Linear(input_dim, output_dim, bias=False)
310
+ else:
311
+ self.linear = LoRA(input_dim, output_dim, low_rank_dim)
312
+ self.mu = nn.Parameter(torch.zeros(input_dim))
313
+
314
+ def __repr__(self) -> str:
315
+ s = f"{self.__class__.__name__}({self.input_dim}, {self.output_dim}"
316
+ if self.low_rank_dim is not None:
317
+ s += f", low_rank_dim={self.low_rank_dim}"
318
+ s += ")"
319
+ return s
320
+
321
+ def forward(self, x: torch.Tensor, delta: torch.Tensor | None = None,
322
+ cu_seqlens: torch.LongTensor | None = None) -> torch.Tensor:
323
+ if delta is None:
324
+ delta = token_shift(x, cu_seqlens)
325
+ return self.linear(x + delta * self.mu)
326
+
327
+
328
+ class DDLerpLinear(nn.Module):
329
+
330
+ def __init__(
331
+ self,
332
+ input_dim: int,
333
+ output_dim: int,
334
+ low_rank_dim: int | None = None,
335
+ ):
336
+ super().__init__()
337
+
338
+ self.input_dim = input_dim
339
+ self.output_dim = output_dim
340
+ self.low_rank_dim = low_rank_dim
341
+
342
+ self.time_shift = nn.ZeroPad2d((0, 0, 1, -1))
343
+ if low_rank_dim is None:
344
+ self.linear = nn.Linear(input_dim, output_dim, bias=False)
345
+ else:
346
+ self.linear = LoRA(input_dim, output_dim, low_rank_dim)
347
+
348
+ def __repr__(self) -> str:
349
+ s = f"{self.__class__.__name__}({self.input_dim}, {self.output_dim}"
350
+ if self.low_rank_dim is not None:
351
+ s += f", low_rank_dim={self.low_rank_dim}"
352
+ s += ")"
353
+ return s
354
+
355
+ def forward(self, x: torch.Tensor, mu: torch.Tensor,
356
+ delta: torch.Tensor | None = None,
357
+ cu_seqlens: torch.LongTensor | None = None) -> torch.Tensor:
358
+ if delta is None:
359
+ delta = token_shift(x, cu_seqlens)
360
+ return self.linear(x + delta * mu)
fla/layers/rwkv7.py ADDED
@@ -0,0 +1,347 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+
3
+ from __future__ import annotations
4
+
5
+ import warnings
6
+ from typing import TYPE_CHECKING
7
+
8
+ import torch
9
+ import torch.nn as nn
10
+ from einops import rearrange
11
+ from torch.nn import functional as F
12
+
13
+ from fla.layers.rwkv6 import LoRA
14
+ from fla.layers.utils import get_layer_cache, update_layer_cache
15
+ from fla.modules import GroupNorm
16
+ from fla.modules.l2norm import l2_norm
17
+ from fla.modules.token_shift import token_shift
18
+ from fla.ops.rwkv7 import chunk_rwkv7, fused_mul_recurrent_rwkv7
19
+ from fla.ops.rwkv7.fused_addcmul import fused_addcmul_rwkv7
20
+ from fla.ops.rwkv7.fused_k_update import fused_k_rwkv7
21
+ from fla.ops.rwkv7.gate_output_correction import gate_output_correction
22
+
23
+ if TYPE_CHECKING:
24
+ from fla.models.utils import Cache
25
+
26
+
27
+ class RWKV7Attention(nn.Module):
28
+
29
+ def __init__(
30
+ self,
31
+ mode: str = 'chunk',
32
+ hidden_size: int = 1024,
33
+ head_dim: int | None = 64,
34
+ num_heads: int | None = None,
35
+ decay_low_rank_dim: int | None = None,
36
+ gate_low_rank_dim: int | None = None,
37
+ a_low_rank_dim: int | None = None,
38
+ v_low_rank_dim: int | None = None,
39
+ elementwise_affine: bool | None = True,
40
+ norm_eps: float = 1e-5,
41
+ layer_idx: int = None,
42
+ fuse_norm: bool = False,
43
+ value_dim: int = None,
44
+ num_hidden_layers: int = None,
45
+ **kwargs,
46
+ ) -> RWKV7Attention:
47
+ super().__init__()
48
+
49
+ self.mode = mode
50
+ assert mode in ['chunk', 'fused_recurrent'], f"Not supported mode `{mode}`."
51
+ self.hidden_size = hidden_size
52
+
53
+ self.key_dim = hidden_size
54
+ self.value_dim = value_dim if value_dim is not None else hidden_size
55
+ if head_dim is None and num_heads is None:
56
+ raise ValueError("Either `head_dim` or `num_heads` must be specified.")
57
+ elif head_dim is not None:
58
+ self.head_dim = head_dim
59
+ self.num_heads = int(hidden_size // head_dim)
60
+ elif num_heads is not None:
61
+ self.head_dim = int(hidden_size // num_heads)
62
+ self.num_heads = num_heads
63
+ self.head_v_dim = int(self.value_dim // self.num_heads)
64
+
65
+ # Increase lora dimension for headdim>64
66
+ factor = self.head_dim / 64
67
+ if decay_low_rank_dim is None:
68
+ decay_low_rank_dim = max(32, int(round((2.5 * (hidden_size**0.5)) * factor / 32) * 32))
69
+ self.decay_low_rank_dim = decay_low_rank_dim
70
+ else:
71
+ self.decay_low_rank_dim = decay_low_rank_dim
72
+
73
+ if gate_low_rank_dim is None:
74
+ gate_low_rank_dim = max(32, int(round((5 * (hidden_size**0.5)) / 32) * 32))
75
+ self.gate_low_rank_dim = gate_low_rank_dim
76
+ else:
77
+ self.gate_low_rank_dim = gate_low_rank_dim
78
+
79
+ if a_low_rank_dim is None:
80
+ a_low_rank_dim = max(32, int(round((2.5 * (hidden_size**0.5)) * factor / 32) * 32))
81
+ self.a_low_rank_dim = a_low_rank_dim
82
+ else:
83
+ self.a_low_rank_dim = a_low_rank_dim
84
+
85
+ if v_low_rank_dim is None:
86
+ v_low_rank_dim = max(32, int(round((1.7 * (hidden_size**0.5)) * factor / 32) * 32))
87
+ self.v_low_rank_dim = v_low_rank_dim
88
+ else:
89
+ self.v_low_rank_dim = v_low_rank_dim
90
+
91
+ self.layer_idx = layer_idx
92
+ self.num_hidden_layers = num_hidden_layers
93
+ self.fuse_norm = fuse_norm
94
+
95
+ self.time_shift = nn.ZeroPad2d((0, 0, 1, -1))
96
+ self.x_r = nn.Parameter(torch.zeros(1, 1, hidden_size))
97
+ self.x_w = nn.Parameter(torch.zeros(1, 1, hidden_size))
98
+ self.x_k = nn.Parameter(torch.zeros(1, 1, hidden_size))
99
+ self.x_v = nn.Parameter(torch.zeros(1, 1, hidden_size))
100
+ self.x_a = nn.Parameter(torch.zeros(1, 1, hidden_size))
101
+ self.x_g = nn.Parameter(torch.zeros(1, 1, hidden_size))
102
+
103
+ self.k_k = nn.Parameter(torch.zeros(self.key_dim))
104
+ self.k_a = nn.Parameter(torch.zeros(self.key_dim))
105
+ self.r_k = nn.Parameter(torch.zeros(self.num_heads, self.head_dim))
106
+
107
+ self.r_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
108
+ self.k_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
109
+ self.v_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
110
+ self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
111
+
112
+ self.w_lora = LoRA(hidden_size, self.key_dim, low_rank_dim=decay_low_rank_dim, activation='tanh')
113
+ if self.layer_idx != 0:
114
+ self.v_lora = LoRA(hidden_size, self.value_dim, low_rank_dim=v_low_rank_dim, activation=None)
115
+ self.a_lora = LoRA(hidden_size, self.key_dim, low_rank_dim=a_low_rank_dim, activation=None)
116
+ self.g_lora = LoRA(hidden_size, self.value_dim, low_rank_dim=gate_low_rank_dim, activation='sigmoid', bias=False)
117
+
118
+ if self.fuse_norm:
119
+ self.g_norm = GroupNorm(
120
+ num_groups=self.num_heads,
121
+ hidden_size=self.value_dim,
122
+ elementwise_affine=elementwise_affine,
123
+ eps=self.head_dim*norm_eps,
124
+ bias=True,
125
+ )
126
+ else:
127
+ self.g_norm = nn.GroupNorm(
128
+ num_groups=self.num_heads,
129
+ num_channels=self.value_dim,
130
+ eps=self.head_dim*norm_eps,
131
+ affine=elementwise_affine,
132
+ )
133
+
134
+ try:
135
+ from transformers.modeling_utils import _init_weights
136
+ except ImportError:
137
+ _init_weights = True
138
+ if _init_weights:
139
+ self.apply(self._initialize_weights)
140
+ for name, module in self.named_modules():
141
+ module._in_rwkv_module = True
142
+
143
+ warnings.warn(
144
+ "According to Bo, you are using a potentially buggy FLA implementation of RWKV. "
145
+ "If you plan to report any numbers based on this implementation, we strongly recommend "
146
+ "cross-checking with the official repo: https://github.com/BlinkDL/RWKV-LM. "
147
+ "Bo may disagree with results reported from this version.",
148
+ )
149
+
150
+ @torch.no_grad()
151
+ @torch.compiler.disable
152
+ def _initialize_weights(self, module: nn.Module):
153
+ if getattr(module, "_is_hf_initialized", False):
154
+ return
155
+
156
+ # Initialize only when we're processing the RWKV7Attention module itself
157
+ if isinstance(module, RWKV7Attention) and self.layer_idx is not None:
158
+ ratio_0_to_1 = self.layer_idx / (self.num_hidden_layers - 1) # 0 to 1
159
+ ratio_1_to_almost0 = 1.0 - (self.layer_idx / self.num_hidden_layers) # 1 to ~0
160
+
161
+ # Create position-based initialization tensor
162
+ ddd = torch.ones(1, 1, self.hidden_size, device=self.x_r.device)
163
+ www = torch.zeros(self.hidden_size, device=self.x_r.device)
164
+ zigzag = torch.zeros(self.hidden_size, device=self.x_r.device)
165
+ linear = torch.zeros(self.hidden_size, device=self.x_r.device)
166
+ for n in range(self.hidden_size):
167
+ linear[n] = n / (self.hidden_size-1) - 0.5
168
+ zigzag[n] = ((n % self.head_dim) - ((self.head_dim-1) / 2)) / ((self.head_dim-1) / 2)
169
+ zigzag[n] = zigzag[n] * abs(zigzag[n])
170
+ www[n] = -6 + 6 * (n / (self.hidden_size - 1)) ** (1 + 1 * ratio_0_to_1 ** 0.3)
171
+ ddd[0, 0, n] = n / self.hidden_size
172
+
173
+ # Initialize x_* parameters directly
174
+ self.x_r.data = (1.0 - torch.pow(ddd, 0.2 * ratio_1_to_almost0)).to(self.x_r.dtype)
175
+ self.x_w.data = (1.0 - torch.pow(ddd, 0.9 * ratio_1_to_almost0)).to(self.x_w.dtype)
176
+ self.x_k.data = (1.0 - torch.pow(ddd, 0.7 * ratio_1_to_almost0)).to(self.x_k.dtype)
177
+ self.x_v.data = (1.0 - torch.pow(ddd, 0.7 * ratio_1_to_almost0)).to(self.x_v.dtype)
178
+ self.x_a.data = (1.0 - torch.pow(ddd, 0.9 * ratio_1_to_almost0)).to(self.x_a.dtype)
179
+ self.x_g.data = (1.0 - torch.pow(ddd, 0.2 * ratio_1_to_almost0)).to(self.x_g.dtype)
180
+
181
+ # Initialize k_k, k_a, r_k
182
+ nn.init.constant_(self.k_a, 1.02)
183
+ nn.init.constant_(self.r_k, -0.04)
184
+ self.k_k.data.copy_((torch.zeros(self.hidden_size, device=self.k_k.device) +
185
+ 0.71 - linear*0.1).to(self.k_k.dtype))
186
+ # Set specific bias values for LoRA modules
187
+ # 0.5 comes from F.softplus
188
+ self.w_lora.set_bias_value(www + 0.5 + zigzag*2.5)
189
+ self.a_lora.set_bias_value(-0.19 + zigzag*0.3 + linear*0.4)
190
+
191
+ # v0 initialization - ones (for non-first layers)
192
+ if self.layer_idx != 0:
193
+ self.v_lora._initialize_weights(self.v_lora)
194
+ self.v_lora.set_bias_value(0.73 - linear*0.4)
195
+
196
+ # Initialize GroupNorm
197
+ self.g_norm.weight.data[:] = ((self.layer_idx + 1) / self.num_hidden_layers) ** 0.7
198
+
199
+ # Initialize Linear projections
200
+ self._orthogonal_init(self.r_proj.weight)
201
+ self._orthogonal_init(self.k_proj.weight, gain=0.1)
202
+ self._orthogonal_init(self.v_proj.weight)
203
+ self.o_proj.weight.data.zero_()
204
+
205
+ # Clean up temporary tensors to free memory
206
+ del ddd, www, zigzag, linear
207
+
208
+ module._is_hf_initialized = True
209
+
210
+ @staticmethod
211
+ def _orthogonal_init(weight, gain=1.0):
212
+ oringinal_dtype = weight.dtype
213
+ weight = weight.float()
214
+ nn.init.orthogonal_(weight, gain=gain)
215
+ weight = weight.to(oringinal_dtype)
216
+
217
+ def forward(
218
+ self,
219
+ hidden_states: torch.Tensor,
220
+ attention_mask: torch.Tensor | None = None,
221
+ past_key_values: Cache | None = None,
222
+ use_cache: bool | None = False,
223
+ output_attentions: bool | None = False,
224
+ v_first: torch.Tensor = None,
225
+ cu_seqlens: torch.LongTensor | None = None,
226
+ **kwargs,
227
+ ) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
228
+ batch_size, seq_len, _ = hidden_states.shape
229
+ if attention_mask is not None:
230
+ assert len(attention_mask.shape) == 2, (
231
+ "Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
232
+ "for padding purposes (0 indicating padding). "
233
+ "Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
234
+ )
235
+ am = attention_mask.narrow(1, attention_mask.size(1) - seq_len, seq_len).unsqueeze(-1)
236
+
237
+ last_state = get_layer_cache(self, past_key_values)
238
+
239
+ if attention_mask is not None:
240
+ hidden_states = hidden_states.mul(am)
241
+
242
+ # delta [batch_size, seq_len, hidden_size]
243
+ # conv_cache [N, D]
244
+ if last_state is None:
245
+ conv_cache = None
246
+ recurrent_state = None
247
+ else:
248
+ conv_cache = last_state['conv_state']
249
+ recurrent_state = last_state['recurrent_state']
250
+
251
+ delta, conv_state = token_shift(
252
+ hidden_states, cu_seqlens, output_cache=True, cache=conv_cache,
253
+ )
254
+ xr, xw, xk, xv, xa, xg = fused_addcmul_rwkv7(hidden_states, delta, self.x_r, self.x_w,
255
+ self.x_k, self.x_v, self.x_a, self.x_g)
256
+
257
+ r = self.r_proj(xr)
258
+ # Using bf16 for LoRA computation is numerically safe here because:
259
+ # 1. After sigmoid activation:
260
+ # - Max absolute error (vs float32): 0.003
261
+ # - Mean absolute error: 0.0004
262
+ # 2. Subsequent scaling by -0.6065 will further reduce relative error
263
+ # (error scales linearly with constant multiplication)
264
+ # 3. Final compounded error remains within acceptable bounds for bf16 precision
265
+ # Empirical observation confirms bf16 introduces no practical degradation
266
+ w = -0.6065306597126334 * self.w_lora(xw).sigmoid()
267
+
268
+ k = self.k_proj(xk)
269
+ v = self.v_proj(xv)
270
+
271
+ if self.layer_idx == 0:
272
+ v_first = v
273
+ else:
274
+ v = torch.lerp(v, v_first, self.v_lora(xv).sigmoid())
275
+ a = self.a_lora(xa).sigmoid()
276
+ g = self.g_lora(xg)
277
+
278
+ if self.fuse_norm:
279
+ kk = l2_norm(rearrange(k * self.k_k, 'b t (h d) -> b t h d', d=self.head_dim))
280
+ else:
281
+ kk = F.normalize(rearrange(k * self.k_k, 'b t (h d) -> b t h d', d=self.head_dim), dim=-1, p=2.0)
282
+
283
+ # Prefer addcmul over expanded form for numerical stability in bf16:
284
+ # 1. Fused Multiply-Add (FMA) in addcmul reduces intermediate rounding:
285
+ # - Single op vs original 3 ops (mul, sub, mul)
286
+ # - 1 less intermediate value storage (bf16 write->read overhead)
287
+ # 2. Mathematically equivalent to k*(1 + (a-1)*self.k_a)
288
+ # but with better precision preservation
289
+ # 3. Particularly crucial for bf16 where intermediate values easily lose precision
290
+ # 4. Pytorch method: k = k.addcmul(k * (a - 1), self.k_a)
291
+ k = fused_k_rwkv7(k, a, self.k_a)
292
+
293
+ # dealing with left-padding
294
+ if attention_mask is not None:
295
+ v = v * am
296
+
297
+ r, w, k, a = map(lambda x: rearrange(x, 'b t (h d) -> b t h d', d=self.head_dim), (r, w, k, a))
298
+ v = rearrange(v, 'b t (h d) -> b t h d', d=self.head_v_dim)
299
+
300
+ if self.training or seq_len >= 64:
301
+ # if training, use chunk mode no matter how short the sequence is
302
+ # launching the triton kernel for just one token will actually be slower
303
+ o, recurrent_state = chunk_rwkv7(
304
+ r=r,
305
+ w=w,
306
+ k=k,
307
+ v=v,
308
+ a=-kk,
309
+ b=kk * a,
310
+ scale=1.,
311
+ initial_state=recurrent_state,
312
+ output_final_state=use_cache,
313
+ cu_seqlens=cu_seqlens,
314
+ safe_gate=True,
315
+ chunk_size=64,
316
+ )
317
+ else:
318
+ o, recurrent_state = fused_mul_recurrent_rwkv7(
319
+ r=r,
320
+ w=w,
321
+ k=k,
322
+ v=v,
323
+ kk=kk,
324
+ a=a,
325
+ scale=1.,
326
+ initial_state=recurrent_state,
327
+ output_final_state=use_cache,
328
+ cu_seqlens=cu_seqlens,
329
+ )
330
+
331
+ update_layer_cache(
332
+ self,
333
+ past_key_values,
334
+ recurrent_state=recurrent_state,
335
+ conv_state=conv_state,
336
+ offset=r.shape[1],
337
+ )
338
+
339
+ if self.fuse_norm:
340
+ o = self.g_norm(rearrange(o, '... h d -> ... (h d)'))
341
+ else:
342
+ o = self.g_norm(rearrange(o, 'b t h d -> (b t) (h d)')).view(batch_size, seq_len, -1)
343
+
344
+ o = gate_output_correction(o, r, k, self.r_k, v, g)
345
+ o = self.o_proj(o)
346
+
347
+ return o, None, past_key_values, v_first
fla/layers/simple_gla.py ADDED
@@ -0,0 +1,273 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ import torch
8
+ import torch.nn as nn
9
+ import torch.nn.functional as F
10
+ from einops import rearrange, repeat
11
+
12
+ from fla.layers.utils import get_layer_cache, update_layer_cache
13
+ from fla.modules import FusedRMSNormGated, RMSNorm, ShortConvolution
14
+ from fla.modules.activations import ACT2FN
15
+ from fla.ops.simple_gla import chunk_simple_gla, fused_recurrent_simple_gla
16
+
17
+ if TYPE_CHECKING:
18
+ from fla.models.utils import Cache
19
+
20
+
21
+ class SimpleGatedLinearAttention(nn.Module):
22
+ r"""
23
+ The layer implementaion for [Gated Linear Attention Transformers with Hardware-Efficient Training](https://arxiv.org/abs/2312.06635). # noqa
24
+ This layer calls the simplified GLA kernel in which the gating is head-wise instead of elementwise.
25
+
26
+ Args:
27
+ mode (str, Optional):
28
+ Which GLA kernel to use.
29
+ Currently available: `chunk`.
30
+ Default: `chunk`.
31
+ hidden_size (int, Optional):
32
+ The hidden size of the input. Default: 1024.
33
+ expand_k (float, Optional):
34
+ The expansion ratio for the key dim. Default: 1.0.
35
+ expand_v (float, Optional):
36
+ The expansion ratio for the value dim. Default: 1.0.
37
+ num_heads (int, Optional):
38
+ The number of heads. Default: 4.
39
+ num_kv_heads (int, Optional):
40
+ The number of key/value heads, used for MQA. Default: None.
41
+ feature_map (str, Optional):
42
+ Feature map function applied to queries/keys. Default: None.
43
+ use_short_conv (bool, Optional):
44
+ Whether to use short convolutions. Default: `False`.
45
+ conv_size (int, Optional):
46
+ The kernel size of the short convolution, only used when `use_short_conv` is `True`. Default: 4.
47
+ conv_bias (bool, Optional):
48
+ Whether to use bias in the short convolution, only used when `use_short_conv` is `True`. Default: `False`.
49
+ gate_fn (str, Optional):
50
+ The activation function for the output gate. Default: `swish`.
51
+ elementwise_affine (bool, Optional):
52
+ If `True`, applies elementwise affine to LayerNorm with learnable parameters. Default: `True`.
53
+ norm_eps (float, Optional):
54
+ The epsilon value for the layernorm/rmsnorm layer. Default: 1e-5.
55
+ gate_logit_normalizer (int, Optional):
56
+ The normalizer for the gate logits, appied after `logsigmoid`. Default: 16.
57
+ fuse_norm (bool, Optional):
58
+ Whether to fuse the norm and the output gate for better memory footprint. Default: `True`.
59
+ layer_idx (int, Optional):
60
+ The index of the layer. Default: None.
61
+ """
62
+
63
+ def __init__(
64
+ self,
65
+ mode: str = 'chunk',
66
+ hidden_size: int = 1024,
67
+ expand_k: float = 1.,
68
+ expand_v: float = 1.,
69
+ num_heads: int = 4,
70
+ num_kv_heads: int | None = None,
71
+ feature_map: str | None = None,
72
+ use_short_conv: bool = True,
73
+ conv_size: int = 4,
74
+ conv_bias: bool = False,
75
+ gate_fn: str = 'swish',
76
+ elementwise_affine: bool | None = True,
77
+ norm_eps: float = 1e-5,
78
+ gate_logit_normalizer: int = 16,
79
+ fuse_norm: bool = True,
80
+ layer_idx: int = None,
81
+ ) -> SimpleGatedLinearAttention:
82
+ super().__init__()
83
+
84
+ self.mode = mode
85
+ self.hidden_size = hidden_size
86
+ self.expand_k = expand_k
87
+ self.expand_v = expand_v
88
+ self.num_heads = num_heads
89
+ self.num_kv_heads = num_kv_heads if num_kv_heads is not None else num_heads
90
+ self.num_kv_groups = self.num_heads // self.num_kv_heads
91
+ self.feature_map_fn = ACT2FN[feature_map] if feature_map is not None else None
92
+
93
+ self.use_short_conv = use_short_conv
94
+ self.conv_size = conv_size
95
+ self.conv_bias = conv_bias
96
+
97
+ self.key_dim = int(hidden_size * expand_k)
98
+ self.value_dim = int(hidden_size * expand_v)
99
+ self.key_dim_per_group = self.key_dim // self.num_kv_groups
100
+ self.value_dim_per_group = self.value_dim // self.num_kv_groups
101
+ self.layer_idx = layer_idx
102
+
103
+ assert mode in ['chunk', "fused_recurrent"], f"Not supported mode `{mode}`."
104
+ assert self.key_dim % num_heads == 0, f"key dim must be divisible by num_heads of {num_heads}"
105
+ assert self.value_dim % num_heads == 0, f"value dim must be divisible by num_heads of {num_heads}"
106
+
107
+ self.head_k_dim = self.key_dim // num_heads
108
+ self.head_v_dim = self.value_dim // num_heads
109
+
110
+ self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
111
+ self.k_proj = nn.Linear(hidden_size, self.key_dim_per_group, bias=False)
112
+ self.v_proj = nn.Linear(hidden_size, self.value_dim_per_group, bias=False)
113
+ self.g_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
114
+
115
+ if use_short_conv:
116
+ self.conv_size = conv_size
117
+ self.q_conv1d = ShortConvolution(
118
+ hidden_size=self.key_dim,
119
+ kernel_size=conv_size,
120
+ bias=conv_bias,
121
+ activation='silu',
122
+ )
123
+ self.k_conv1d = ShortConvolution(
124
+ hidden_size=self.key_dim_per_group,
125
+ kernel_size=conv_size,
126
+ bias=conv_bias,
127
+ activation='silu',
128
+ )
129
+ self.v_conv1d = ShortConvolution(
130
+ hidden_size=self.value_dim_per_group,
131
+ kernel_size=conv_size,
132
+ bias=conv_bias,
133
+ activation='silu',
134
+ )
135
+
136
+ self.gk_proj = nn.Linear(hidden_size, self.num_heads)
137
+
138
+ if gate_fn == 'swish' and fuse_norm:
139
+ self.g_norm_swish_gate = FusedRMSNormGated(
140
+ hidden_size=self.head_v_dim,
141
+ elementwise_affine=elementwise_affine,
142
+ eps=norm_eps,
143
+ )
144
+ self.fuse_norm_and_gate = True
145
+ else:
146
+ self.fuse_norm_and_gate = False
147
+ self.g_norm = RMSNorm(
148
+ hidden_size=self.head_v_dim,
149
+ elementwise_affine=elementwise_affine,
150
+ eps=norm_eps,
151
+ dtype=torch.float32
152
+ )
153
+ self.gate_fn = ACT2FN[gate_fn]
154
+ self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
155
+
156
+ self.gate_logit_normalizer = gate_logit_normalizer
157
+
158
+ def forward(
159
+ self,
160
+ hidden_states: torch.Tensor,
161
+ attention_mask: torch.Tensor | None = None,
162
+ past_key_values: Cache | None = None,
163
+ use_cache: bool | None = False,
164
+ output_attentions: bool | None = False,
165
+ **kwargs,
166
+ ) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
167
+ if attention_mask is not None:
168
+ assert len(attention_mask.shape) == 2, (
169
+ "Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
170
+ "for padding purposes (0 indicating padding). "
171
+ "Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
172
+ )
173
+
174
+ # launching the triton kernel for just one token will actually be slower
175
+ mode = 'fused_recurrent' if hidden_states.shape[1] <= 64 else self.mode
176
+
177
+ last_state = get_layer_cache(self, past_key_values)
178
+
179
+ cu_seqlens = kwargs.get('cu_seqlens')
180
+ if self.use_short_conv:
181
+ conv_state_q, conv_state_k, conv_state_v = None, None, None
182
+ if last_state is not None:
183
+ conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
184
+ conv_mask = attention_mask[:, -hidden_states.shape[1]:] if attention_mask is not None else None
185
+ q, conv_state_q = self.q_conv1d(
186
+ x=self.q_proj(hidden_states),
187
+ mask=conv_mask,
188
+ cache=conv_state_q,
189
+ output_final_state=use_cache,
190
+ cu_seqlens=cu_seqlens,
191
+ )
192
+ k, conv_state_k = self.k_conv1d(
193
+ x=self.k_proj(hidden_states),
194
+ mask=conv_mask,
195
+ cache=conv_state_k,
196
+ output_final_state=use_cache,
197
+ cu_seqlens=cu_seqlens,
198
+ )
199
+ v, conv_state_v = self.v_conv1d(
200
+ x=self.v_proj(hidden_states),
201
+ mask=conv_mask,
202
+ cache=conv_state_v,
203
+ output_final_state=use_cache,
204
+ cu_seqlens=cu_seqlens,
205
+ )
206
+ else:
207
+ q = self.q_proj(hidden_states)
208
+ k = self.k_proj(hidden_states)
209
+ v = self.v_proj(hidden_states)
210
+ gk = self.gk_proj(hidden_states)
211
+
212
+ if self.feature_map_fn is not None:
213
+ q, k = map(self.feature_map_fn, (q, k))
214
+ # dealing with left-padding
215
+ if attention_mask is not None:
216
+ v = v.mul_(attention_mask[:, -v.shape[-2]:, None])
217
+ q = rearrange(q, '... (h d) -> ... h d', h=self.num_heads)
218
+ if self.num_kv_groups > 1:
219
+ k, v = (repeat(x, '... (h d) -> ... (h g) d', h=self.num_kv_heads, g=self.num_kv_groups) for x in (k, v))
220
+ else:
221
+ k, v = (rearrange(x, '... (h d) -> ... h d', h=self.num_kv_heads) for x in (k, v))
222
+ gk = F.logsigmoid(gk) / self.gate_logit_normalizer
223
+
224
+ recurrent_state = last_state['recurrent_state'] if last_state is not None else None
225
+ if mode == 'chunk':
226
+ o, recurrent_state = chunk_simple_gla(
227
+ q=q,
228
+ k=k,
229
+ v=v,
230
+ g=gk,
231
+ initial_state=recurrent_state,
232
+ output_final_state=use_cache,
233
+ cu_seqlens=cu_seqlens,
234
+ )
235
+ elif mode == 'fused_recurrent':
236
+ o, recurrent_state = fused_recurrent_simple_gla(
237
+ q=q,
238
+ k=k,
239
+ v=v,
240
+ g=gk,
241
+ initial_state=recurrent_state,
242
+ output_final_state=use_cache,
243
+ cu_seqlens=cu_seqlens,
244
+ )
245
+ else:
246
+ raise NotImplementedError(f"Not supported mode `{mode}`.")
247
+
248
+ update_layer_cache(
249
+ self,
250
+ past_key_values,
251
+ recurrent_state=recurrent_state,
252
+ conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
253
+ offset=q.shape[1],
254
+ )
255
+
256
+ g = self.g_proj(hidden_states)
257
+ if self.fuse_norm_and_gate:
258
+ g = rearrange(g, 'b t (h d) -> b t h d', h=self.num_heads)
259
+ o = self.g_norm_swish_gate(o, g)
260
+ o = rearrange(o, 'b t h d -> b t (h d)')
261
+ else:
262
+ o = rearrange(self.g_norm(o), 'b t h d -> b t (h d)')
263
+ o = o * self.gate_fn(g)
264
+ o = self.o_proj(o)
265
+
266
+ return o, None, past_key_values
267
+
268
+ def state_size(self, **kwargs) -> int:
269
+ state_size = self.key_dim * self.head_v_dim
270
+ for module in self.children():
271
+ if isinstance(module, ShortConvolution):
272
+ state_size += module.state_size
273
+ return state_size
fla/layers/utils.py ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
2
+
3
+ # Code is adapted from flash-attn.bert_padding.py
4
+
5
+
6
+ import torch
7
+ from einops import rearrange, repeat
8
+
9
+ from fla.ops.utils.index import prepare_cu_seqlens_from_mask, prepare_lens_from_mask
10
+ from fla.utils import tensor_cache
11
+
12
+ _LAYER_IDX_REQUIRED_MSG = "{cls} requires `layer_idx` when `past_key_values` is provided."
13
+
14
+
15
+ class IndexFirstAxis(torch.autograd.Function):
16
+
17
+ @staticmethod
18
+ def forward(ctx, x, indices):
19
+ ctx.save_for_backward(indices)
20
+ assert x.ndim >= 2
21
+ ctx.first_axis_dim, other_shape = x.shape[0], x.shape[1:]
22
+ second_dim = other_shape.numel()
23
+ # TD [2022-03-04] For some reason torch.gather is a bit faster than indexing.
24
+ # return x[indices]
25
+ return torch.gather(
26
+ rearrange(x, "b ... -> b (...)"), 0, repeat(indices, "z -> z d", d=second_dim),
27
+ ).reshape(-1, *other_shape)
28
+
29
+ @staticmethod
30
+ def backward(ctx, do):
31
+ (indices,) = ctx.saved_tensors
32
+ assert do.ndim >= 2
33
+ other_shape = do.shape[1:]
34
+ do = rearrange(do, "b ... -> b (...)")
35
+ dx = torch.zeros(
36
+ [ctx.first_axis_dim, do.shape[1]],
37
+ device=do.device,
38
+ dtype=do.dtype,
39
+ )
40
+ # TD [2022-03-04] For some reason torch.scatter is a bit faster than indexing.
41
+ # dx[indices] = do
42
+ dx.scatter_(0, repeat(indices, "z -> z d", d=do.shape[1]), do)
43
+ return dx.reshape(ctx.first_axis_dim, *other_shape), None
44
+
45
+
46
+ index_first_axis = IndexFirstAxis.apply
47
+
48
+
49
+ class IndexPutFirstAxis(torch.autograd.Function):
50
+
51
+ @staticmethod
52
+ def forward(ctx, x, indices, first_axis_dim):
53
+ ctx.save_for_backward(indices)
54
+ assert indices.ndim == 1
55
+ assert x.ndim >= 2
56
+ y = torch.zeros(first_axis_dim, *x.shape[1:], device=x.device, dtype=x.dtype)
57
+ # TODO [2022-03-04] For some reason torch.scatter is a bit faster than indexing.
58
+ y[indices] = x
59
+ # y.scatter_(0, repeat(indices, 'z -> z d', d=x.shape[1]), x)
60
+ return y
61
+
62
+ @staticmethod
63
+ def backward(ctx, do):
64
+ (indices,) = ctx.saved_tensors
65
+ # TODO [2022-03-04] For some reason torch.gather is a bit faster than indexing.
66
+ dx = do[indices]
67
+ # dx = torch.gather(do, 0, repeat(indices, 'z -> z d', d=do.shape[1]))
68
+ return dx, None, None
69
+
70
+
71
+ index_put_first_axis = IndexPutFirstAxis.apply
72
+
73
+
74
+ @tensor_cache
75
+ def get_unpad_data(
76
+ attention_mask: torch.Tensor,
77
+ ) -> tuple[torch.Tensor, torch.Tensor, int]:
78
+ """
79
+ Retrieves indexing data required to repad unpadded (ragged) tensors.
80
+
81
+ Args:
82
+ attention_mask (`torch.Tensor`):
83
+ Boolean or int tensor of shape (batch_size, sequence_length), 1 means valid and 0 means not valid.
84
+
85
+ Return:
86
+ indices (`torch.Tensor`):
87
+ The indices of non-masked tokens from the flattened input sequence.
88
+ cu_seqlens (`torch.Tensor`):
89
+ The cumulative sequence lengths, used to index into ragged (unpadded) tensors.
90
+ `cu_seqlens` shape is [batch_size + 1].
91
+ max_seqlen_in_batch (`int`):
92
+ Maximum sequence length in batch.
93
+ """
94
+ lens = prepare_lens_from_mask(attention_mask)
95
+ indices = torch.nonzero(attention_mask.flatten(), as_tuple=False).flatten()
96
+ max_seqlen_in_batch = lens.max().item()
97
+ cu_seqlens = prepare_cu_seqlens_from_mask(attention_mask)
98
+ return indices, cu_seqlens, max_seqlen_in_batch
99
+
100
+
101
+ def unpad_input(
102
+ q: torch.Tensor,
103
+ states: tuple[torch.Tensor],
104
+ attention_mask: torch.Tensor,
105
+ q_len: int,
106
+ keepdim: bool = False,
107
+ ):
108
+ """
109
+ Unpads query, key, and values tensors, using a single dimension for all tokens
110
+ even though they belong to different batches.
111
+
112
+
113
+ Arguments:
114
+ q (`torch.Tensor`):
115
+ Query state with padding. Shape: [batch_size, q_len, ...].
116
+ states (`Tuple[torch.Tensor]`):
117
+ Attention state with padding. Shape: [batch_size, seq_len, ...].
118
+ attention_mask (`torch.Tensor`):
119
+ Boolean or int tensor of shape [batch_size, sequence_length], 1 means valid and 0 means not valid.
120
+ q_len (`int`):
121
+ Target length.
122
+ keepdim (`bool`):
123
+ Whether to keep the batch dimension. Default: `False`.
124
+
125
+ Return:
126
+ q (`torch.Tensor`):
127
+ Query state without padding.
128
+ Shape: [1, total_target_length, ...] if `keepdim=True` else [total_target_length, ...].
129
+ states (`Tuple[torch.Tensor]`):
130
+ Attention state without padding.
131
+ Shape: [1, total_source_length, ...] if `keepdim=True` else [total_source_length, ...].
132
+ indices_q (`torch.Tensor`):
133
+ The indices of non-masked tokens from the flattened input target sequence.
134
+ (cu_seqlens_q, cu_seqlens_k) (`Tuple[int]`):
135
+ The cumulative sequence lengths for the target (query) and source (key, value),
136
+ used to index into ragged (unpadded) tensors.
137
+ `cu_seqlens` shape is [batch_size + 1].
138
+ (max_seqlen_in_batch_q, max_seqlen_in_batch_k) (`Tuple[int]`):
139
+ Maximum sequence length in batch (`max_seqlen_in_batch_q` for the target sequence
140
+ i.e. query, `max_seqlen_in_batch_k` for the source sequence i.e. key/value).
141
+ """
142
+ indices_k, cu_seqlens_k, max_seqlen_in_batch_k = get_unpad_data(attention_mask)
143
+ batch_size, seq_len, *_ = states[0].shape
144
+
145
+ state = tuple(
146
+ index_first_axis(rearrange(s, "b s ... -> (b s) ..."), indices_k)
147
+ for s in states
148
+ )
149
+
150
+ if q_len == seq_len:
151
+ q = index_first_axis(rearrange(q, "b s ... -> (b s) ..."), indices_k)
152
+ cu_seqlens_q = cu_seqlens_k
153
+ max_seqlen_in_batch_q = max_seqlen_in_batch_k
154
+ indices_q = indices_k
155
+ elif q_len == 1:
156
+ max_seqlen_in_batch_q = 1
157
+ cu_seqlens_q = torch.arange(batch_size + 1, dtype=torch.int32, device=q.device)
158
+ indices_q = cu_seqlens_q[:-1]
159
+ q = q.squeeze(1)
160
+ else:
161
+ raise NotImplementedError("We only support either q_len == k_len (prefilling) or q_len == 1 (decoding)")
162
+
163
+ if keepdim:
164
+ q = q.unsqueeze(0)
165
+ state = tuple(s.unsqueeze(0) for s in state)
166
+
167
+ return (
168
+ q,
169
+ state,
170
+ indices_q,
171
+ (cu_seqlens_q, cu_seqlens_k),
172
+ (max_seqlen_in_batch_q, max_seqlen_in_batch_k),
173
+ )
174
+
175
+
176
+ def pad_input(
177
+ hidden_states: torch.Tensor,
178
+ indices: torch.LongTensor,
179
+ batch_size: int,
180
+ seq_len: int,
181
+ ) -> torch.Tensor:
182
+ """
183
+ Args:
184
+ hidden_states ([total_tokens, ...]):
185
+ where total_tokens denotes the number of tokens in selected in attention_mask.
186
+ indices ([total_tokens]):
187
+ the indices that represent the non-masked tokens of the original padded input sequence.
188
+ batch_size (int):
189
+ batch_size size for the padded sequence.
190
+ seq_len (int):
191
+ maximum sequence length for the padded sequence.
192
+
193
+ Return:
194
+ hidden_states of shape [batch_size, seq_len, ...]
195
+ """
196
+ output = index_put_first_axis(hidden_states, indices, batch_size * seq_len)
197
+ return rearrange(output, "(b s) ... -> b s ...", b=batch_size)
198
+
199
+
200
+ def require_cache_layer_idx(module, past_key_values):
201
+ layer_idx = getattr(module, "layer_idx", None)
202
+ if past_key_values is not None and layer_idx is None:
203
+ raise ValueError(_LAYER_IDX_REQUIRED_MSG.format(cls=module.__class__.__name__))
204
+ return layer_idx
205
+
206
+
207
+ def get_layer_cache(module, past_key_values):
208
+ layer_idx = require_cache_layer_idx(module, past_key_values)
209
+ if past_key_values is not None and len(past_key_values) > layer_idx:
210
+ return past_key_values[layer_idx]
211
+ return None
212
+
213
+
214
+ def update_layer_cache(module, past_key_values, **kwargs):
215
+ layer_idx = require_cache_layer_idx(module, past_key_values)
216
+ if past_key_values is not None:
217
+ return past_key_values.update(layer_idx=layer_idx, **kwargs)
218
+ return None
fla/models/__init__.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ # Keep model package imports lazy.
2
+ __all__ = []
3
+
fla/models/abc/__init__.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ from transformers import AutoConfig, AutoModel, AutoModelForCausalLM
3
+
4
+ from fla.models.abc.configuration_abc import ABCConfig
5
+ from fla.models.abc.modeling_abc import ABCForCausalLM, ABCModel
6
+
7
+ AutoConfig.register(ABCConfig.model_type, ABCConfig, exist_ok=True)
8
+ AutoModel.register(ABCConfig, ABCModel, exist_ok=True)
9
+ AutoModelForCausalLM.register(ABCConfig, ABCForCausalLM, exist_ok=True)
10
+
11
+
12
+ __all__ = ['ABCConfig', 'ABCForCausalLM', 'ABCModel']
fla/models/abc/configuration_abc.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import warnings
3
+
4
+ from transformers.configuration_utils import PretrainedConfig
5
+
6
+
7
+ class ABCConfig(PretrainedConfig):
8
+
9
+ model_type = 'abc'
10
+ keys_to_ignore_at_inference = ['past_key_values']
11
+
12
+ def __init__(
13
+ self,
14
+ hidden_size: int = 2048,
15
+ gate_low_rank_dim: int = 16,
16
+ clamp_min: float = -32,
17
+ clamp_max: float = 32,
18
+ hidden_ratio: int | None = 4,
19
+ intermediate_size: int | None = None,
20
+ num_hidden_layers: int = 24,
21
+ num_heads: int = 4,
22
+ num_slots: int | None = 64,
23
+ use_short_conv: bool = False,
24
+ conv_size: int = 4,
25
+ exapnd_k: float = 0.5,
26
+ exapnd_v: float = 1,
27
+ hidden_act: str = "swish",
28
+ max_position_embeddings: int = 2048,
29
+ elementwise_affine: bool | None = True,
30
+ norm_eps: float = 1e-6,
31
+ use_rope: bool = True,
32
+ attn: dict | None = None,
33
+ use_cache: bool = True,
34
+ pad_token_id: int | None = None,
35
+ bos_token_id: int = 1,
36
+ eos_token_id: int = 2,
37
+ tie_word_embeddings: bool = False,
38
+ initializer_range: float = 0.02,
39
+ fuse_norm: bool = True,
40
+ fuse_swiglu: bool = True,
41
+ fuse_cross_entropy: bool = True,
42
+ fuse_linear_cross_entropy: bool = False,
43
+ use_l2warp: bool = False,
44
+ vocab_size: int = 32000,
45
+ **kwargs,
46
+ ):
47
+ self.hidden_size = hidden_size
48
+ self.gate_low_rank_dim = gate_low_rank_dim
49
+ self.clamp_min = clamp_min
50
+ self.clamp_max = clamp_max
51
+ self.hidden_ratio = hidden_ratio
52
+ self.intermediate_size = intermediate_size
53
+ self.num_hidden_layers = num_hidden_layers
54
+ self.num_heads = num_heads
55
+ self.num_slots = num_slots
56
+ self.use_short_conv = use_short_conv
57
+ self.conv_size = conv_size
58
+ self.expand_k = exapnd_k
59
+ self.expand_v = exapnd_v
60
+ self.hidden_act = hidden_act
61
+ self.max_position_embeddings = max_position_embeddings
62
+ self.elementwise_affine = elementwise_affine
63
+ self.norm_eps = norm_eps
64
+ self.use_rope = use_rope
65
+ self.attn = attn
66
+ self.use_cache = use_cache
67
+ self.initializer_range = initializer_range
68
+
69
+ self.fuse_norm = fuse_norm
70
+ self.fuse_swiglu = fuse_swiglu
71
+ self.fuse_cross_entropy = fuse_cross_entropy
72
+ self.fuse_linear_cross_entropy = fuse_linear_cross_entropy
73
+ self.use_l2warp = use_l2warp
74
+ self.vocab_size = vocab_size
75
+
76
+ if fuse_cross_entropy and fuse_linear_cross_entropy:
77
+ raise ValueError(
78
+ "`fuse_cross_entropy` and `fuse_linear_cross_entropy` cannot be True at the same time.",
79
+ )
80
+ if fuse_linear_cross_entropy:
81
+ warnings.warn(
82
+ "`fuse_linear_cross_entropy` is enabled, which can improves memory efficiency "
83
+ "at the potential cost of reduced precision. "
84
+ "If you observe issues like loss divergence, consider disabling this setting.",
85
+ )
86
+
87
+ if attn is not None:
88
+ if not isinstance(attn, dict):
89
+ raise ValueError("attn must be a dictionary")
90
+ if 'layers' not in attn:
91
+ raise ValueError("Layer indices must be provided to initialize hybrid attention layers")
92
+ if 'num_heads' not in attn:
93
+ raise ValueError("Number of heads must be provided to initialize hybrid attention layers")
94
+ attn['num_kv_heads'] = attn.get('num_kv_heads', attn['num_heads'])
95
+ attn['qkv_bias'] = attn.get('qkv_bias', False)
96
+ attn['window_size'] = attn.get('window_size', None)
97
+ attn['rope_theta'] = attn.get('rope_theta', 10000.)
98
+
99
+ super().__init__(
100
+ pad_token_id=pad_token_id,
101
+ bos_token_id=bos_token_id,
102
+ eos_token_id=eos_token_id,
103
+ tie_word_embeddings=tie_word_embeddings,
104
+ **kwargs,
105
+ )
fla/models/abc/modeling_abc.py ADDED
@@ -0,0 +1,371 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import math
4
+ import warnings
5
+ from typing import TYPE_CHECKING, Optional
6
+
7
+ import torch
8
+ import torch.nn as nn
9
+ from transformers.modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast
10
+ from transformers.modeling_utils import PreTrainedModel
11
+ from transformers.utils import logging
12
+ from transformers.utils.deprecation import deprecate_kwarg
13
+
14
+ from fla.layers.abc import ABCAttention
15
+ from fla.layers.attn import Attention
16
+ from fla.models.abc.configuration_abc import ABCConfig
17
+ from fla.models.utils import Cache, FLAGenerationMixin
18
+ from fla.modules import FusedCrossEntropyLoss, FusedLinearCrossEntropyLoss, RMSNorm
19
+ from fla.modules import GatedMLP as ABCMLP
20
+ from fla.modules.l2warp import l2_warp
21
+
22
+ try:
23
+ from transformers.modeling_layers import GradientCheckpointingLayer
24
+ except ImportError:
25
+ from fla.models.modeling_layers import GradientCheckpointingLayer
26
+
27
+ logger = logging.get_logger(__name__)
28
+
29
+ if TYPE_CHECKING:
30
+ from transformers.processing_utils import Unpack
31
+
32
+
33
+ class ABCBlock(GradientCheckpointingLayer):
34
+
35
+ def __init__(self, config: ABCConfig, layer_idx: int):
36
+ super().__init__()
37
+
38
+ self.config = config
39
+ self.layer_idx = layer_idx
40
+
41
+ self.attn_norm = (RMSNorm if config.fuse_norm else nn.RMSNorm)(config.hidden_size, eps=config.norm_eps)
42
+ if config.attn is not None and layer_idx in config.attn['layers']:
43
+ self.attn = Attention(
44
+ hidden_size=config.hidden_size,
45
+ num_heads=config.attn['num_heads'],
46
+ num_kv_heads=config.attn['num_kv_heads'],
47
+ qkv_bias=config.attn['qkv_bias'],
48
+ window_size=config.attn['window_size'],
49
+ rope_theta=config.attn['rope_theta'],
50
+ max_position_embeddings=config.max_position_embeddings,
51
+ layer_idx=layer_idx,
52
+ )
53
+ else:
54
+ self.attn = ABCAttention(
55
+ hidden_size=config.hidden_size,
56
+ expand_k=config.expand_k,
57
+ expand_v=config.expand_v,
58
+ num_heads=config.num_heads,
59
+ num_slots=config.num_slots,
60
+ use_short_conv=config.use_short_conv,
61
+ conv_size=config.conv_size,
62
+ gate_fn=config.hidden_act,
63
+ elementwise_affine=config.elementwise_affine,
64
+ norm_eps=config.norm_eps,
65
+ use_rope=config.use_rope,
66
+ clamp_min=config.clamp_min,
67
+ clamp_max=config.clamp_max,
68
+ fuse_norm=config.fuse_norm,
69
+ layer_idx=layer_idx,
70
+ )
71
+ self.mlp_norm = (RMSNorm if config.fuse_norm else nn.RMSNorm)(config.hidden_size, eps=config.norm_eps)
72
+ self.mlp = ABCMLP(
73
+ hidden_size=config.hidden_size,
74
+ hidden_ratio=config.hidden_ratio,
75
+ intermediate_size=config.intermediate_size,
76
+ hidden_act=config.hidden_act,
77
+ fuse_swiglu=config.fuse_swiglu,
78
+ )
79
+
80
+ def forward(
81
+ self,
82
+ hidden_states: torch.Tensor,
83
+ attention_mask: torch.Tensor | None = None,
84
+ past_key_values: Cache | list[torch.FloatTensor] | None = None,
85
+ use_cache: bool | None = False,
86
+ output_attentions: bool | None = False,
87
+ **kwargs: Unpack[dict],
88
+ ) -> tuple[torch.FloatTensor, tuple[torch.FloatTensor, torch.FloatTensor] | None]:
89
+
90
+ residual = hidden_states
91
+
92
+ hidden_states = self.attn_norm(hidden_states)
93
+ hidden_states, attentions, past_key_values = self.attn(
94
+ hidden_states=hidden_states,
95
+ attention_mask=attention_mask,
96
+ past_key_values=past_key_values,
97
+ use_cache=use_cache,
98
+ output_attentions=output_attentions,
99
+ **kwargs,
100
+ )
101
+ if self.config.fuse_norm:
102
+ hidden_states, residual = self.mlp_norm(hidden_states, residual, True)
103
+ else:
104
+ hidden_states = residual + hidden_states
105
+ residual = hidden_states
106
+ hidden_states = self.mlp_norm(hidden_states)
107
+ hidden_states = self.mlp(hidden_states)
108
+ hidden_states = residual + hidden_states
109
+
110
+ outputs = (hidden_states, attentions, past_key_values)
111
+
112
+ return outputs
113
+
114
+
115
+ class ABCPreTrainedModel(PreTrainedModel):
116
+
117
+ config_class = ABCConfig
118
+ base_model_prefix = 'model'
119
+ supports_gradient_checkpointing = True
120
+ _no_split_modules = ['ABCBlock']
121
+ _supports_cache_class = True
122
+
123
+ def __init__(self, *inputs, **kwargs):
124
+ super().__init__(*inputs, **kwargs)
125
+
126
+ def _init_weights(
127
+ self,
128
+ module: nn.Module,
129
+ prenorm_residual_strategy: str | None = None,
130
+ num_residuals_per_layer: int = 2,
131
+ ):
132
+ if isinstance(module, (nn.Linear, nn.Conv1d)):
133
+ # Slightly different from the TF version which uses truncated_normal for initialization
134
+ # cf https://github.com/pytorch/pytorch/pull/5617
135
+ nn.init.normal_(module.weight, mean=0.0, std=self.config.initializer_range)
136
+ if module.bias is not None:
137
+ nn.init.zeros_(module.bias)
138
+ elif isinstance(module, nn.Embedding):
139
+ nn.init.normal_(module.weight, mean=0.0, std=self.config.initializer_range)
140
+ elif hasattr(module, 'reset_parameters'):
141
+ module.reset_parameters()
142
+
143
+ if prenorm_residual_strategy is not None:
144
+ # Reinitialize selected weights subject to the OpenAI GPT-2 Paper Scheme:
145
+ # > A modified initialization which accounts for the accumulation on the residual path with model depth. Scale
146
+ # > the weights of residual layers at initialization by a factor of 1/√N where N is the # of residual layers.
147
+ # > -- GPT-2 :: https://openai.com/blog/better-language-models/
148
+ #
149
+ # Reference (Megatron-LM): https://github.com/NVIDIA/Megatron-LM/blob/main/megatron/model/gpt_model.py
150
+ p = None
151
+ if hasattr(module, 'o_proj'):
152
+ p = module.o_proj.weight
153
+ elif hasattr(module, 'down_proj'):
154
+ p = module.down_proj.weight
155
+ if p is not None:
156
+ # Special Scaled Initialization --> There are 2 Layer Norms per Transformer Block
157
+ # Following Pytorch init, except scale by 1/sqrt(2 * n_layer)
158
+ # We need to reinit p since this code could be called multiple times
159
+ # Having just p *= scale would repeatedly scale it down
160
+ if prenorm_residual_strategy == 'rescale':
161
+ nn.init.kaiming_uniform_(p, a=math.sqrt(5))
162
+ with torch.no_grad():
163
+ p /= math.sqrt(num_residuals_per_layer * self.config.num_hidden_layers)
164
+ elif prenorm_residual_strategy == 'zero':
165
+ nn.init.zeros_(p)
166
+ else:
167
+ raise ValueError(f"Invalid prenorm_residual_strategy: {prenorm_residual_strategy}")
168
+
169
+
170
+ class ABCModel(ABCPreTrainedModel):
171
+
172
+ def __init__(self, config: ABCConfig):
173
+ super().__init__(config)
174
+ self.padding_idx = config.pad_token_id
175
+ self.vocab_size = config.vocab_size
176
+
177
+ self.embeddings = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)
178
+ self.layers = nn.ModuleList([ABCBlock(config, layer_idx) for layer_idx in range(config.num_hidden_layers)])
179
+ self.norm = (RMSNorm if config.fuse_norm else nn.RMSNorm)(config.hidden_size, eps=config.norm_eps)
180
+
181
+ self.gradient_checkpointing = False
182
+
183
+ self.post_init()
184
+
185
+ def get_input_embeddings(self):
186
+ return self.embeddings
187
+
188
+ def set_input_embeddings(self, value):
189
+ self.embeddings = value
190
+
191
+ def forward(
192
+ self,
193
+ input_ids: torch.LongTensor | None = None,
194
+ attention_mask: Optional[torch.Tensor] = None, # noqa
195
+ inputs_embeds: torch.FloatTensor | None = None,
196
+ past_key_values: Cache | list[torch.FloatTensor] | None = None,
197
+ use_cache: bool | None = None,
198
+ output_attentions: bool | None = None,
199
+ output_hidden_states: bool | None = None,
200
+ return_dict: bool | None = None,
201
+ **kwargs: Unpack[dict],
202
+ ) -> tuple | BaseModelOutputWithPast:
203
+ if output_attentions:
204
+ warnings.warn("`ABCModel` does not `output_attentions` now, setting it to `False`.")
205
+ output_attentions = False
206
+ output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
207
+ output_hidden_states = output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
208
+ use_cache = use_cache if use_cache is not None else (self.config.use_cache if not self.training else False)
209
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
210
+
211
+ # retrieve input_ids and inputs_embeds
212
+ if input_ids is not None and inputs_embeds is not None:
213
+ raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time")
214
+ if input_ids is None and inputs_embeds is None:
215
+ raise ValueError("You have to specify either input_ids or inputs_embeds")
216
+
217
+ if inputs_embeds is None:
218
+ inputs_embeds = self.embeddings(input_ids)
219
+ hidden_states = inputs_embeds
220
+
221
+ if use_cache and not isinstance(past_key_values, Cache):
222
+ past_key_values = Cache.from_legacy_cache(past_key_values)
223
+
224
+ all_hidden_states = () if output_hidden_states else None
225
+ all_attns = () if output_attentions else None
226
+ for layer in self.layers:
227
+ if output_hidden_states:
228
+ all_hidden_states += (hidden_states,)
229
+
230
+ hidden_states, attentions, past_key_values = layer(
231
+ hidden_states,
232
+ attention_mask,
233
+ past_key_values=past_key_values,
234
+ use_cache=use_cache,
235
+ output_attentions=output_attentions,
236
+ **kwargs,
237
+ )
238
+
239
+ if output_attentions:
240
+ all_attns += (attentions,)
241
+
242
+ hidden_states = self.norm(hidden_states)
243
+
244
+ # add hidden states from the last decoder layer
245
+ if output_hidden_states:
246
+ all_hidden_states += (hidden_states,)
247
+
248
+ if not return_dict:
249
+ return tuple(i for i in [hidden_states, past_key_values, all_hidden_states, all_attns] if i is not None)
250
+ return BaseModelOutputWithPast(
251
+ last_hidden_state=hidden_states,
252
+ past_key_values=past_key_values,
253
+ hidden_states=all_hidden_states,
254
+ attentions=all_attns,
255
+ )
256
+
257
+
258
+ class ABCForCausalLM(ABCPreTrainedModel, FLAGenerationMixin):
259
+
260
+ _tied_weights_keys = ["lm_head.weight"]
261
+
262
+ def __init__(self, config):
263
+ super().__init__(config)
264
+ self.model = ABCModel(config)
265
+ self.vocab_size = config.vocab_size
266
+ self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
267
+ self.criterion = None
268
+
269
+ # Initialize weights and apply final processing
270
+ self.post_init()
271
+
272
+ def get_input_embeddings(self):
273
+ return self.model.embeddings
274
+
275
+ def set_input_embeddings(self, value):
276
+ self.model.embeddings = value
277
+
278
+ def get_output_embeddings(self):
279
+ return self.lm_head
280
+
281
+ def set_output_embeddings(self, new_embeddings):
282
+ self.lm_head = new_embeddings
283
+
284
+ def set_decoder(self, decoder):
285
+ self.model = decoder
286
+
287
+ def get_decoder(self):
288
+ return self.model
289
+
290
+ def generate(self, *args, **kwargs):
291
+ try:
292
+ return super().generate(*args, **kwargs)
293
+ except AttributeError as exception:
294
+ if 'past_key_values' in str(exception):
295
+ raise AttributeError(
296
+ f"You tried to call `generate` with a decoding strategy that manipulates `past_key_values`, "
297
+ f"which is not supported for {self.__class__.__name__}. "
298
+ f"Try another generation strategy instead. "
299
+ f"For the available generation strategies, check this doc: "
300
+ f"https://huggingface.co/docs/transformers/en/generation_strategies#decoding-strategies",
301
+ )
302
+ else:
303
+ raise exception
304
+
305
+ @deprecate_kwarg("num_logits_to_keep", version="4.50", new_name="logits_to_keep")
306
+ def forward(
307
+ self,
308
+ input_ids: torch.LongTensor = None,
309
+ attention_mask: torch.Tensor | None = None,
310
+ inputs_embeds: torch.Tensor | None = None,
311
+ past_key_values: Cache | list[torch.FloatTensor] | None = None,
312
+ labels: torch.LongTensor | None = None,
313
+ use_cache: bool | None = None,
314
+ output_attentions: bool | None = None,
315
+ output_hidden_states: bool | None = None,
316
+ return_dict: bool | None = None,
317
+ logits_to_keep: int | None = 0,
318
+ **kwargs: Unpack[dict],
319
+ ) -> tuple | CausalLMOutputWithPast:
320
+ output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
321
+ output_hidden_states = (
322
+ output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
323
+ )
324
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
325
+
326
+ outputs = self.model(
327
+ input_ids=input_ids,
328
+ attention_mask=attention_mask,
329
+ inputs_embeds=inputs_embeds,
330
+ past_key_values=past_key_values,
331
+ use_cache=use_cache,
332
+ output_attentions=output_attentions,
333
+ output_hidden_states=output_hidden_states,
334
+ return_dict=return_dict,
335
+ **kwargs,
336
+ )
337
+
338
+ hidden_states = outputs[0]
339
+
340
+ loss, logits = None, None
341
+ if not self.config.fuse_linear_cross_entropy or labels is None:
342
+ logits = self.lm_head(hidden_states if logits_to_keep is None else hidden_states[:, -logits_to_keep:])
343
+ if labels is not None:
344
+ if getattr(self, 'criterion', None) is None:
345
+ if self.config.fuse_linear_cross_entropy:
346
+ criterion = FusedLinearCrossEntropyLoss(use_l2warp=self.config.use_l2warp)
347
+ elif self.config.fuse_cross_entropy:
348
+ criterion = FusedCrossEntropyLoss(inplace_backward=True)
349
+ else:
350
+ criterion = nn.CrossEntropyLoss()
351
+ else:
352
+ criterion = self.criterion
353
+ labels = labels.to(hidden_states.device)
354
+ labels = torch.cat((labels[..., 1:], torch.full_like(labels[:, :1], criterion.ignore_index)), 1)
355
+ if self.config.fuse_linear_cross_entropy:
356
+ loss = criterion(hidden_states, labels, self.lm_head.weight, self.lm_head.bias)
357
+ else:
358
+ loss = criterion(logits.view(labels.numel(), -1), labels.view(-1))
359
+ loss = l2_warp(loss, logits) if self.config.use_l2warp else loss
360
+
361
+ if not return_dict:
362
+ output = (logits,) + outputs[1:]
363
+ return (loss,) + output if loss is not None else output
364
+
365
+ return CausalLMOutputWithPast(
366
+ loss=loss,
367
+ logits=logits,
368
+ past_key_values=outputs.past_key_values,
369
+ hidden_states=outputs.hidden_states,
370
+ attentions=outputs.attentions,
371
+ )
fla/models/bitnet/__init__.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ from transformers import AutoConfig, AutoModel, AutoModelForCausalLM
3
+
4
+ from fla.models.bitnet.configuration_bitnet import BitNetConfig
5
+ from fla.models.bitnet.modeling_bitnet import BitNetForCausalLM, BitNetModel
6
+
7
+ AutoConfig.register(BitNetConfig.model_type, BitNetConfig, exist_ok=True)
8
+ AutoModel.register(BitNetConfig, BitNetModel, exist_ok=True)
9
+ AutoModelForCausalLM.register(BitNetConfig, BitNetForCausalLM, exist_ok=True)
10
+
11
+
12
+ __all__ = ['BitNetConfig', 'BitNetForCausalLM', 'BitNetModel']
fla/models/bitnet/configuration_bitnet.py ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import warnings
3
+
4
+ from transformers.configuration_utils import PretrainedConfig
5
+
6
+
7
+ class BitNetConfig(PretrainedConfig):
8
+
9
+ model_type = 'bitnet'
10
+ keys_to_ignore_at_inference = ['past_key_values']
11
+
12
+ def __init__(
13
+ self,
14
+ hidden_size: int = 2048,
15
+ num_hidden_layers: int = 24,
16
+ num_heads: int = 32,
17
+ num_kv_heads: int | None = None,
18
+ window_size: int | None = None,
19
+ rope_theta: float | None = 10000.,
20
+ max_position_embeddings: int = 2048,
21
+ hidden_ratio: int | None = 4,
22
+ intermediate_size: int | None = None,
23
+ hidden_act: str = "swish",
24
+ initializer_range: float = 0.02,
25
+ elementwise_affine: bool | None = True,
26
+ norm_eps: float = 1e-6,
27
+ use_cache: bool = True,
28
+ pad_token_id: int | None = None,
29
+ bos_token_id: int = 1,
30
+ eos_token_id: int = 2,
31
+ tie_word_embeddings: bool = False,
32
+ fuse_norm: bool = True,
33
+ fuse_swiglu: bool = True,
34
+ fuse_cross_entropy: bool = True,
35
+ fuse_linear_cross_entropy: bool = False,
36
+ use_l2warp: bool = False,
37
+ vocab_size: int = 32000,
38
+ **kwargs,
39
+ ):
40
+ self.hidden_size = hidden_size
41
+ self.num_hidden_layers = num_hidden_layers
42
+ self.num_heads = num_heads
43
+ self.num_kv_heads = num_kv_heads
44
+ self.window_size = window_size
45
+ self.rope_theta = rope_theta
46
+ self.max_position_embeddings = max_position_embeddings
47
+
48
+ self.hidden_ratio = hidden_ratio
49
+ self.intermediate_size = intermediate_size
50
+ self.hidden_act = hidden_act
51
+
52
+ self.initializer_range = initializer_range
53
+ self.elementwise_affine = elementwise_affine
54
+ self.norm_eps = norm_eps
55
+ self.use_cache = use_cache
56
+
57
+ self.fuse_norm = fuse_norm
58
+ self.fuse_swiglu = fuse_swiglu
59
+ self.fuse_cross_entropy = fuse_cross_entropy
60
+ self.fuse_linear_cross_entropy = fuse_linear_cross_entropy
61
+ self.use_l2warp = use_l2warp
62
+ self.vocab_size = vocab_size
63
+
64
+ if fuse_cross_entropy and fuse_linear_cross_entropy:
65
+ raise ValueError(
66
+ "`fuse_cross_entropy` and `fuse_linear_cross_entropy` cannot be True at the same time.",
67
+ )
68
+ if fuse_linear_cross_entropy:
69
+ warnings.warn(
70
+ "`fuse_linear_cross_entropy` is enabled, which can improves memory efficiency "
71
+ "at the potential cost of reduced precision. "
72
+ "If you observe issues like loss divergence, consider disabling this setting.",
73
+ )
74
+
75
+ super().__init__(
76
+ pad_token_id=pad_token_id,
77
+ bos_token_id=bos_token_id,
78
+ eos_token_id=eos_token_id,
79
+ tie_word_embeddings=tie_word_embeddings,
80
+ **kwargs,
81
+ )
fla/models/bitnet/modeling_bitnet.py ADDED
@@ -0,0 +1,395 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import math
4
+ import warnings
5
+ from typing import TYPE_CHECKING, Any
6
+
7
+ import torch
8
+ import torch.nn as nn
9
+ from transformers.modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast
10
+ from transformers.modeling_utils import PreTrainedModel
11
+ from transformers.utils import logging
12
+ from transformers.utils.deprecation import deprecate_kwarg
13
+
14
+ from fla.layers.bitattn import BitAttention
15
+ from fla.models.bitnet.configuration_bitnet import BitNetConfig
16
+ from fla.models.utils import Cache, FLAGenerationMixin
17
+ from fla.modules import FusedCrossEntropyLoss, FusedLinearCrossEntropyLoss, RMSNorm
18
+ from fla.modules.activations import swiglu
19
+ from fla.modules.fused_bitlinear import FusedBitLinear
20
+ from fla.modules.l2warp import l2_warp
21
+
22
+ if TYPE_CHECKING:
23
+ from transformers.processing_utils import Unpack
24
+
25
+
26
+ try:
27
+ from transformers.modeling_layers import GradientCheckpointingLayer
28
+ except ImportError:
29
+ from fla.models.modeling_layers import GradientCheckpointingLayer
30
+
31
+ logger = logging.get_logger(__name__)
32
+
33
+
34
+ class BitNetMLP(nn.Module):
35
+
36
+ def __init__(
37
+ self,
38
+ hidden_size: int,
39
+ hidden_ratio: int | None = None,
40
+ intermediate_size: int | None = None,
41
+ hidden_act: str = 'swish',
42
+ fuse_swiglu: bool = True,
43
+ ) -> BitNetMLP:
44
+ super().__init__()
45
+
46
+ self.hidden_size = hidden_size
47
+ # the final number of params is `hidden_ratio * hidden_size^2`
48
+ # `intermediate_size` is chosen to be a multiple of 256 closest to `2/3 * hidden_size * hidden_ratio`
49
+ if hidden_ratio is None:
50
+ hidden_ratio = 4
51
+ if intermediate_size is None:
52
+ intermediate_size = int(hidden_size * hidden_ratio * 2 / 3)
53
+ intermediate_size = 256 * ((intermediate_size + 256 - 1) // 256)
54
+ self.hidden_ratio = hidden_ratio
55
+ self.intermediate_size = intermediate_size
56
+ self.hidden_act = hidden_act
57
+ self.fuse_swiglu = fuse_swiglu
58
+
59
+ if hidden_act != 'swish':
60
+ raise ValueError(f'Unsupported hidden_act: {hidden_act}')
61
+
62
+ self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
63
+ self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
64
+ self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False)
65
+
66
+ def forward(
67
+ self,
68
+ x: torch.Tensor,
69
+ **kwargs: Unpack[Any],
70
+ ) -> torch.Tensor:
71
+ gate, y = self.gate_proj(x), self.up_proj(x)
72
+ return self.down_proj(swiglu(gate, y))
73
+
74
+
75
+ class BitNetBlock(GradientCheckpointingLayer):
76
+
77
+ def __init__(self, config: BitNetConfig, layer_idx: int):
78
+ super().__init__()
79
+
80
+ self.config = config
81
+ self.layer_idx = layer_idx
82
+
83
+ self.attn_norm = (RMSNorm if config.fuse_norm else nn.RMSNorm)(config.hidden_size, eps=config.norm_eps)
84
+ self.attn = BitAttention(
85
+ hidden_size=config.hidden_size,
86
+ num_heads=config.num_heads,
87
+ num_kv_heads=config.num_kv_heads,
88
+ window_size=config.window_size,
89
+ rope_theta=config.rope_theta,
90
+ max_position_embeddings=config.max_position_embeddings,
91
+ layer_idx=layer_idx,
92
+ )
93
+
94
+ self.mlp_norm = (RMSNorm if config.fuse_norm else nn.RMSNorm)(config.hidden_size, eps=config.norm_eps)
95
+ self.mlp = BitNetMLP(
96
+ hidden_size=config.hidden_size,
97
+ hidden_ratio=config.hidden_ratio,
98
+ intermediate_size=config.intermediate_size,
99
+ hidden_act=config.hidden_act,
100
+ fuse_swiglu=config.fuse_swiglu,
101
+ )
102
+
103
+ def forward(
104
+ self,
105
+ hidden_states: torch.Tensor,
106
+ attention_mask: torch.Tensor | None = None,
107
+ past_key_values: tuple[torch.Tensor] | None = None,
108
+ output_attentions: bool | None = False,
109
+ use_cache: bool | None = False,
110
+ **kwargs: Unpack[Any],
111
+ ) -> tuple[torch.FloatTensor, tuple[torch.FloatTensor, torch.FloatTensor] | None]:
112
+
113
+ residual = hidden_states
114
+ hidden_states = self.attn_norm(hidden_states)
115
+ hidden_states, attentions, past_key_values = self.attn(
116
+ hidden_states=hidden_states,
117
+ attention_mask=attention_mask,
118
+ past_key_values=past_key_values,
119
+ use_cache=use_cache,
120
+ output_attentions=output_attentions,
121
+ **kwargs,
122
+ )
123
+ if self.config.fuse_norm:
124
+ hidden_states, residual = self.mlp_norm(hidden_states, residual, True)
125
+ else:
126
+ hidden_states = residual + hidden_states
127
+ residual = hidden_states
128
+ hidden_states = self.mlp_norm(hidden_states)
129
+ hidden_states = self.mlp(hidden_states, **kwargs)
130
+ hidden_states = residual + hidden_states
131
+
132
+ outputs = (hidden_states,)
133
+
134
+ if output_attentions:
135
+ outputs += (attentions,)
136
+
137
+ if use_cache:
138
+ outputs += (past_key_values,)
139
+
140
+ return outputs
141
+
142
+
143
+ class BitNetPreTrainedModel(PreTrainedModel):
144
+
145
+ config_class = BitNetConfig
146
+ base_model_prefix = 'model'
147
+ supports_gradient_checkpointing = True
148
+ _no_split_modules = ['BitNetBlock']
149
+ _supports_cache_class = True
150
+
151
+ def __init__(self, *inputs, **kwargs):
152
+ super().__init__(*inputs, **kwargs)
153
+
154
+ def _init_weights(
155
+ self,
156
+ module: nn.Module,
157
+ rescale_prenorm_residual: bool = False,
158
+ num_residuals_per_layer: int = 2,
159
+ ):
160
+ if isinstance(module, (nn.Linear, FusedBitLinear, nn.Conv1d)):
161
+ # Slightly different from the TF version which uses truncated_normal for initialization
162
+ # cf https://github.com/pytorch/pytorch/pull/5617
163
+ nn.init.normal_(module.weight, mean=0.0, std=self.config.initializer_range)
164
+ if module.bias is not None:
165
+ nn.init.zeros_(module.bias)
166
+ elif isinstance(module, nn.Embedding):
167
+ nn.init.normal_(module.weight, mean=0.0, std=self.config.initializer_range)
168
+ elif hasattr(module, 'reset_parameters'):
169
+ module.reset_parameters()
170
+
171
+ if rescale_prenorm_residual:
172
+ # Reinitialize selected weights subject to the OpenAI GPT-2 Paper Scheme:
173
+ # > A modified initialization which accounts for the accumulation on the residual path with model depth. Scale
174
+ # > the weights of residual layers at initialization by a factor of 1/√N where N is the # of residual layers.
175
+ # > -- GPT-2 :: https://openai.com/blog/better-language-models/
176
+ #
177
+ # Reference (Megatron-LM): https://github.com/NVIDIA/Megatron-LM/blob/main/megatron/model/gpt_model.py
178
+ p = None
179
+ if hasattr(module, 'o_proj'):
180
+ p = module.o_proj.weight
181
+ elif hasattr(module, 'down_proj'):
182
+ p = module.down_proj.weight
183
+ if p is not None:
184
+ # Special Scaled Initialization --> There are 2 Layer Norms per Transformer Block
185
+ # Following Pytorch init, except scale by 1/sqrt(2 * n_layer)
186
+ # We need to reinit p since this code could be called multiple times
187
+ # Having just p *= scale would repeatedly scale it down
188
+ nn.init.kaiming_uniform_(p, a=math.sqrt(5))
189
+ with torch.no_grad():
190
+ p /= math.sqrt(num_residuals_per_layer * self.config.num_hidden_layers)
191
+
192
+
193
+ class BitNetModel(BitNetPreTrainedModel):
194
+
195
+ def __init__(
196
+ self,
197
+ config: BitNetConfig,
198
+ ) -> BitNetModel:
199
+ super().__init__(config)
200
+ self.padding_idx = config.pad_token_id
201
+ self.vocab_size = config.vocab_size
202
+
203
+ self.embeddings = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)
204
+ self.layers = nn.ModuleList([BitNetBlock(config, layer_idx) for layer_idx in range(config.num_hidden_layers)])
205
+ self.norm = (RMSNorm if config.fuse_norm else nn.RMSNorm)(config.hidden_size, eps=config.norm_eps)
206
+
207
+ self.gradient_checkpointing = False
208
+
209
+ self.post_init()
210
+
211
+ def get_input_embeddings(self):
212
+ return self.embeddings
213
+
214
+ def set_input_embeddings(self, value):
215
+ self.embeddings = value
216
+
217
+ def forward(
218
+ self,
219
+ input_ids: torch.LongTensor | None = None,
220
+ attention_mask: torch.Tensor | None = None,
221
+ past_key_values: list[torch.FloatTensor] | None = None,
222
+ inputs_embeds: torch.FloatTensor | None = None,
223
+ use_cache: bool | None = None,
224
+ output_attentions: bool | None = None,
225
+ output_hidden_states: bool | None = None,
226
+ return_dict: bool | None = None,
227
+ **kwargs: Unpack[Any],
228
+ ) -> tuple | CausalLMOutputWithPast:
229
+ if output_attentions:
230
+ warnings.warn(
231
+ "`BitNetModel` does not support output attention weights now, so `output_attentions` is set to `False`.",
232
+ )
233
+ output_attentions = False
234
+ output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
235
+ output_hidden_states = output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
236
+ use_cache = use_cache if use_cache is not None else (self.config.use_cache if not self.training else False)
237
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
238
+
239
+ # retrieve input_ids and inputs_embeds
240
+ if input_ids is not None and inputs_embeds is not None:
241
+ raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time")
242
+ elif input_ids is None and inputs_embeds is None:
243
+ raise ValueError("You have to specify either input_ids or inputs_embeds")
244
+
245
+ if use_cache and not isinstance(past_key_values, Cache):
246
+ past_key_values = Cache.from_legacy_cache(past_key_values)
247
+
248
+ if inputs_embeds is None:
249
+ inputs_embeds = self.embeddings(input_ids)
250
+
251
+ # embed positions
252
+ hidden_states = inputs_embeds
253
+
254
+ all_hidden_states = () if output_hidden_states else None
255
+ all_attns = () if output_attentions else None
256
+ next_cache = None
257
+
258
+ for layer in self.layers:
259
+ if output_hidden_states:
260
+ all_hidden_states += (hidden_states,)
261
+
262
+ layer_outputs = layer(
263
+ hidden_states,
264
+ attention_mask=attention_mask,
265
+ past_key_values=past_key_values,
266
+ output_attentions=output_attentions,
267
+ use_cache=use_cache,
268
+ **kwargs,
269
+ )
270
+
271
+ hidden_states = layer_outputs[0]
272
+
273
+ if use_cache:
274
+ next_cache = layer_outputs[2 if output_attentions else 1]
275
+
276
+ if output_attentions:
277
+ all_attns += (layer_outputs[1],)
278
+
279
+ hidden_states = self.norm(hidden_states)
280
+
281
+ # add hidden states from the last decoder layer
282
+ if output_hidden_states:
283
+ all_hidden_states += (hidden_states,)
284
+
285
+ if not return_dict:
286
+ return tuple(v for v in [hidden_states, next_cache, all_hidden_states, all_attns] if v is not None)
287
+
288
+ return BaseModelOutputWithPast(
289
+ last_hidden_state=hidden_states,
290
+ past_key_values=next_cache,
291
+ hidden_states=all_hidden_states,
292
+ attentions=all_attns,
293
+ )
294
+
295
+
296
+ class BitNetForCausalLM(BitNetPreTrainedModel, FLAGenerationMixin):
297
+
298
+ _tied_weights_keys = ["lm_head.weight"]
299
+
300
+ def __init__(self, config):
301
+ super().__init__(config)
302
+ self.model = BitNetModel(config)
303
+ self.vocab_size = config.vocab_size
304
+ self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
305
+ self.criterion = None
306
+
307
+ # Initialize weights and apply final processing
308
+ self.post_init()
309
+
310
+ def get_input_embeddings(self):
311
+ return self.model.embeddings
312
+
313
+ def set_input_embeddings(self, value):
314
+ self.model.embeddings = value
315
+
316
+ def get_output_embeddings(self):
317
+ return self.lm_head
318
+
319
+ def set_output_embeddings(self, new_embeddings):
320
+ self.lm_head = new_embeddings
321
+
322
+ def set_decoder(self, decoder):
323
+ self.model = decoder
324
+
325
+ def get_decoder(self):
326
+ return self.model
327
+
328
+ @deprecate_kwarg("num_logits_to_keep", version="4.50", new_name="logits_to_keep")
329
+ def forward(
330
+ self,
331
+ input_ids: torch.LongTensor = None,
332
+ attention_mask: torch.Tensor | None = None,
333
+ past_key_values: Cache | list[torch.FloatTensor] | None = None,
334
+ inputs_embeds: torch.FloatTensor | None = None,
335
+ labels: torch.LongTensor | None = None,
336
+ use_cache: bool | None = None,
337
+ output_attentions: bool | None = None,
338
+ output_hidden_states: bool | None = None,
339
+ return_dict: bool | None = None,
340
+ logits_to_keep: int | None = 0,
341
+ **kwargs: Unpack[Any],
342
+ ) -> tuple | CausalLMOutputWithPast:
343
+ output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
344
+ output_hidden_states = (
345
+ output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
346
+ )
347
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
348
+
349
+ outputs = self.model(
350
+ input_ids=input_ids,
351
+ attention_mask=attention_mask,
352
+ past_key_values=past_key_values,
353
+ inputs_embeds=inputs_embeds,
354
+ use_cache=use_cache,
355
+ output_attentions=output_attentions,
356
+ output_hidden_states=output_hidden_states,
357
+ return_dict=return_dict,
358
+ **kwargs,
359
+ )
360
+
361
+ hidden_states = outputs[0]
362
+
363
+ logits = None if self.config.fuse_linear_cross_entropy else self.lm_head(hidden_states[:, -logits_to_keep:])
364
+
365
+ loss = None
366
+ if labels is not None:
367
+ if getattr(self, 'criterion', None) is None:
368
+ if self.config.fuse_linear_cross_entropy:
369
+ criterion = FusedLinearCrossEntropyLoss(use_l2warp=self.config.use_l2warp)
370
+ elif self.config.fuse_cross_entropy:
371
+ criterion = FusedCrossEntropyLoss(inplace_backward=True)
372
+ else:
373
+ criterion = nn.CrossEntropyLoss()
374
+ else:
375
+ criterion = self.criterion
376
+
377
+ labels = labels.to(hidden_states.device)
378
+ labels = torch.cat((labels[..., 1:], torch.full_like(labels[:, :1], criterion.ignore_index)), 1)
379
+ if self.config.fuse_linear_cross_entropy:
380
+ loss = criterion(hidden_states, labels, self.lm_head.weight, self.lm_head.bias)
381
+ else:
382
+ loss = criterion(logits.view(labels.numel(), -1), labels.view(-1))
383
+ loss = l2_warp(loss, logits) if self.config.use_l2warp else loss
384
+
385
+ if not return_dict:
386
+ output = (logits,) + outputs[1:]
387
+ return (loss,) + output if loss is not None else output
388
+
389
+ return CausalLMOutputWithPast(
390
+ loss=loss,
391
+ logits=logits,
392
+ past_key_values=outputs.past_key_values,
393
+ hidden_states=outputs.hidden_states,
394
+ attentions=outputs.attentions,
395
+ )
fla/models/comba/__init__.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ from transformers import AutoConfig, AutoModel, AutoModelForCausalLM
3
+
4
+ from fla.models.comba.configuration_comba import CombaConfig
5
+ from fla.models.comba.modeling_comba import CombaForCausalLM, CombaModel
6
+
7
+ AutoConfig.register(CombaConfig.model_type, CombaConfig, exist_ok=True)
8
+ AutoModel.register(CombaConfig, CombaModel, exist_ok=True)
9
+ AutoModelForCausalLM.register(CombaConfig, CombaForCausalLM, exist_ok=True)
10
+
11
+ __all__ = ['CombaConfig', 'CombaForCausalLM', 'CombaModel']